From f6f6c6421e0e286db920d3e01fa64cc3ebe4f4df Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Mon, 2 Sep 2013 14:37:41 +0200 Subject: [PATCH] location-manager: use Geoclue 2 https://bugzilla.gnome.org/show_bug.cgi?id=706627 --- libempathy-gtk/empathy-location-manager.c | 443 ++++------------------ 1 file changed, 72 insertions(+), 371 deletions(-) diff --git a/libempathy-gtk/empathy-location-manager.c b/libempathy-gtk/empathy-location-manager.c index 8216cf31..459adb28 100644 --- a/libempathy-gtk/empathy-location-manager.c +++ b/libempathy-gtk/empathy-location-manager.c @@ -22,11 +22,11 @@ #include "config.h" #include "empathy-location-manager.h" -#include #include #include "empathy-gsettings.h" #include "empathy-location.h" +#include "empathy-geoclue-helper.h" #define DEBUG_FLAG EMPATHY_DEBUG_LOCATION #include "empathy-debug.h" @@ -35,8 +35,16 @@ #define TIMEOUT 10 static EmpathyLocationManager *location_manager = NULL; +typedef enum +{ + GEOCLUE_NONE = 0, + GEOCLUE_STARTING, + GEOCLUE_STARTED, + GEOCLUE_FAILED, +} GeoclueStatus; + struct _EmpathyLocationManagerPrivate { - gboolean geoclue_is_setup; + GeoclueStatus geoclue_status; /* Contains the location to be sent to accounts. Geoclue is used * to populate it. This HashTable uses Telepathy's style (string, * GValue). Keys are defined in empathy-location.h @@ -45,13 +53,9 @@ struct _EmpathyLocationManagerPrivate { GSettings *gsettings_loc; - GeoclueResourceFlags resources; - GeoclueMasterClient *gc_client; - GeocluePosition *gc_position; - GeoclueAddress *gc_address; - gboolean reduce_accuracy; TpAccountManager *account_manager; + EmpathyGeoclueHelper *geoclue; /* The idle id for publish_on_idle func */ guint timeout_id; @@ -91,9 +95,6 @@ location_manager_dispose (GObject *object) tp_clear_object (&self->priv->account_manager); tp_clear_object (&self->priv->gsettings_loc); - tp_clear_object (&self->priv->gc_client); - tp_clear_object (&self->priv->gc_position); - tp_clear_object (&self->priv->gc_address); tp_clear_pointer (&self->priv->location, g_hash_table_unref); if (dispose != NULL) @@ -240,332 +241,104 @@ new_connection_cb (TpAccount *account, } static void -update_timestamp (EmpathyLocationManager *self) +update_location (EmpathyLocationManager *self, + GClueLocation *proxy) { + gdouble latitude, longitude, accuracy; + const gchar *desc; gint64 timestamp; - timestamp = tpaw_time_get_current (); - tp_asv_set_int64 (self->priv->location, EMPATHY_LOCATION_TIMESTAMP, - timestamp); + latitude = gclue_location_get_latitude (proxy); + longitude = gclue_location_get_longitude (proxy); + accuracy = gclue_location_get_accuracy (proxy); + desc = gclue_location_get_description (proxy); - DEBUG ("\t - Timestamp: %" G_GINT64_FORMAT, timestamp); -} - -static void -address_changed_cb (GeoclueAddress *address, - int timestamp, - GHashTable *details, - GeoclueAccuracy *accuracy, - gpointer user_data) -{ - EmpathyLocationManager *self = user_data; - GeoclueAccuracyLevel level; - GHashTableIter iter; - gpointer key, value; - - geoclue_accuracy_get_details (accuracy, &level, NULL, NULL); - DEBUG ("New address (accuracy level %d):", level); - /* FIXME: Publish accuracy level also considering the position's */ - - g_hash_table_remove (self->priv->location, EMPATHY_LOCATION_STREET); - g_hash_table_remove (self->priv->location, EMPATHY_LOCATION_AREA); - g_hash_table_remove (self->priv->location, EMPATHY_LOCATION_REGION); - g_hash_table_remove (self->priv->location, EMPATHY_LOCATION_COUNTRY); - g_hash_table_remove (self->priv->location, EMPATHY_LOCATION_COUNTRY_CODE); - g_hash_table_remove (self->priv->location, EMPATHY_LOCATION_POSTAL_CODE); - - if (g_hash_table_size (details) == 0) - { - DEBUG ("\t - (Empty)"); - return; - } - - g_hash_table_iter_init (&iter, details); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - /* Discard street information if reduced accuracy is on */ - if (self->priv->reduce_accuracy && - !tp_strdiff (key, EMPATHY_LOCATION_STREET)) - continue; - - tp_asv_set_string (self->priv->location, key, value); - - DEBUG ("\t - %s: %s", (gchar *) key, (gchar *) value); - } - - update_timestamp (self); - if (self->priv->timeout_id == 0) - self->priv->timeout_id = g_timeout_add_seconds (TIMEOUT, publish_on_idle, - self); -} + DEBUG ("Location updated: (%f %f) accuracy: %f (%s)", + latitude, longitude, accuracy, desc); -static void -initial_address_cb (GeoclueAddress *address, - int timestamp, - GHashTable *details, - GeoclueAccuracy *accuracy, - GError *error, - gpointer self) -{ - if (error) + if (self->priv->reduce_accuracy) { - DEBUG ("Error: %s", error->message); - g_error_free (error); + /* Truncate at 1 decimal place */ + latitude = ((int) (latitude * 10)) / 10.0; + longitude = ((int) (longitude * 10)) / 10.0; } else { - address_changed_cb (address, timestamp, details, accuracy, self); + /* Include the description only if we are not asked to reduce the + * accuracy as it can contains a pretty specific description of the + * location. */ + tp_asv_set_string (self->priv->location, EMPATHY_LOCATION_DESCRIPTION, + desc); } -} - -static void -position_changed_cb (GeocluePosition *position, - GeocluePositionFields fields, - int timestamp, - double latitude, - double longitude, - double altitude, - GeoclueAccuracy *accuracy, - gpointer user_data) -{ - EmpathyLocationManager *self = user_data; - GeoclueAccuracyLevel level; - gdouble mean, horizontal, vertical; - geoclue_accuracy_get_details (accuracy, &level, &horizontal, &vertical); - DEBUG ("New position (accuracy level %d)", level); - if (level == GEOCLUE_ACCURACY_LEVEL_NONE) - return; - - if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) - { - - if (self->priv->reduce_accuracy) - /* Truncate at 1 decimal place */ - longitude = ((int) (longitude * 10)) / 10.0; - - tp_asv_set_double (self->priv->location, EMPATHY_LOCATION_LON, longitude); - - DEBUG ("\t - Longitude: %f", longitude); - } - else - { - g_hash_table_remove (self->priv->location, EMPATHY_LOCATION_LON); - } + tp_asv_set_double (self->priv->location, EMPATHY_LOCATION_LAT, latitude); + tp_asv_set_double (self->priv->location, EMPATHY_LOCATION_LON, longitude); + tp_asv_set_double (self->priv->location, EMPATHY_LOCATION_ACCURACY, accuracy); - if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE) - { - if (self->priv->reduce_accuracy) - /* Truncate at 1 decimal place */ - latitude = ((int) (latitude * 10)) / 10.0; - - tp_asv_set_double (self->priv->location, EMPATHY_LOCATION_LAT, latitude); - - DEBUG ("\t - Latitude: %f", latitude); - } - else - { - g_hash_table_remove (self->priv->location, EMPATHY_LOCATION_LAT); - } - - if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE) - { - tp_asv_set_double (self->priv->location, EMPATHY_LOCATION_ALT, altitude); - - DEBUG ("\t - Altitude: %f", altitude); - } - else - { - g_hash_table_remove (self->priv->location, EMPATHY_LOCATION_ALT); - } - - if (level == GEOCLUE_ACCURACY_LEVEL_DETAILED) - { - mean = (horizontal + vertical) / 2.0; - tp_asv_set_double (self->priv->location, EMPATHY_LOCATION_ACCURACY, mean); - - DEBUG ("\t - Accuracy: %f", mean); - } - else - { - g_hash_table_remove (self->priv->location, EMPATHY_LOCATION_ACCURACY); - } + timestamp = tpaw_time_get_current (); + tp_asv_set_int64 (self->priv->location, EMPATHY_LOCATION_TIMESTAMP, + timestamp); - update_timestamp (self); if (self->priv->timeout_id == 0) self->priv->timeout_id = g_timeout_add_seconds (TIMEOUT, publish_on_idle, self); } static void -initial_position_cb (GeocluePosition *position, - GeocluePositionFields fields, - int timestamp, - double latitude, - double longitude, - double altitude, - GeoclueAccuracy *accuracy, - GError *error, - gpointer self) -{ - if (error) - { - DEBUG ("Error: %s", error->message); - g_error_free (error); - } - else - { - position_changed_cb (position, fields, timestamp, latitude, longitude, - altitude, accuracy, self); - } -} - -static void -set_requirements (EmpathyLocationManager *self, - GeoclueSetRequirementsCallback callback) +location_changed_cb (EmpathyGeoclueHelper *geoclue, + GClueLocation *location, + EmpathyLocationManager *self) { - geoclue_master_client_set_requirements_async (self->priv->gc_client, - GEOCLUE_ACCURACY_LEVEL_COUNTRY, 0, FALSE, self->priv->resources, - callback, self); + update_location (self, location); } static void -update_resources_set_requirements_cb (GeoclueMasterClient *client, - GError *error, - gpointer userdata) -{ - EmpathyLocationManager *self = userdata; - - if (error != NULL) - { - DEBUG ("set_requirements failed: %s", error->message); - g_error_free (error); - return; - } - - geoclue_address_get_address_async (self->priv->gc_address, - initial_address_cb, self); - geoclue_position_get_position_async (self->priv->gc_position, - initial_position_cb, self); -} - -static void -update_resources (EmpathyLocationManager *self) -{ - DEBUG ("Updating resources %d", self->priv->resources); - - if (!self->priv->geoclue_is_setup) - return; - - /* As per Geoclue bug #15126, using NONE results in no address - * being found as geoclue-manual report an empty address with - * accuracy = NONE */ - set_requirements (self, update_resources_set_requirements_cb); -} - -static void -create_address_cb (GeoclueMasterClient *client, - GeoclueAddress *address, - GError *error, - gpointer userdata) +geoclue_new_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) { - EmpathyLocationManager *self = userdata; - - if (error != NULL) - { - DEBUG ("Failed to create GeoclueAddress: %s", error->message); - g_error_free (error); - return; - } - - self->priv->gc_address = address; - - g_signal_connect (G_OBJECT (self->priv->gc_address), "address-changed", - G_CALLBACK (address_changed_cb), self); - - self->priv->geoclue_is_setup = TRUE; -} + EmpathyLocationManager *self = EMPATHY_LOCATION_MANAGER (user_data); + GError *error = NULL; + GClueLocation *location; -static void -create_position_cb (GeoclueMasterClient *client, - GeocluePosition *position, - GError *error, - gpointer userdata) -{ - EmpathyLocationManager *self = userdata; + self->priv->geoclue = empathy_geoclue_helper_new_started_finish (result, + &error); - if (error != NULL) + if (self->priv->geoclue == NULL) { - DEBUG ("Failed to create GeocluePosition: %s", error->message); + DEBUG ("Failed to create Geoclue client: %s", error->message); g_error_free (error); + self->priv->geoclue_status = GEOCLUE_FAILED; return; } - self->priv->gc_position = position; - - g_signal_connect (G_OBJECT (self->priv->gc_position), "position-changed", - G_CALLBACK (position_changed_cb), self); - - /* Get updated when the address changes */ - geoclue_master_client_create_address_async (self->priv->gc_client, - create_address_cb, self); -} - -static void -create_client_set_requirements_cb (GeoclueMasterClient *client, - GError *error, - gpointer userdata) -{ - EmpathyLocationManager *self = userdata; + self->priv->geoclue_status = GEOCLUE_STARTED; - if (error != NULL) - { - DEBUG ("set_requirements failed: %s", error->message); - g_error_free (error); - return; - } + g_signal_connect_object (self->priv->geoclue, "location-changed", + G_CALLBACK (location_changed_cb), self, 0); - /* Get updated when the position is changes */ - geoclue_master_client_create_position_async (self->priv->gc_client, - create_position_cb, self); + location = empathy_geoclue_helper_get_location (self->priv->geoclue); + if (location != NULL) + update_location (self, location); } static void -create_client_cb (GeoclueMaster *master, - GeoclueMasterClient *client, - char *object_path, - GError *error, - gpointer userdata) +setup_geoclue (EmpathyLocationManager *self) { - EmpathyLocationManager *self = userdata; - - if (error != NULL) + switch (self->priv->geoclue_status) { - DEBUG ("Failed to create GeoclueMasterClient: %s", error->message); - g_error_free (error); + case GEOCLUE_NONE: + g_assert (self->priv->geoclue == NULL); + self->priv->geoclue_status = GEOCLUE_STARTING; + empathy_geoclue_helper_new_started_async (0, geoclue_new_cb, self); + break; + case GEOCLUE_STARTED: + case GEOCLUE_STARTING: + case GEOCLUE_FAILED: return; } - - /* @client seems be (transfer full) looking at the geoclue code; yeah for - * undocumented API... */ - self->priv->gc_client = client; - - set_requirements (self, create_client_set_requirements_cb); } -static void -setup_geoclue (EmpathyLocationManager *self) -{ - GeoclueMaster *master; - - DEBUG ("Setting up Geoclue"); - master = geoclue_master_get_default (); - - geoclue_master_create_client_async (master, create_client_cb, self); - - g_object_unref (master); - } - static void publish_cb (GSettings *gsettings_loc, const gchar *key, @@ -577,16 +350,7 @@ publish_cb (GSettings *gsettings_loc, if (g_settings_get_boolean (gsettings_loc, key)) { - if (!self->priv->geoclue_is_setup) - setup_geoclue (self); - /* if still not setup than the init failed */ - if (!self->priv->geoclue_is_setup) - return; - - geoclue_address_get_address_async (self->priv->gc_address, - initial_address_cb, self); - geoclue_position_get_position_async (self->priv->gc_position, - initial_position_cb, self); + setup_geoclue (self); } else { @@ -595,54 +359,10 @@ publish_cb (GSettings *gsettings_loc, */ g_hash_table_remove_all (self->priv->location); publish_to_all_connections (self, TRUE); - } - -} - -static void -resource_cb (GSettings *gsettings_loc, - const gchar *key, - gpointer user_data) -{ - EmpathyLocationManager *self = EMPATHY_LOCATION_MANAGER (user_data); - GeoclueResourceFlags resource = 0; - - DEBUG ("%s changed", key); - if (!tp_strdiff (key, EMPATHY_PREFS_LOCATION_RESOURCE_NETWORK)) - resource = GEOCLUE_RESOURCE_NETWORK; - if (!tp_strdiff (key, EMPATHY_PREFS_LOCATION_RESOURCE_CELL)) - resource = GEOCLUE_RESOURCE_CELL; - if (!tp_strdiff (key, EMPATHY_PREFS_LOCATION_RESOURCE_GPS)) - resource = GEOCLUE_RESOURCE_GPS; - - if (g_settings_get_boolean (gsettings_loc, key)) - self->priv->resources |= resource; - else - self->priv->resources &= ~resource; - - if (self->priv->geoclue_is_setup) - update_resources (self); -} - -static void -accuracy_cb (GSettings *gsettings_loc, - const gchar *key, - gpointer user_data) -{ - EmpathyLocationManager *self = EMPATHY_LOCATION_MANAGER (user_data); - - DEBUG ("%s changed", key); - - self->priv->reduce_accuracy = g_settings_get_boolean (gsettings_loc, key); - - if (!self->priv->geoclue_is_setup) - return; - - geoclue_address_get_address_async (self->priv->gc_address, - initial_address_cb, self); - geoclue_position_get_position_async (self->priv->gc_position, - initial_position_cb, self); + g_clear_object (&self->priv->geoclue); + self->priv->geoclue_status = GEOCLUE_NONE; + } } static void @@ -680,7 +400,6 @@ empathy_location_manager_init (EmpathyLocationManager *self) EMPATHY_TYPE_LOCATION_MANAGER, EmpathyLocationManagerPrivate); self->priv = priv; - priv->geoclue_is_setup = FALSE; priv->location = tp_asv_new (NULL, NULL); priv->gsettings_loc = g_settings_new (EMPATHY_PREFS_LOCATION_SCHEMA); @@ -694,25 +413,7 @@ empathy_location_manager_init (EmpathyLocationManager *self) g_signal_connect (priv->gsettings_loc, "changed::" EMPATHY_PREFS_LOCATION_PUBLISH, G_CALLBACK (publish_cb), self); - g_signal_connect (priv->gsettings_loc, - "changed::" EMPATHY_PREFS_LOCATION_RESOURCE_NETWORK, - G_CALLBACK (resource_cb), self); - g_signal_connect (priv->gsettings_loc, - "changed::" EMPATHY_PREFS_LOCATION_RESOURCE_CELL, - G_CALLBACK (resource_cb), self); - g_signal_connect (priv->gsettings_loc, - "changed::" EMPATHY_PREFS_LOCATION_RESOURCE_GPS, - G_CALLBACK (resource_cb), self); - g_signal_connect (priv->gsettings_loc, - "changed::" EMPATHY_PREFS_LOCATION_REDUCE_ACCURACY, - G_CALLBACK (accuracy_cb), self); - - resource_cb (priv->gsettings_loc, EMPATHY_PREFS_LOCATION_RESOURCE_NETWORK, - self); - resource_cb (priv->gsettings_loc, EMPATHY_PREFS_LOCATION_RESOURCE_CELL, self); - resource_cb (priv->gsettings_loc, EMPATHY_PREFS_LOCATION_RESOURCE_GPS, self); - accuracy_cb (priv->gsettings_loc, EMPATHY_PREFS_LOCATION_REDUCE_ACCURACY, - self); + publish_cb (priv->gsettings_loc, EMPATHY_PREFS_LOCATION_PUBLISH, self); } -- 2.39.2