diff options
Diffstat (limited to 'navit/traffic/traff_android/traffic_traff_android.c')
-rw-r--r-- | navit/traffic/traff_android/traffic_traff_android.c | 179 |
1 files changed, 178 insertions, 1 deletions
diff --git a/navit/traffic/traff_android/traffic_traff_android.c b/navit/traffic/traff_android/traffic_traff_android.c index 266f51a0c..e0862ec5c 100644 --- a/navit/traffic/traff_android/traffic_traff_android.c +++ b/navit/traffic/traff_android/traffic_traff_android.c @@ -22,7 +22,7 @@ * * @brief The TraFF plugin for Android * - * This plugin receives TraFF feeds via Android broadcasts. + * This plugin receives TraFF feeds via Android broadcasts and content providers. */ #include <string.h> @@ -36,13 +36,31 @@ #include "item.h" #include "attr.h" #include "coord.h" +#include "map.h" +#include "route_protected.h" +#include "route.h" +#include "transform.h" #include "xmlconfig.h" #include "android.h" #include "traffic.h" #include "plugin.h" #include "callback.h" +#include "vehicle.h" #include "debug.h" #include "navit.h" +#include "util.h" + +/** + * @brief Minimum area around the current position for which to retrieve traffic updates. + * + * 100000 is equivalent to around 50 km on each side of the current position. The actual subscription area + * can be larger, allowing for a subscription area to be kept over multiple position updates. + * + * The actual subscription area around the current location is stored in + * {@link struct traffic_priv::position_rect} and updated in + * {@link traffic_traff_android_position_callback(struct traffic_priv *, struct navit *, struct vehicle *)}. + */ +#define POSITION_RECT_SIZE 100000 /** * @brief Stores information about the plugin instance. @@ -50,13 +68,38 @@ struct traffic_priv { struct navit * nav; /**< The navit instance */ struct callback * cbid; /**< The callback function for TraFF feeds **/ + int position_valid; /**< Whether Navit currently has a valid position */ + struct coord_rect * position_rect; /**< Rectangle around last known vehicle position (in `projection_mg`) */ + struct map_selection * route_map_sel; /**< Map selection for the current route */ jclass NavitTraffClass; /**< The `NavitTraff` class */ jobject NavitTraff; /**< An instance of `NavitTraff` */ }; +void traffic_traff_android_destroy(struct traffic_priv * this_); struct traffic_message ** traffic_traff_android_get_messages(struct traffic_priv * this_); /** + * @brief Destructor. + */ +void traffic_traff_android_destroy(struct traffic_priv * this_) { + jmethodID cid; + + cid = (*jnienv)->GetMethodID(jnienv, this_->NavitTraffClass, "close", "()V"); + if (cid == NULL) { + dbg(lvl_error,"no method found"); + return; /* exception thrown */ + } + (*jnienv)->CallVoidMethod(jnienv, this_->NavitTraff, cid); + + if (this_->position_rect) + g_free(this_->position_rect); + this_->position_rect = NULL; + if (this_->route_map_sel) + route_free_selection(this_->route_map_sel); + this_->route_map_sel = NULL; +} + +/** * @brief Returns an empty traffic report. * * @return Always `NULL` @@ -70,6 +113,7 @@ struct traffic_message ** traffic_traff_android_get_messages(struct traffic_priv */ static struct traffic_methods traffic_traff_android_meth = { traffic_traff_android_get_messages, + traffic_traff_android_destroy, }; @@ -109,12 +153,133 @@ static void traffic_traff_android_on_feed_received(struct traffic_priv * this_, /** + * @brief Sets the route map selection + * + * @param this_ The instance which will handle the selection update + */ +static void traffic_traff_android_set_selection(struct traffic_priv * this_) { + struct route * route; + struct coord_geo lu, rl; + gchar *filter_list; + jstring j_filter_list; + gchar *min_road_class; + jmethodID cid; + + if (this_->route_map_sel) + route_free_selection(this_->route_map_sel); + this_->route_map_sel = NULL; + if (navit_get_destination_count(this_->nav) && (route = (navit_get_route(this_->nav)))) + this_->route_map_sel = route_get_selection(route); + + /* start building the filter list */ + filter_list = g_strconcat_printf(NULL, "<filter_list>\n"); + if (this_->position_rect) { + transform_to_geo(projection_mg, &this_->position_rect->lu, &lu); + transform_to_geo(projection_mg, &this_->position_rect->rl, &rl); + filter_list = g_strconcat_printf(filter_list, " <filter bbox=\"%.5f %.5f %.5f %.5f\"/>\n", + rl.lat, lu.lng, lu.lat, rl.lng); + } + for (struct map_selection * sel = this_->route_map_sel; sel; sel = sel->next) { + transform_to_geo(projection_mg, &sel->u.c_rect.lu, &lu); + transform_to_geo(projection_mg, &sel->u.c_rect.rl, &rl); + min_road_class = order_to_min_road_class(sel->order); + if (!min_road_class) + filter_list = g_strconcat_printf(filter_list, " <filter bbox=\"%.5f %.5f %.5f %.5f\"/>\n", + rl.lat, lu.lng, lu.lat, rl.lng); + else + filter_list = g_strconcat_printf(filter_list, " <filter min_road_class=\"%s\" bbox=\"%.5f %.5f %.5f %.5f\"/>\n", + min_road_class, rl.lat, lu.lng, lu.lat, rl.lng); + } + /* the trailing \0 is required for NewStringUTF */ + filter_list = g_strconcat_printf(filter_list, "</filter_list>\0"); + j_filter_list = (*jnienv)->NewStringUTF(jnienv, filter_list); + cid = (*jnienv)->GetMethodID(jnienv, this_->NavitTraffClass, "onFilterUpdate", "(Ljava/lang/String;)V"); + if (cid) + (*jnienv)->CallVoidMethod(jnienv, this_->NavitTraff, cid, j_filter_list); + g_free(filter_list); +} + + +/** + * @brief Callback for destination changes + * + * @param this_ The instance which will handle the destination update + */ +static void traffic_traff_android_destination_callback(struct traffic_priv * this_) { + traffic_traff_android_set_selection(this_); +} + + +/** + * @brief Callback for navigation status changes + * + * This callback is necessary to force an update of existing subscriptions when Navit acquires a new + * position (after not having had valid position information), as the map selection will change when + * the current position becomes known for the first time. + * + * @param this_ The instance which will handle the navigation status update + * @param status The status of the navigation engine (the value of the {@code nav_status} attribute) + */ +static void traffic_traff_android_status_callback(struct traffic_priv * this_, int status) { + int new_position_valid = (status != 1); + if (new_position_valid && !this_->position_valid) { + this_->position_valid = new_position_valid; + traffic_traff_android_set_selection(this_); + } else if (new_position_valid != this_->position_valid) + this_->position_valid = new_position_valid; +} + + +/** + * @brief Callback for position changes + * + * This updates {@link struct traffic_priv::position_rect} if the vehicle has moved far enough from its + * center to be within {@link POSITION_RECT_SIZE} of one of its boundaries. The new rectangle is created + * with twice that amount of padding, allowing the vehicle to move for at least that distance before the + * subscription needs to be updated again. + * + * @param this_ The instance which will handle the position update + * @param navit The Navit instance + * @param vehicle The vehicle which delivered the position update and from which the position can be queried + */ +static void traffic_traff_android_position_callback(struct traffic_priv * this_, struct navit *navit, + struct vehicle *vehicle) { + struct attr attr; + struct coord c; + struct coord_rect cr; + jmethodID cid; + if (!vehicle_get_attr(vehicle, attr_position_coord_geo, &attr, NULL)) + return; + transform_from_geo(projection_mg, attr.u.coord_geo, &c); + cr.lu = c; + cr.rl = c; + cr.lu.x -= POSITION_RECT_SIZE; + cr.rl.x += POSITION_RECT_SIZE; + cr.lu.y += POSITION_RECT_SIZE; + cr.rl.y -= POSITION_RECT_SIZE; + if (!this_->position_rect) + this_->position_rect = g_new0(struct coord_rect, 1); + if (!coord_rect_contains(this_->position_rect, &cr.lu) || !coord_rect_contains(this_->position_rect, &cr.rl)) { + cr.lu.x -= POSITION_RECT_SIZE; + cr.rl.x += POSITION_RECT_SIZE; + cr.lu.y += POSITION_RECT_SIZE; + cr.rl.y -= POSITION_RECT_SIZE; + *(this_->position_rect) = cr; + traffic_traff_android_set_selection(this_); + } +} + + +/** * @brief Initializes a traff_android plugin * * @return True on success, false on failure */ static int traffic_traff_android_init(struct traffic_priv * this_) { jmethodID cid; + struct route * route; + struct attr attr; + struct navigation * navigation; if (!android_find_class_global("org/navitproject/navit/NavitTraff", &this_->NavitTraffClass)) return 0; @@ -131,6 +296,15 @@ static int traffic_traff_android_init(struct traffic_priv * this_) { if (this_->NavitTraff) this_->NavitTraff = (*jnienv)->NewGlobalRef(jnienv, this_->NavitTraff); + /* register callbacks for position and destination changes */ + navit_add_callback(this_->nav, callback_new_attr_1(callback_cast(traffic_traff_android_position_callback), + attr_position_coord_geo, this_)); + navit_add_callback(this_->nav, callback_new_attr_1(callback_cast(traffic_traff_android_destination_callback), + attr_destination, this_)); + if ((navigation = navit_get_navigation(this_->nav))) + navigation_register_callback(navigation, attr_nav_status, + callback_new_attr_1(callback_cast(traffic_traff_android_status_callback), attr_nav_status, this_)); + return 1; } @@ -154,6 +328,9 @@ static struct traffic_priv * traffic_traff_android_new(struct navit *nav, struct ret = g_new0(struct traffic_priv, 1); ret->nav = nav; ret->cbid = callback_new_1(callback_cast(traffic_traff_android_on_feed_received), ret); + ret->position_valid = 0; + ret->position_rect = NULL; + ret->route_map_sel = NULL; /* TODO populate members, if any */ *meth = traffic_traff_android_meth; |