Enable GPS from Azimuth according to configuration
[azimuth] / src / position-publisher.c
1 /*
2  * position-publisher.c - Source for PositionPublisher
3  * Copyright (C) 2010 Guillaume Desmottes
4  * @author Guillaume Desmottes <gdesmott@gnome.org>
5  *
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.
10  *
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.
15  *
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
19  */
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/dbus.h>
26 #include <telepathy-glib/interfaces.h>
27
28 #include <location/location-gps-device.h>
29 #include <location/location-gpsd-control.h>
30
31 #include "connection-watcher.h"
32 #include "position-publisher.h"
33
34 G_DEFINE_TYPE(PositionPublisher, position_publisher, G_TYPE_OBJECT)
35
36 /* properties */
37 enum
38 {
39   PROP_START_GPS = 1,
40   PROP_BLUR,
41   LAST_PROPERTY
42 };
43
44 /* Minimum time before 2 publishing (in seconds) */
45 #define PUBLISH_THROTTLE 10
46
47 /* private structure */
48 typedef struct _PositionPublisherPrivate PositionPublisherPrivate;
49
50 struct _PositionPublisherPrivate
51 {
52   ConnectionWatcher *watcher;
53   LocationGPSDControl *gps_control;
54   LocationGPSDevice *gps_device;
55   GSList *connections;
56   GHashTable *location;
57   /* If not 0, we are waiting before publishing again */
58   guint throttle_timeout;
59   /* TRUE if location has been modified while we were waiting */
60   gboolean modified;
61   gboolean blur;
62   gboolean start_gps;
63
64   gboolean dispose_has_run;
65 };
66
67 #define POSITION_PUBLISHER_GET_PRIVATE(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), POSITION_PUBLISHER_TYPE, PositionPublisherPrivate))
68
69 static void conn_invalidated_cb (TpProxy *conn,
70     guint domain,
71     gint code,
72     gchar *message,
73     PositionPublisher *self);
74
75 static void publish_to_all (PositionPublisher *self);
76
77 static void
78 remove_connection (PositionPublisher *self,
79     TpConnection *conn)
80 {
81   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
82
83   g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (conn_invalidated_cb),
84       self);
85   priv->connections = g_slist_remove (priv->connections, conn);
86   g_object_unref (conn);
87 }
88
89 static void
90 set_location_cb (TpConnection *conn,
91     const GError *error,
92     gpointer user_data,
93     GObject *weak_object)
94 {
95   PositionPublisher *self = POSITION_PUBLISHER (weak_object);
96
97   if (error != NULL)
98     {
99       g_print ("SetLocation failed (%s): %s\n", tp_proxy_get_object_path (conn),
100           error->message);
101
102       if (error->code == TP_ERROR_NOT_IMPLEMENTED)
103         {
104           g_print ("remove connection\n");
105           remove_connection (self, conn);
106         }
107
108       return;
109     }
110
111   g_print ("SetLocation succeed (%s)\n", tp_proxy_get_object_path (conn));
112 }
113
114 static gboolean
115 publish_throttle_timeout_cb (gpointer data)
116 {
117   PositionPublisher *self = data;
118   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
119
120   priv->throttle_timeout = 0;
121
122   if (priv->modified)
123     {
124       publish_to_all (self);
125       priv->modified = FALSE;
126     }
127
128   return FALSE;
129 }
130
131 static void
132 publish_to_conn (PositionPublisher *self,
133     TpConnection *conn)
134 {
135   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
136
137   if (priv->location == NULL)
138     return;
139
140   if (priv->throttle_timeout != 0)
141     /* We are waiting */
142     return;
143
144   tp_cli_connection_interface_location_call_set_location (conn, -1,
145       priv->location, set_location_cb, NULL, NULL, G_OBJECT (self));
146
147   /* We won't publish during the next PUBLISH_THROTTLE seconds */
148   priv->throttle_timeout = g_timeout_add_seconds (PUBLISH_THROTTLE,
149       publish_throttle_timeout_cb, self);
150 }
151
152 static void
153 publish_to_all (PositionPublisher *self)
154 {
155   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
156   GSList *l;
157
158   for (l = priv->connections; l != NULL; l = g_slist_next (l))
159     {
160       TpConnection *conn = l->data;
161
162       publish_to_conn (self, conn);
163     }
164 }
165
166 static void
167 update_position (PositionPublisher *self,
168     gdouble lat,
169     gdouble lon,
170     gdouble alt,
171     gdouble accuracy)
172 {
173   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
174
175   g_print ("update position: lat: %f  lon:  %f  alt: %f accuracy: %f\n",
176       lat, lon, alt, accuracy);
177
178   if (priv->location != NULL)
179     g_hash_table_unref (priv->location);
180
181   priv->location = tp_asv_new (
182       "timestamp", G_TYPE_INT64, (gint64) time (NULL),
183       "lat", G_TYPE_DOUBLE, lat,
184       "lon", G_TYPE_DOUBLE, lon,
185       "alt", G_TYPE_DOUBLE, alt,
186       "accuracy", G_TYPE_DOUBLE, accuracy,
187       NULL);
188
189   priv->modified = TRUE;
190
191   publish_to_all (self);
192 }
193
194 static void
195 location_changed_cb (LocationGPSDevice *device,
196     PositionPublisher *self)
197 {
198   if (device == NULL)
199     return;
200
201   if (device->fix == NULL)
202     return;
203
204   if (!(device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET))
205     return;
206
207   update_position (self, device->fix->latitude, device->fix->longitude,
208       device->fix->altitude, device->fix->eph / 100.0);
209 }
210
211 static void
212 conn_invalidated_cb (TpProxy *conn,
213     guint domain,
214     gint code,
215     gchar *message,
216     PositionPublisher *self)
217 {
218   g_print ("connection %s invalidated; removing\n", tp_proxy_get_object_path (
219         conn));
220
221   remove_connection (self, TP_CONNECTION (conn));
222 }
223
224 static void
225 connection_added_cb (ConnectionWatcher *watcher,
226     TpConnection *conn,
227     PositionPublisher *self)
228 {
229   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
230
231   if (g_slist_find (priv->connections, conn) != NULL)
232     return;
233
234   if (!tp_proxy_has_interface_by_id (conn,
235         TP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION))
236     return;
237
238   priv->connections = g_slist_prepend (priv->connections, g_object_ref (conn));
239
240   g_signal_connect (conn, "invalidated",
241       G_CALLBACK (conn_invalidated_cb), self);
242
243   publish_to_conn (self, conn);
244 }
245
246 static void
247 position_publisher_init (PositionPublisher *obj)
248 {
249   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (obj);
250
251   priv->watcher = connection_watcher_new ();
252
253   g_signal_connect (priv->watcher, "connection-added",
254       G_CALLBACK (connection_added_cb), obj);
255
256   priv->gps_control = location_gpsd_control_get_default();
257
258   g_object_set (G_OBJECT(priv->gps_control),
259     "preferred-method", LOCATION_METHOD_USER_SELECTED,
260     "preferred-interval", LOCATION_INTERVAL_120S,
261     NULL);
262
263   priv->gps_device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
264
265   g_signal_connect (priv->gps_device, "changed",
266       G_CALLBACK (location_changed_cb), obj);
267
268   priv->connections = NULL;
269 }
270
271 static void
272 position_publisher_constructed (GObject *object)
273 {
274   PositionPublisher *self = POSITION_PUBLISHER (object);
275   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
276
277   connection_watcher_start (priv->watcher);
278
279   if (G_OBJECT_CLASS (position_publisher_parent_class)->constructed)
280     G_OBJECT_CLASS (position_publisher_parent_class)->constructed (object);
281 }
282
283 static void position_publisher_dispose (GObject *object);
284 static void position_publisher_finalize (GObject *object);
285 static void position_publisher_get_property (GObject *object,
286     guint property_id, GValue *value, GParamSpec *pspec);
287 static void position_publisher_set_property (GObject *object,
288     guint property_id, const GValue *value, GParamSpec *pspec);
289
290 static void
291 position_publisher_class_init (PositionPublisherClass *position_publisher_class)
292 {
293   GObjectClass *object_class = G_OBJECT_CLASS (position_publisher_class);
294   GParamSpec *param_spec;
295
296   g_type_class_add_private (position_publisher_class, sizeof (PositionPublisherPrivate));
297
298   object_class->dispose = position_publisher_dispose;
299   object_class->finalize = position_publisher_finalize;
300   object_class->get_property = position_publisher_get_property;
301   object_class->set_property = position_publisher_set_property;
302
303   object_class->constructed = position_publisher_constructed;
304
305   param_spec = g_param_spec_boolean ("blur", "Blur?",
306       "Whether the real GPS position is truncated for privacy concerns.", TRUE,
307       G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE
308       | G_PARAM_STATIC_STRINGS);
309   g_object_class_install_property (object_class, PROP_BLUR, param_spec);
310
311   param_spec = g_param_spec_boolean ("start-gps", "Start GPS?",
312       "Whether the publisher starts the GPS.", FALSE,
313       G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE
314       | G_PARAM_STATIC_STRINGS);
315   g_object_class_install_property (object_class, PROP_START_GPS, param_spec);
316 }
317
318 void
319 position_publisher_dispose (GObject *object)
320 {
321   PositionPublisher *self = POSITION_PUBLISHER (object);
322   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
323   GSList *l;
324
325   if (priv->dispose_has_run)
326     return;
327
328   priv->dispose_has_run = TRUE;
329
330   if (priv->start_gps)
331     {
332       location_gpsd_control_stop (priv->gps_control);
333       priv->start_gps = FALSE;
334     }
335
336   g_object_unref (priv->watcher);
337   g_object_unref (priv->gps_control);
338   g_object_unref (priv->gps_device);
339
340   for (l = priv->connections; l != NULL; l = g_slist_next (l))
341     {
342       g_object_unref (l->data);
343     }
344
345   g_hash_table_unref (priv->location);
346
347   if (priv->throttle_timeout != 0)
348     g_source_remove (priv->throttle_timeout);
349
350   if (G_OBJECT_CLASS (position_publisher_parent_class)->dispose)
351     G_OBJECT_CLASS (position_publisher_parent_class)->dispose (object);
352 }
353
354 void
355 position_publisher_finalize (GObject *object)
356 {
357   PositionPublisher *self = POSITION_PUBLISHER (object);
358   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
359
360   g_slist_free (priv->connections);
361
362   G_OBJECT_CLASS (position_publisher_parent_class)->finalize (object);
363 }
364
365 static void
366 position_publisher_get_property (GObject *object,
367                                  guint property_id,
368                                  GValue *value,
369                                  GParamSpec *pspec)
370 {
371   PositionPublisher *self = POSITION_PUBLISHER (object);
372   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
373
374   switch (property_id) {
375     case PROP_BLUR:
376       g_value_set_boolean (value, priv->blur);
377       break;
378     case PROP_START_GPS:
379       g_value_set_boolean (value, priv->start_gps);
380       break;
381     default:
382       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
383       break;
384   }
385 }
386
387 static void
388 position_publisher_set_property (GObject *object,
389                                  guint property_id,
390                                  const GValue *value,
391                                  GParamSpec *pspec)
392 {
393   PositionPublisher *self = POSITION_PUBLISHER (object);
394   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
395
396   switch (property_id) {
397     case PROP_BLUR:
398       priv->blur = g_value_get_boolean (value);
399       break;
400     case PROP_START_GPS:
401       priv->start_gps = g_value_get_boolean (value);
402       if (priv->start_gps)
403         location_gpsd_control_start (priv->gps_control);
404       else
405         location_gpsd_control_stop (priv->gps_control);
406
407       break;
408     default:
409       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
410       break;
411   }
412 }
413
414 PositionPublisher *
415 position_publisher_new (gboolean blur, gboolean start_gps)
416 {
417   return g_object_new (POSITION_PUBLISHER_TYPE,
418       "blur", blur,
419       "start-gps", start_gps,
420       NULL);
421 }