--- /dev/null
+/**
+ * @brief Elocation Library
+ *
+ * @mainpage Elocation
+ * @version 0.0.0
+ * @author Stefan Schmidt <stefan@datenfreihafen.org>
+ * @date 2012
+ *
+ * @section intro Elocation Use Cases
+ *
+ * Elocation is meant as a convenience library to ease application developers
+ * the usage of geo information in their apps. Adding a geo tag to a picture or
+ * translating an address to a GPS position and show it on a map widget are just
+ * some of the use cases.
+ *
+ * In the beginning elocation will rely on the GeoClue DBus service. Its has
+ * providers for various techniques to get hold off the current position.
+ * Ranging from GeoIP over wifi and GSM cell location to GPS. As well as
+ * provider to translates between location in a textual form to coordinates
+ * (GeoCode).
+ *
+ * Elocation covers all of these interfaces but in the end it depends on your
+ * system and the installed GeoClue providers what can be used.
+ *
+ * Currently it offer the following functionality:
+ * @li Request current address in textual form
+ * @li Request current position in GPS format
+ * @li Translate a position into and address or an address in a position
+ *
+ * You can find the API documentation at @ref Location
+*/
+#ifndef _ELOCATION_H
+#define _ELOCATION_H
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_ECORE_BUILD
+# ifdef DLL_EXPORT
+# define EAPI __declspec(dllexport)
+# else
+# define EAPI
+# endif /* ! DLL_EXPORT */
+# else
+# define EAPI __declspec(dllimport)
+# endif /* ! EFL_ECORE_BUILD */
+#else
+# ifdef __GNUC__
+# if __GNUC__ >= 4
+# define EAPI __attribute__ ((visibility("default")))
+# else
+# define EAPI
+# endif
+# else
+# define EAPI
+# endif
+#endif /* ! _WIN32 */
+
+#include <stdio.h>
+
+#include <Ecore.h>
+#include <Eldbus.h>
+
+/**
+ * @file Elocation.h
+ *
+ * @defgroup Location Location
+ */
+
+/**
+ * @ingroup Location
+ * @brief Available location events that are emitted from the library
+ * @since 1.8
+ *
+ * Ecore events emitted by the library. Applications can register ecore event
+ * handlers to react on such events. After the initial query this can be used
+ * to keep track of changes and update your UI or data accordingly.
+ * @{
+ */
+EAPI extern int ELOCATION_EVENT_STATUS; /**< Status changed */
+EAPI extern int ELOCATION_EVENT_POSITION; /**< Position changed */
+EAPI extern int ELOCATION_EVENT_ADDRESS; /**< Address changed */
+EAPI extern int ELOCATION_EVENT_VELOCITY; /**< Velocity changed */
+EAPI extern int ELOCATION_EVENT_GEOCODE; /**< Reply for geocode translation arrived */
+EAPI extern int ELOCATION_EVENT_REVERSEGEOCODE; /**< Reply for geocode translation arrived */
+EAPI extern int ELOCATION_EVENT_NMEA; /**< NMEA update */
+EAPI extern int ELOCATION_EVENT_SATELLITE; /**< Satellite info changed */
+EAPI extern int ELOCATION_EVENT_POI; /**< POI reply */
+EAPI extern int ELOCATION_EVENT_META_READY; /**< Meta provider is ready to be used */
+/**@}*/
+
+/**
+ * @ingroup Location
+ * @typedef Elocation_Accuracy_Level
+ * @since 1.8
+ *
+ * Different location accuracy levels from country level up to detailed,
+ * e.g. GPS, information.
+ * @{
+ */
+typedef enum {
+ ELOCATION_ACCURACY_LEVEL_NONE = 0,
+ ELOCATION_ACCURACY_LEVEL_COUNTRY,
+ ELOCATION_ACCURACY_LEVEL_REGION,
+ ELOCATION_ACCURACY_LEVEL_LOCALITY,
+ ELOCATION_ACCURACY_LEVEL_POSTALCODE,
+ ELOCATION_ACCURACY_LEVEL_STREET,
+ ELOCATION_ACCURACY_LEVEL_DETAILED,
+} Elocation_Accuracy_Level;
+/**@}*/
+
+/**
+ * @ingroup Location
+ * @typedef Elocation_Resource_Flags
+ * @since 1.8
+ *
+ * Flags for available system resources to be used for locating. So far they
+ * cover physical resources like network connection, cellular network
+ * connection and GPS.
+ * @{
+ */
+typedef enum {
+ ELOCATION_RESOURCE_NONE = 0,
+ ELOCATION_RESOURCE_NETWORK = 1 << 0, /**< Internet connection is available */
+ ELOCATION_RESOURCE_CELL = 1 << 1, /**< Cell network information, e.g. GSM, is available */
+ ELOCATION_RESOURCE_GPS = 1 << 2, /**< GPS information is available */
+
+ ELOCATION_RESOURCE_ALL = (1 << 10) - 1 /**< All resources are available */
+} Elocation_Resource_Flags;
+/**@}*/
+
+/**
+ * @ingroup Location
+ * @typedef Elocation_Accuracy
+ * @since 1.8
+ *
+ * Information about the accuracy of the reported location. For details about
+ * the level of accuracy see #Elocation_Accuracy_Level. It also covers
+ * horizontal and vertical accuracy. The values depend on the used provider
+ * and may very in quality.
+ */
+typedef struct _Elocation_Accuracy
+{
+ Elocation_Accuracy_Level level;
+ double horizontal;
+ double vertical;
+} Elocation_Accuracy;
+
+/**
+ * @ingroup Location
+ * @typedef Elocation_Address
+ * @since 1.8
+ *
+ * Location information in textual form. Depending on the used provider this
+ * can cover only the country or a detailed address with postcode and street.
+ * The level of detail varies depending on the used provider.
+ * A timestamp is available to calculate the age of the address data.
+ */
+typedef struct _Elocation_Address
+{
+ unsigned int timestamp; /**< Timestamp of data read out in seconds since epoch */
+ char *country;
+ char *countrycode;
+ char *locality;
+ char *postalcode;
+ char *region;
+ char *timezone;
+ Elocation_Accuracy *accur;
+} Elocation_Address;
+
+/**
+ * @ingroup Location
+ * @typedef Elocation_Position
+ * @since 1.8
+ *
+ * Location information based on the GPS grid. Latitude, longitude and altitude.
+ * A timestamp is available to calculate the age of the address data.
+ */
+typedef struct _Elocation_Postion
+{
+ unsigned int timestamp; /**< Timestamp of data read out in seconds since epoch */
+ double latitude;
+ double longitude;
+ double altitude;
+ Elocation_Accuracy *accur;
+} Elocation_Position;
+
+/**
+ * @ingroup Location
+ * @typedef Elocation_Velocity
+ * @since 1.8
+ *
+ * Velocity information. So far this interface is only offered with GPS based
+ * providers. It offers information about speed, direction and climb.
+ * A timestamp is available to calculate the age of the address data.
+ *
+ * FIXME: check units and formats of this values coming in from GeoClue
+ */
+typedef struct _Elocation_Velocity
+{
+ unsigned int timestamp; /**< Timestamp of data read out in seconds since epoch */
+ double speed;
+ double direction;
+ double climb;
+} Elocation_Velocity;
+
+/**
+ * @ingroup Location
+ * @typedef Elocation_Requirements
+ * @since 1.8
+ *
+ * Requirement settings for the location provider. Requirements can be a level
+ * of accuracy or allowed resources like network access or GPS. See
+ * #Elocation_Resource_Flags for all available resource flags.
+ *
+ * Based on this setting the best provider is chosen between the available
+ * providers of GeoClue.
+ */
+typedef struct _Elocation_Requirements
+{
+ Elocation_Accuracy_Level accurancy_level;
+ int min_time; /**< Minimal time between updates. Not implemented upstream */
+ Eina_Bool require_update;
+ Elocation_Resource_Flags allowed_resources;
+} Elocation_Requirements;
+
+/**
+ * @brief Create a new address object to operate on.
+ * @return Address object.
+ *
+ * The returned address object is safe to be operated on. It can be used for
+ * all other elocation functions. Once finished with it it need to be destroyed
+ * with a call to #elocation_address_free.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Elocation_Address *elocation_address_new(void);
+
+/**
+ * @brief Free an address object
+ * @param address Address object to be freed.
+ *
+ * Destroys an address object created with #elocation_address_new. Should be
+ * used during the cleanup of the application or whenever the address object is
+ * no longer needed.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI void elocation_address_free(Elocation_Address *address);
+
+/**
+ * @brief Create a new position object to operate on.
+ * @return Position object.
+ *
+ * The returned address object is safe to be operated on. It can be used for
+ * all other elocation functions. Once finished with it it need to be destroyed
+ * with a call to #elocation_address_free.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Elocation_Position *elocation_position_new(void);
+
+/**
+ * @brief Free an position object
+ * @param position Position object to be freed.
+ *
+ * Destroys a position object created with #elocation_address_new. Should be
+ * used during the cleanup of the application or whenever the location object is
+ * no longer needed.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI void elocation_position_free(Elocation_Position *position);
+
+/**
+ * @brief Get the current address information.
+ * @param address Address struct to be filled with information.
+ * @return EINA_TRUE for success and EINA_FALSE for failure.
+ *
+ * Request the latest address. The requested to the underling components might
+ * be asynchronous so better check the timestamp if the data has changed. To get
+ * events when the address changes one can also subscribe to the
+ * #ELOCATION_EVENT_ADDRESS ecore event which will have the address object as
+ * event.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Eina_Bool elocation_address_get(Elocation_Address *address);
+
+/**
+ * @brief Get the current position information.
+ * @param position Position struct to be filled with information.
+ * @return EINA_TRUE for success and EINA_FALSE for failure.
+ *
+ * Request the latest position. The requested to the underling components might
+ * be asynchronous so better check the timestamp if the data has changed. To get
+ * events when the position changes one can also subscribe to the
+ * #ELOCATION_EVENT_POSITION ecore event which will have the position object as
+ * event.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Eina_Bool elocation_position_get(Elocation_Position *position);
+
+/**
+ * @brief Get the current status.
+ * @param status Status
+ * @return EINA_TRUE for success and EINA_FALSE for failure.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Eina_Bool elocation_status_get(int *status);
+
+/**
+ * @brief Set the requirements.
+ * @param requirements Requirements
+ * @return EINA_TRUE for success and EINA_FALSE for failure.
+ *
+ * Set the requirements for selecting a provider.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Eina_Bool elocation_requirements_set(Elocation_Requirements *requirements);
+
+/**
+ * @brief Convert position to address
+ * @param position_shadow Position input
+ * @param address_shadow Address output
+ * @return EINA_TRUE for success and EINA_FALSE for failure.
+ *
+ * Use a GeoCode provider to translate from a given GPS coordinate
+ * representation of a location to a representation in textual form.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Eina_Bool elocation_position_to_address(Elocation_Position *position_shadow, Elocation_Address *address_shadow);
+
+/**
+ * @brief Convert address to position
+ * @param address_shadow Address input
+ * @param position_shadow Position output
+ * @return EINA_TRUE for success and EINA_FALSE for failure.
+ *
+ * Use a GeoCode provider to translate from a given textual form
+ * representation of a location to a representation as GPS coordinates.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Eina_Bool elocation_address_to_position(Elocation_Address *address_shadow, Elocation_Position *position_shadow);
+
+/**
+ * @brief Convert free form address tring to position
+ * @param freeform_address Address string in free form
+ * @param position_shadow Position output
+ * @return EINA_TRUE for success and EINA_FALSE for failure.
+ *
+ * Similar GeoCode translation from textual form to GPS coordinates as
+ * #elocation_address_to_position but in this case the address is a simple
+ * string which hopefully contains enough information for the provider to
+ * understand and translate.
+ *
+ * Useful for an easy search interface in an application but also more error
+ * prone regarding correct results.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Eina_Bool elocation_freeform_address_to_position(const char *freeform_address, Elocation_Position *position_shadow);
+
+/**
+ * @brief Request a landmark position
+ * @param position_shadow Position ouput
+ * @param address_shadow Address input
+ * @return EINA_TRUE for success and EINA_FALSE for failure.
+ *
+ * Request a landmark position also known as Point Of Interest (POI) from
+ * GeoClue.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Eina_Bool elocation_landmarks_get(Elocation_Position *position_shadow, Elocation_Address *address_shadow);
+
+/**
+ * @brief Initialize the elocation subsystem.
+ * @return EINA_TRUE for success and EINA_FALSE for failure.
+ *
+ * This function must be called before using any of the Elocation functionality
+ * in your application to make sure it it setup correctly for usage.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI Eina_Bool elocation_init(void);
+
+/**
+ * @brief Cleanup and shutdown the elocation subsystem.
+ *
+ * This function must be called when the application is no longer using any of
+ * the Elocation functionality to allow the subsystem to shutdown cleanly.
+ *
+ * @ingroup Location
+ * @since 1.8
+ */
+EAPI void elocation_shutdown(void);
+#endif
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include <Eina.h>
+#include <Ecore.h>
+#include <Eldbus.h>
+#include <Elocation.h>
+#include <elocation_private.h>
+
+/* FIXME: These globals really need to get reduced before leaving the PROTO
+ * area.
+ */
+static char *unique_name = NULL;
+static Eldbus_Connection *conn = NULL;
+static Elocation_Provider *address_provider = NULL;
+static Elocation_Provider *position_provider = NULL;
+static Eldbus_Object *obj_meta = NULL;
+static Eldbus_Proxy *manager_master = NULL;
+static Eldbus_Proxy *meta_geoclue = NULL;
+static Eldbus_Proxy *meta_address = NULL;
+static Eldbus_Proxy *meta_position = NULL;
+static Eldbus_Proxy *meta_masterclient = NULL;
+static Eldbus_Proxy *meta_velocity = NULL;
+static Eldbus_Proxy *meta_nmea = NULL;
+static Eldbus_Proxy *meta_satellite = NULL;
+static Eldbus_Proxy *geonames_geocode = NULL;
+static Eldbus_Proxy *geonames_rgeocode = NULL;
+static Eldbus_Proxy *master_poi = NULL;
+static Elocation_Address *address = NULL;
+static Elocation_Position *position = NULL;
+static Elocation_Address *addr_geocode = NULL;
+static Elocation_Position *pos_geocode = NULL;
+static Elocation_Velocity *velocity = NULL;
+static int *status = -1; /* 0 is a valid status code */
+static char nmea_sentence[256];
+
+int _elocation_log_dom = -1;
+
+/* Elocation ecore event types we provide to the application. */
+EAPI int ELOCATION_EVENT_IN;
+EAPI int ELOCATION_EVENT_OUT;
+EAPI int ELOCATION_EVENT_STATUS;
+EAPI int ELOCATION_EVENT_POSITION;
+EAPI int ELOCATION_EVENT_ADDRESS;
+EAPI int ELOCATION_EVENT_VELOCITY;
+EAPI int ELOCATION_EVENT_GEOCODE;
+EAPI int ELOCATION_EVENT_REVERSEGEOCODE;
+EAPI int ELOCATION_EVENT_NMEA;
+EAPI int ELOCATION_EVENT_SATELLITE;
+EAPI int ELOCATION_EVENT_POI;
+EAPI int ELOCATION_EVENT_META_READY;
+
+static void
+_dummy_free(void *user_data, void *func_data)
+{
+ /* Don't free the event data after dispatching the event. We keep track of
+ * it on our own
+ */
+}
+
+/* Generic provider message unmarshaller. Used from all different provider
+ * calbacks that receive such a message
+ */
+static Eina_Bool
+unmarshall_provider(const Eldbus_Message *reply, Elocation_Provider *provider)
+{
+ char *name = NULL, *desc = NULL, *service = NULL, *path = NULL;
+
+ if (!eldbus_message_arguments_get(reply, "ssss", &name, &desc, &service, &path))
+ return EINA_FALSE;
+
+ provider->name = strdup(name);
+ provider->description = strdup(desc);
+ provider->service = strdup(service);
+ provider->path = strdup(path);
+ return EINA_TRUE;
+}
+
+static void
+meta_address_provider_info_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+ Elocation_Provider *addr_provider;
+
+ addr_provider = data;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!unmarshall_provider(reply, addr_provider))
+ {
+ ERR("Error: Unable to unmarshall address provider");
+ return;
+ }
+
+ DBG("Meta address provider name: %s, %s, %s, %s", addr_provider->name,
+ addr_provider->description,
+ addr_provider->service,
+ addr_provider->path);
+}
+
+static void
+meta_position_provider_info_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+ Elocation_Provider *pos_provider;
+
+ pos_provider = data;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!unmarshall_provider(reply, pos_provider))
+ {
+ ERR("Error: Unable to unmarshall position provider");
+ return;
+ }
+
+ DBG("Meta position provider name: %s, %s, %s, %s", pos_provider->name,
+ pos_provider->description,
+ pos_provider->service,
+ pos_provider->path);
+}
+
+static void
+meta_address_provider_info_signal_cb(void *data, const Eldbus_Message *reply)
+{
+ Elocation_Provider *addr_provider;
+ addr_provider = data;
+
+ if (!unmarshall_provider(reply, addr_provider))
+ {
+ ERR("Error: Unable to unmarshall address provider");
+ return;
+ }
+
+ DBG("Meta address provider name changed: %s, %s, %s, %s", addr_provider->name,
+ addr_provider->description,
+ addr_provider->service,
+ addr_provider->path);
+}
+
+static void
+meta_position_provider_info_signal_cb(void *data, const Eldbus_Message *reply)
+{
+ Elocation_Provider *pos_provider;
+ pos_provider = data;
+
+ if (!unmarshall_provider(reply, pos_provider))
+ {
+ ERR("Error: Unable to unmarshall position provider");
+ return;
+ }
+
+ DBG("Meta position provider name changed: %s, %s, %s, %s", pos_provider->name,
+ pos_provider->description,
+ pos_provider->service,
+ pos_provider->path);
+}
+
+/* A address is quite flexible what kind of key value pairs it contains in the
+ * dict. Similar to a reverse GeoCode message as both return an address object.
+ */
+static Eina_Bool
+unmarshall_address(const Eldbus_Message *reply, Elocation_Address *addr)
+{
+ int32_t level, timestamp;
+ Eldbus_Message_Iter *sub, *dict, *entry;
+ double horizontal;
+ double vertical;
+ const char *key, *signature;
+ char *value;
+
+ signature = eldbus_message_signature_get(reply);
+
+ if (!strcmp(signature, "ia{ss}(idd)"))
+ {
+ if (!eldbus_message_arguments_get(reply, "ia{ss}(idd)", ×tamp, &dict, &sub))
+ return EINA_FALSE;
+ addr->timestamp = timestamp;
+ }
+ else if (!strcmp(signature, "a{ss}(idd)"))
+ {
+ if (!eldbus_message_arguments_get(reply, "a{ss}(idd)", &dict, &sub))
+ return EINA_FALSE;
+ addr->timestamp = 0;
+ }
+ else
+ return EINA_FALSE;
+
+
+ /* Cleanup potential old entries before re-using */
+ addr->country = NULL;
+ addr->countrycode = NULL;
+ addr->locality = NULL;
+ addr->postalcode = NULL;
+ addr->region = NULL;
+ addr->timezone = NULL;
+
+ while (eldbus_message_iter_get_and_next(dict, 'e', &entry))
+ {
+ eldbus_message_iter_arguments_get(entry, "ss", &key, &value);
+
+ if (!strcmp(key, "country"))
+ {
+ free(addr->country);
+ addr->country = strdup(value);
+ }
+ else if (!strcmp(key, "countrycode"))
+ {
+ free(addr->countrycode);
+ addr->countrycode = strdup(value);
+ }
+ else if (!strcmp(key, "locality"))
+ {
+ free(addr->locality);
+ addr->locality = strdup(value);
+ }
+ else if (!strcmp(key, "postalcode"))
+ {
+ free(addr->postalcode);
+ addr->postalcode = strdup(value);
+ }
+ else if (!strcmp(key, "region"))
+ {
+ free(addr->region);
+ addr->region = strdup(value);
+ }
+ else if (!strcmp(key, "timezone"))
+ {
+ free(addr->timezone);
+ addr->timezone = strdup(value);
+ }
+ }
+
+ eldbus_message_iter_arguments_get(sub, "idd", &level, &horizontal, &vertical);
+ addr->accur->level = level;
+ addr->accur->horizontal = horizontal;
+ addr->accur->vertical = vertical;
+ return EINA_TRUE;
+}
+
+/* Receive and unmarshall a reverse GeoCode message. The dict can contain a
+ * variable set of key value pairs so we need to handle this with care
+ */
+static void
+rgeocode_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!unmarshall_address(reply, addr_geocode))
+ {
+ ERR("Error: Unable to unmarshall address");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_REVERSEGEOCODE, addr_geocode, _dummy_free, NULL);
+}
+
+/* Point of Interest (POI) aka landmark message unmarshalling. Thsi interface is
+ * not in standard GeoClue but currently a Tizen extension.
+ */
+static void
+poi_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ int32_t count, id, rank;
+ double lat, lon, bound_left, bound_top, bound_right, bound_bottom;
+ const char *name, *icon, *house, *road, *village, *suburb, *postcode;
+ const char *city, *county, *country, *country_code;
+ Eldbus_Message_Iter *array, *struct_landmark;
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ /* Yeah, its quite a horrible message. The POI interface could use a better design */
+ if (!eldbus_message_arguments_get(reply, "ia(iiddddddsssssssssss", &count ,&array))
+ return;
+
+ /* TODO re-check that the parameter ordering is what we expect */
+ while (eldbus_message_iter_get_and_next(array, 'r', &struct_landmark))
+ {
+ eldbus_message_iter_arguments_get(struct_landmark, "iiddddddsssssssssss", &id, &rank,
+ &lat, &lon, &bound_left, &bound_top, &bound_right,
+ &bound_bottom, &name, &icon, &house, &road,
+ &village, &suburb, &postcode, &city, &county,
+ &country, &country_code);
+ DBG("Landmark received: %i, %i, %f, %f, %f, %f, %f, %f, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,",
+ id, rank, lat, lon, bound_left, bound_top, bound_right,
+ bound_bottom, name, icon, house, road, village,
+ suburb, postcode, city, county, country, country_code);
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_POI, NULL, _dummy_free, NULL);
+}
+
+/* Unmarshall a GeoCode message */
+static void
+geocode_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ GeocluePositionFields fields;
+ int32_t level;
+ double horizontal = 0.0;
+ double vertical = 0.0;
+ double latitude = 0.0;
+ double longitude = 0.0;
+ double altitude = 0.0;
+ Eldbus_Message_Iter *sub;
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!eldbus_message_arguments_get(reply, "iddd(idd)", &fields,&latitude,
+ &longitude, &altitude, &sub))
+ return;
+
+ /* GeoClue uses some flags to mark position fields as valid. We set invalid
+ * fields to 0.0 */
+ if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE)
+ pos_geocode->latitude = latitude;
+ else
+ pos_geocode->latitude = 0.0;
+
+ if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE)
+ pos_geocode->longitude = longitude;
+ else
+ pos_geocode->longitude = 0.0;
+
+ if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE)
+ pos_geocode->altitude = altitude;
+ else
+ pos_geocode->altitude = 0.0;
+
+ eldbus_message_iter_arguments_get(sub, "idd", &level, &horizontal, &vertical);
+ pos_geocode->accur->level = level;
+ pos_geocode->accur->horizontal = horizontal;
+ pos_geocode->accur->vertical = vertical;
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_GEOCODE, pos_geocode, _dummy_free, NULL);
+}
+
+static void
+address_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!unmarshall_address(reply, address))
+ {
+ ERR("Error: Unable to unmarshall address");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_ADDRESS, address, _dummy_free, NULL);
+}
+
+static void
+address_signal_cb(void *data, const Eldbus_Message *reply)
+{
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!unmarshall_address(reply, address))
+ {
+ ERR("Error: Unable to unmarshall address");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_ADDRESS, address, _dummy_free, NULL);
+}
+
+/* Unmarshall a velocity message. This is only available if we use a GPS
+ * provider from GeoClue. None of the other providers offer this currently.
+ */
+static Eina_Bool
+unmarshall_velocity(const Eldbus_Message *reply)
+{
+ GeoclueVelocityFields fields;
+ int32_t timestamp = 0;
+ double speed = 0.0;
+ double direction = 0.0;
+ double climb = 0.0;
+
+ if (!eldbus_message_arguments_get(reply, "iiddd", &fields, ×tamp,
+ &speed, &direction, &climb))
+ return EINA_FALSE;
+
+ velocity->timestamp = timestamp;
+
+ /* GeoClue uses some flags to mark velocity fields as valid. We set invalid
+ * fields to 0.0 */
+ if (fields & GEOCLUE_VELOCITY_FIELDS_SPEED)
+ velocity->speed = speed;
+ else
+ velocity->speed = 0.0;
+
+ if (fields & GEOCLUE_VELOCITY_FIELDS_DIRECTION)
+ velocity->direction = direction;
+ else
+ velocity->direction = 0.0;
+
+ if (fields & GEOCLUE_VELOCITY_FIELDS_CLIMB)
+ velocity->climb = climb;
+ else
+ velocity->climb = 0.0;
+
+ return EINA_TRUE;
+}
+
+static void
+velocity_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ WARN("Warning: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!unmarshall_velocity(reply))
+ {
+ ERR("Error: Unable to unmarshall velocity");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_VELOCITY, velocity, _dummy_free, NULL);
+}
+
+static void
+velocity_signal_cb(void *data, const Eldbus_Message *reply)
+{
+ if (!unmarshall_velocity(reply))
+ {
+ ERR("Error: Unable to unmarshall velocity");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_VELOCITY, velocity, _dummy_free, NULL);
+}
+
+/* Unmarshall an raw NMEA message. It conatins a raw NMEA sentence which we can
+ * pass on to applications that want to use their own NMEA parser. This is not
+ * reommended. Better use the other interfaces to access the needed data.
+ *
+ * This is currently a Tizen only interface and not in GeoClue upstream.
+ */
+static Eina_Bool
+unmarshall_nmea(const Eldbus_Message *reply)
+{
+ int32_t timestamp = 0;
+
+ if (!eldbus_message_arguments_get(reply, "is", ×tamp, &nmea_sentence))
+ return EINA_FALSE;
+
+ return EINA_TRUE;
+}
+
+static void
+nmea_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ WARN("Warning: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!unmarshall_nmea(reply))
+ {
+ ERR("Error: Unable to unmarshall nmea");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_NMEA, nmea_sentence, _dummy_free, NULL);
+}
+
+static void
+nmea_signal_cb(void *data, const Eldbus_Message *reply)
+{
+ if (!unmarshall_nmea(reply))
+ {
+ ERR("Error: Unable to unmarshall nmea");
+ return;
+ }
+
+ ecore_event_add(ELOCATION_EVENT_NMEA, nmea_sentence, _dummy_free, NULL);
+}
+
+/* Unmarshall a satellite information message. This offers GPS specific
+ * information about the used satellites and its properties. It can be used for
+ * applications that rely on GPS and want to show more information like a 3D fix
+ * or used satellites.
+ *
+ * This is currently a Tizen only interface and not available in GeoClue upstream.
+ */
+static Eina_Bool
+unmarshall_satellite(const Eldbus_Message *reply)
+{
+ int32_t timestamp = 0, satellite_used = 0, satellite_visible = 0;
+ int32_t snr = 0, elevation = 0, azimuth = 0, prn = 0, used_prn = 0;
+ Eldbus_Message_Iter *sub_prn, *sub_info, *struct_info;
+
+ if (!eldbus_message_arguments_get(reply, "iiiaia(iiii)", ×tamp, &satellite_used,
+ &satellite_visible, &sub_prn, &sub_info))
+ return EINA_FALSE;
+
+ while (eldbus_message_iter_get_and_next(sub_prn, 'i', &used_prn))
+ {
+ DBG("Satellite used PRN %i", used_prn);
+ }
+
+ /* TODO re-check that the parameter ordering is what we expect */
+ while (eldbus_message_iter_get_and_next(sub_info, 'r', &struct_info))
+ {
+ eldbus_message_iter_arguments_get(struct_info, "iiii", &prn, &elevation, &azimuth, &snr);
+ DBG("Satellite info %i, %i, %i, %i", prn, elevation, azimuth, snr);
+ }
+
+ return EINA_TRUE;
+}
+
+static void
+satellite_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ WARN("Warning: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!unmarshall_satellite(reply))
+ {
+ ERR("Error: Unable to unmarshall satellite");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_SATELLITE, NULL, _dummy_free, NULL);
+}
+
+static void
+last_satellite_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ WARN("Warning: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!unmarshall_satellite(reply))
+ {
+ ERR("Error: Unable to unmarshall last satellite");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_SATELLITE, NULL, _dummy_free, NULL);
+}
+
+static void
+satellite_signal_cb(void *data, const Eldbus_Message *reply)
+{
+ if (!unmarshall_satellite(reply))
+ {
+ ERR("Error: Unable to unmarshall satellite");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_SATELLITE, NULL, _dummy_free, NULL);
+}
+
+/* Unmarshall position coordination message */
+static Eina_Bool
+unmarshall_position(const Eldbus_Message *reply)
+{
+ GeocluePositionFields fields;
+ int32_t level, timestamp;
+ double horizontal = 0.0;
+ double vertical = 0.0;
+ double latitude = 0.0;
+ double longitude = 0.0;
+ double altitude = 0.0;
+ Eldbus_Message_Iter *sub;
+
+ if (!eldbus_message_arguments_get(reply, "iiddd(idd)", &fields, ×tamp,
+ &latitude, &longitude, &altitude, &sub))
+ return EINA_FALSE;
+
+ if (!eldbus_message_iter_arguments_get(sub, "idd", &level, &horizontal, &vertical))
+ return EINA_FALSE;
+
+ position->timestamp = timestamp;
+
+ /* GeoClue uses some flags to mark position fields as valid. We set invalid
+ * fields to 0.0 */
+ if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE)
+ position->latitude = latitude;
+ else
+ position->latitude = 0.0;
+
+ if (fields & GEOCLUE_POSITION_FIELDS_LONGITUDE)
+ position->longitude = longitude;
+ else
+ position->longitude = 0.0;
+
+ if (fields & GEOCLUE_POSITION_FIELDS_ALTITUDE)
+ position->altitude = altitude;
+ else
+ position->altitude = 0.0;
+
+ position->accur->level = level;
+ position->accur->horizontal = horizontal;
+ position->accur->vertical = vertical;
+
+ return EINA_TRUE;
+}
+
+static void
+position_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!unmarshall_position(reply))
+ {
+ ERR("Error: Unable to unmarshall position");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_POSITION, position, _dummy_free, NULL);
+}
+
+static void
+position_signal_cb(void *data, const Eldbus_Message *reply)
+{
+ if (!unmarshall_position(reply))
+ {
+ ERR("Error: Unable to unmarshall position");
+ return;
+ }
+
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_POSITION, position, _dummy_free, NULL);
+}
+
+static Eina_Bool
+geoclue_start(void *data, int ev_type, void *event)
+{
+ DBG("GeoClue start event at %s", unique_name);
+ return ECORE_CALLBACK_DONE;
+}
+
+static Eina_Bool
+geoclue_stop(void *data, int ev_type, void *event)
+{
+ DBG("GeoClue stop event");
+ return ECORE_CALLBACK_DONE;
+}
+
+static void
+_reference_add_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ DBG("Reference added");
+}
+
+static void
+_reference_del_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ /* Dummy callback. We are not waiting for any reply here on shutdown */
+}
+
+static void
+status_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ /* We need this to be malloced to be passed to ecore_event_add. Or provide a dummy free callback. */
+ status = malloc(sizeof(*status));
+
+ if (!eldbus_message_arguments_get(reply,"i", status))
+ {
+ ERR("Error: Unable to unmarshall status");
+ return;
+ }
+
+ address_provider->status = position_provider->status = *status;
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_STATUS, status, NULL, NULL);
+}
+
+static void
+status_signal_cb(void *data, const Eldbus_Message *reply)
+{
+ /* We need this to be malloced to be passed to ecore_event_add. Or provide a dummy free callback. */
+ status = malloc(sizeof(*status));
+
+ if (!eldbus_message_arguments_get(reply,"i", status))
+ {
+ ERR("Error: Unable to unmarshall status");
+ return;
+ }
+
+ address_provider->status = position_provider->status = *status;
+ /* Send out an event to all interested parties that we have an update */
+ ecore_event_add(ELOCATION_EVENT_STATUS, status, NULL, NULL);
+}
+
+static void
+_dummy_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
+{
+}
+
+/* We got notified from GeoClue that the meta-provider we asked for is now
+ * ready. That means we can finish up our initialization and set up all
+ * callbacks and handling for the interfaces we use on the meta-provider.
+ *
+ * We also call the interfaces to get an initial set of data that we can provide
+ * to eager aplications.
+ */
+static void
+create_cb(void *data, const Eldbus_Message *reply, Eldbus_Pending *pending)
+{
+ const char *object_path;
+ Eina_Bool updates;
+ int accur_level, min_time, resources;
+ const char *err, *errmsg;
+
+ if (eldbus_message_error_get(reply, &err, &errmsg))
+ {
+ ERR("Error: %s %s", err, errmsg);
+ return;
+ }
+
+ if (!eldbus_message_arguments_get(reply, "o", &object_path)) return;
+
+ DBG("Object path for client: %s", object_path);
+
+ /* With the created object path we now have a meta provider we can operate on.
+ * Geoclue handles the selection of the best provider internally for the meta
+ * provider */
+ obj_meta = eldbus_object_get(conn, GEOCLUE_DBUS_NAME, object_path);
+ if (!obj_meta)
+ {
+ ERR("Error: could not get object for client");
+ return;
+ }
+
+ meta_geoclue = eldbus_proxy_get(obj_meta, GEOCLUE_GEOCLUE_IFACE);
+ if (!meta_geoclue)
+ {
+ ERR("Error: could not get proxy for geoclue");
+ return;
+ }
+
+ meta_address = eldbus_proxy_get(obj_meta, GEOCLUE_ADDRESS_IFACE);
+ if (!meta_address)
+ {
+ ERR("Error: could not get proxy address");
+ return;
+ }
+
+ meta_position = eldbus_proxy_get(obj_meta, GEOCLUE_POSITION_IFACE);
+ if (!meta_position)
+ {
+ ERR("Error: could not get proxy for position");
+ return;
+ }
+
+ meta_masterclient = eldbus_proxy_get(obj_meta, GEOCLUE_MASTERCLIENT_IFACE);
+ if (!meta_masterclient)
+ {
+ ERR("Error: could not get proxy for master client");
+ return;
+ }
+
+ meta_velocity = eldbus_proxy_get(obj_meta, GEOCLUE_VELOCITY_IFACE);
+ if (!meta_velocity)
+ {
+ ERR("Error: could not get proxy for velocity");
+ return;
+ }
+
+ meta_nmea = eldbus_proxy_get(obj_meta, GEOCLUE_NMEA_IFACE);
+ if (!meta_nmea)
+ {
+ ERR("Error: could not get proxy for nmea");
+ return;
+ }
+
+ meta_satellite = eldbus_proxy_get(obj_meta, GEOCLUE_SATELLITE_IFACE);
+ if (!meta_satellite)
+ {
+ ERR("Error: could not get proxy for satellite");
+ return;
+ }
+
+ /* Send Geoclue a set of requirements we have for the provider and start the address and position
+ * meta provider afterwards. After this we should be ready for operation. */
+ updates = EINA_FALSE; /* Especially the web providers do not offer updates */
+ accur_level = ELOCATION_ACCURACY_LEVEL_COUNTRY;
+ min_time = 0; /* Minimal times between updates (no implemented yet) */
+ resources = ELOCATION_RESOURCE_ALL;
+
+ eldbus_proxy_signal_handler_add(meta_masterclient, "AddressProviderChanged",
+ meta_address_provider_info_signal_cb, address_provider);
+ eldbus_proxy_signal_handler_add(meta_masterclient, "PositionProviderChanged",
+ meta_position_provider_info_signal_cb, position_provider);
+
+ if (!eldbus_proxy_call(meta_masterclient, "SetRequirements", _dummy_cb, NULL, -1, "iibi",
+ accur_level, min_time, updates, resources))
+ {
+ ERR("Error: could not call SetRequirements");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_masterclient, "AddressStart", _dummy_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call AddressStart");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_masterclient, "PositionStart", _dummy_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call PositionStart");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_geoclue, "AddReference", _reference_add_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call AddReference");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_address, "GetAddress", address_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call GetAddress");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_position, "GetPosition", position_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call GetPosition");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_geoclue, "GetStatus", status_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call GetStatus");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_velocity, "GetVelocity", velocity_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call GetVelocity");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_nmea, "GetNmea", nmea_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call GetNmea");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_satellite, "GetSatellite", satellite_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call GetSatellite");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_satellite, "GetLastSatellite", last_satellite_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call GetLastSatellite");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_masterclient, "GetAddressProvider", meta_address_provider_info_cb,
+ address_provider, -1, ""))
+ {
+ ERR("Error: could not call GetAddressProvider");
+ return;
+ }
+
+ if (!eldbus_proxy_call(meta_masterclient, "GetPositionProvider", meta_position_provider_info_cb,
+ position_provider, -1, ""))
+ {
+ ERR("Error: could not call GetPositionProvider");
+ return;
+ }
+
+ eldbus_proxy_signal_handler_add(meta_address, "AddressChanged", address_signal_cb, NULL);
+ eldbus_proxy_signal_handler_add(meta_position, "PositionChanged", position_signal_cb, NULL);
+ eldbus_proxy_signal_handler_add(meta_geoclue, "StatusChanged", status_signal_cb, NULL);
+ eldbus_proxy_signal_handler_add(meta_velocity, "VelocityChanged", velocity_signal_cb, NULL);
+ eldbus_proxy_signal_handler_add(meta_nmea, "NmeaChanged", nmea_signal_cb, NULL);
+ eldbus_proxy_signal_handler_add(meta_satellite, "SatelliteChanged", satellite_signal_cb, NULL);
+
+ ecore_event_add(ELOCATION_EVENT_META_READY, NULL, NULL, NULL);
+}
+
+static void
+_name_owner_changed(void *data, const char *bus, const char *old, const char *new)
+{
+ if (old[0] == '\0' && new[0] != '\0')
+ {
+ ecore_event_add(ELOCATION_EVENT_IN, NULL, NULL, NULL);
+ unique_name = strdup(new);
+ }
+ else if (old[0] != '\0' && new[0] == '\0')
+ {
+ if (strcmp(unique_name, old) != 0)
+ WARN("%s was not the known name %s, ignored.", old, unique_name);
+ else
+ ecore_event_add(ELOCATION_EVENT_OUT, NULL, NULL, NULL);
+ }
+ else
+ {
+ DBG("unknow change from %s to %s", old, new);
+ }
+}
+
+/* Public API function to request a landmarks position based on an address object */
+EAPI Eina_Bool
+elocation_landmarks_get(Elocation_Position *position_shadow, Elocation_Address *address_shadow)
+{
+ Eldbus_Message *msg;
+ Eldbus_Message_Iter *iter;
+ const char *keyword = NULL, *lang = NULL, *country_code = NULL;
+ int limit = 0;
+ double left= 0.0, top = 0.0, right = 0.0, bottom = 0.0;
+
+ msg = eldbus_proxy_method_call_new(master_poi, "SearchByPosition");
+ iter = eldbus_message_iter_get(msg);
+ eldbus_message_iter_basic_append(iter, 's', keyword);
+ eldbus_message_iter_basic_append(iter, 's', lang);
+ eldbus_message_iter_basic_append(iter, 's', country_code);
+ eldbus_message_iter_basic_append(iter, 'i', limit);
+ eldbus_message_iter_basic_append(iter, 'd', left);
+ eldbus_message_iter_basic_append(iter, 'd', top);
+ eldbus_message_iter_basic_append(iter, 'd', right);
+ eldbus_message_iter_basic_append(iter, 'd', bottom);
+ if (!eldbus_proxy_send(master_poi, msg, poi_cb, NULL, -1))
+ {
+ ERR("Error: could not call SearchByPosition");
+ eldbus_message_unref(msg);
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+/* Public API function to get an address from a position */
+EAPI Eina_Bool
+elocation_position_to_address(Elocation_Position *position_shadow, Elocation_Address *address_shadow)
+{
+ Eldbus_Message *msg;
+ Eldbus_Message_Iter *iter, *structure;
+
+ msg = eldbus_proxy_method_call_new(geonames_rgeocode, "PositionToAddress");
+ iter = eldbus_message_iter_get(msg);
+ eldbus_message_iter_basic_append(iter, 'd', position_shadow->latitude);
+ eldbus_message_iter_basic_append(iter, 'd', position_shadow->longitude);
+ structure = eldbus_message_iter_container_new(iter, 'r', NULL);
+ eldbus_message_iter_basic_append(structure, 'i', position_shadow->accur->level);
+ eldbus_message_iter_basic_append(structure, 'd', position_shadow->accur->horizontal);
+ eldbus_message_iter_basic_append(structure, 'd', position_shadow->accur->vertical);
+ eldbus_message_iter_container_close(iter, structure);
+ if (!eldbus_proxy_send(geonames_rgeocode, msg, rgeocode_cb, NULL, -1))
+ {
+ ERR("Error: could not call PositionToAddress");
+ eldbus_message_unref(msg);
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+/* Public API function to get a position from and address */
+EAPI Eina_Bool
+elocation_address_to_position(Elocation_Address *address_shadow, Elocation_Position *position_shadow)
+{
+ Eldbus_Message *msg;
+ Eldbus_Message_Iter *iter, *array;
+
+ /* In function macro to generate a key value pair structure for the dict */
+ #define ENTRY(key) { #key, address_shadow->key }
+ struct keyval {
+ const char *key;
+ const char *val;
+ } keyval[] = {
+ ENTRY(country),
+ ENTRY(countrycode),
+ ENTRY(locality),
+ ENTRY(postalcode),
+ ENTRY(region),
+ ENTRY(timezone),
+ { NULL, NULL }
+ };
+ #undef ENTRY
+
+ struct keyval *k;
+
+ msg = eldbus_proxy_method_call_new(geonames_geocode, "AddressToPosition");
+ iter = eldbus_message_iter_get(msg);
+
+ array = eldbus_message_iter_container_new(iter, 'a', "{ss}");
+
+ for (k = keyval; k && k->key; k++)
+ {
+ Eldbus_Message_Iter *entry;
+
+ if (!k->val) continue;
+
+ entry = eldbus_message_iter_container_new(array, 'e', NULL);
+ eldbus_message_iter_arguments_append(entry, "ss", k->key, k->val);
+ eldbus_message_iter_container_close(array, entry);
+ }
+
+ eldbus_message_iter_container_close(iter, array);
+
+ if (!eldbus_proxy_send(geonames_geocode, msg, geocode_cb, NULL, -1))
+ {
+ ERR("Error: could not call AddressToPosition");
+ eldbus_message_unref(msg);
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+/* Public API function to get the position from a freeform text input style
+ * address
+ */
+EAPI Eina_Bool
+elocation_freeform_address_to_position(const char *freeform_address, Elocation_Position *position_shadow)
+{
+ if (!eldbus_proxy_call(geonames_geocode, "FreeformAddressToPosition", geocode_cb, NULL, -1, "s", freeform_address))
+ {
+ ERR("Error: could not call FreeformAddressToPosition");
+ return EINA_FALSE;
+ }
+ return EINA_TRUE;
+}
+
+/* Public API function to request the current address */
+EAPI Eina_Bool
+elocation_address_get(Elocation_Address *address_shadow)
+{
+ if (!address) return EINA_FALSE;
+
+ address_shadow = address;
+ return EINA_TRUE;
+}
+
+/* Public API function to request the current position */
+EAPI Eina_Bool
+elocation_position_get(Elocation_Position *position_shadow)
+{
+ if (!position) return EINA_FALSE;
+
+ position_shadow = position;
+ return EINA_TRUE;
+}
+
+/* Public API function to request the status */
+EAPI Eina_Bool
+elocation_status_get(int *status_shadow)
+{
+ if (status < 0) return EINA_FALSE;
+
+ status_shadow = status;
+ return EINA_TRUE;
+}
+
+/* Public API function to create a new position object */
+EAPI Elocation_Position *
+elocation_position_new(void)
+{
+ /* Malloc the global struct we operate on here in this lib. This shadows the
+ * updated data we are giving to the application */
+ position = calloc(1, sizeof(Elocation_Position));
+ if (!position) return NULL;
+
+ position->accur = calloc(1, sizeof(Elocation_Accuracy));
+ if (!position->accur) return NULL;
+
+ return position;
+}
+
+/* Public API function to create an new address object */
+EAPI Elocation_Address *
+elocation_address_new(void)
+{
+ /* Malloc the global struct we operate on here in this lib. This shadows the
+ * updated data we are giving to the application */
+ address = calloc(1, sizeof(Elocation_Address));
+ if (!address) return NULL;
+
+ address->accur = calloc(1, sizeof(Elocation_Accuracy));
+ if (!address->accur) return NULL;
+
+ return address;
+}
+
+/* Public API function to free an position object */
+EAPI void
+elocation_position_free(Elocation_Position *position_shadow)
+{
+ if (position != position_shadow)
+ {
+ ERR("Corrupted position object");
+ return;
+ }
+
+ free(position->accur);
+ free(position);
+}
+
+/* Public API function to free an address object */
+EAPI void
+elocation_address_free(Elocation_Address *address_shadow)
+{
+ if (address != address_shadow)
+ {
+ ERR("Corrupted address object");
+ return;
+ }
+
+ if (address)
+ {
+ free(address->country);
+ free(address->countrycode);
+ free(address->locality);
+ free(address->postalcode);
+ free(address->region);
+ free(address->timezone);
+ free(address->accur);
+ free(address);
+ }
+}
+
+/* Public API funtion to initialize the elocation library */
+EAPI Eina_Bool
+elocation_init(void)
+{
+ Eldbus_Object *obj_master = NULL;
+ Eldbus_Object *obj_geonames = NULL;
+
+ if (!eina_init()) return EINA_FALSE;
+ if (!ecore_init()) return EINA_FALSE;
+ if (!eldbus_init()) return EINA_FALSE;
+
+ _elocation_log_dom = eina_log_domain_register("elocation", EINA_COLOR_BLUE);
+ if (_elocation_log_dom < 0)
+ {
+ EINA_LOG_ERR("Could not register 'elocation' log domain.");
+ }
+
+ /* Create objects, one for each kind, we operate on internally */
+ address_provider = calloc(1, sizeof(Elocation_Provider));
+ position_provider = calloc(1, sizeof(Elocation_Provider));
+
+ addr_geocode = calloc(1, sizeof(Elocation_Address));
+ if (!addr_geocode) return EINA_FALSE;
+
+ addr_geocode->accur = calloc(1, sizeof(Elocation_Accuracy));
+ if (!addr_geocode->accur) return EINA_FALSE;
+
+ pos_geocode = calloc(1, sizeof(Elocation_Position));
+ if (!pos_geocode) return EINA_FALSE;
+
+ pos_geocode->accur = calloc(1, sizeof(Elocation_Accuracy));
+ if (!pos_geocode->accur) return EINA_FALSE;
+
+ conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION);
+ if (!conn)
+ {
+ ERR("Error: could not connect to session bus.");
+ return EXIT_FAILURE;
+ }
+
+ /* Create all ecore event types we send out to interested applications */
+ if (ELOCATION_EVENT_IN == 0)
+ ELOCATION_EVENT_IN = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_OUT == 0)
+ ELOCATION_EVENT_OUT = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_STATUS == 0)
+ ELOCATION_EVENT_STATUS = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_POSITION == 0)
+ ELOCATION_EVENT_POSITION = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_ADDRESS == 0)
+ ELOCATION_EVENT_ADDRESS = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_VELOCITY == 0)
+ ELOCATION_EVENT_VELOCITY = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_GEOCODE == 0)
+ ELOCATION_EVENT_GEOCODE = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_REVERSEGEOCODE == 0)
+ ELOCATION_EVENT_REVERSEGEOCODE = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_NMEA == 0)
+ ELOCATION_EVENT_NMEA = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_SATELLITE == 0)
+ ELOCATION_EVENT_SATELLITE = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_POI == 0)
+ ELOCATION_EVENT_POI = ecore_event_type_new();
+
+ if (ELOCATION_EVENT_META_READY == 0)
+ ELOCATION_EVENT_META_READY = ecore_event_type_new();
+
+ obj_master= eldbus_object_get(conn, GEOCLUE_DBUS_NAME, GEOCLUE_OBJECT_PATH);
+ if (!obj_master)
+ {
+ ERR("Error: could not get object");
+ return EXIT_FAILURE;
+ }
+
+ manager_master = eldbus_proxy_get(obj_master, GEOCLUE_MASTER_IFACE);
+ if (!manager_master)
+ {
+ ERR("Error: could not get proxy");
+ return EXIT_FAILURE;
+ }
+
+ /* Create a meta provider for all normal use cases. This will allow GeoClue
+ * to decide which provider is the best for us internally.
+ * Right now we don't have the functionality in place to specifically request
+ * a provider but we maybe need this in the future. We will try without it
+ * for now.
+ */
+ if (!eldbus_proxy_call(manager_master, "Create", create_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call Create");
+ return EXIT_FAILURE;
+ }
+
+ master_poi = eldbus_proxy_get(obj_master, GEOCLUE_POI_IFACE);
+ if (!master_poi)
+ {
+ ERR("Error: could not get proxy");
+ return EXIT_FAILURE;
+ }
+
+ /* Geocode and reverse geocode never show up as meta provider. Still we want
+ * to be able to convert so we keep them around directly here. */
+ obj_geonames= eldbus_object_get(conn, GEONAMES_DBUS_NAME, GEONAMES_OBJECT_PATH);
+ if (!obj_geonames)
+ {
+ ERR("Error: could not get object for geonames");
+ return EXIT_FAILURE;
+ }
+
+ geonames_geocode = eldbus_proxy_get(obj_geonames, GEOCLUE_GEOCODE_IFACE);
+ if (!geonames_geocode)
+ {
+ ERR("Error: could not get proxy");
+ return EXIT_FAILURE;
+ }
+
+ geonames_rgeocode = eldbus_proxy_get(obj_geonames, GEOCLUE_REVERSEGEOCODE_IFACE);
+ if (!geonames_rgeocode)
+ {
+ ERR("Error: could not get proxy");
+ return EXIT_FAILURE;
+ }
+
+ eldbus_name_owner_changed_callback_add(conn, GEOCLUE_DBUS_NAME, _name_owner_changed,
+ NULL, EINA_TRUE);
+
+ ecore_event_handler_add(ELOCATION_EVENT_IN, geoclue_start, NULL);
+ ecore_event_handler_add(ELOCATION_EVENT_OUT, geoclue_stop, NULL);
+
+ return EINA_TRUE;
+}
+
+/* Public API function to shutdown the elocation library form the application */
+EAPI void
+elocation_shutdown(void)
+{
+ /* Depending on if the create_cb was successfully received meta_geoclue is
+ * setup or not. So we * need to check here if this is not the case
+ */
+ if (meta_geoclue)
+ {
+ /* To allow geoclue freeing unused providers we free our reference on it here */
+ if (!eldbus_proxy_call(meta_geoclue, "RemoveReference", _reference_del_cb, NULL, -1, ""))
+ {
+ ERR("Error: could not call RemoveReference");
+ }
+ }
+
+ /* Quite a bit of allocated string and generic memory cleanup. This should be
+ *less when we went away from all this global var business.
+ */
+ if (address_provider)
+ {
+ free(address_provider->name);
+ free(address_provider->description);
+ free(address_provider->service);
+ free(address_provider->path);
+ free(address_provider);
+ }
+
+ if (position_provider)
+ {
+ free(position_provider->name);
+ free(position_provider->description);
+ free(position_provider->service);
+ free(position_provider->path);
+ free(position_provider);
+ }
+
+ if (pos_geocode)
+ {
+ free(pos_geocode->accur);
+ free(pos_geocode);
+ }
+
+ if (addr_geocode)
+ {
+ free(addr_geocode->country);
+ free(addr_geocode->countrycode);
+ free(addr_geocode->locality);
+ free(addr_geocode->postalcode);
+ free(addr_geocode->region);
+ free(addr_geocode->timezone);
+ free(addr_geocode->accur);
+ free(addr_geocode);
+ }
+
+ /* Unreference some eldbus strcutures we now longer use. To allow eldbus to
+ * free them internally.
+ */
+ if (manager_master)
+ eldbus_proxy_unref(manager_master);
+
+ eldbus_name_owner_changed_callback_del(conn, GEOCLUE_DBUS_NAME, _name_owner_changed, NULL);
+ eldbus_connection_unref(conn);
+ eldbus_shutdown();
+ ecore_shutdown();
+ eina_log_domain_unregister(_elocation_log_dom);
+ eina_shutdown();
+}