Changelog
5.1.12
- add fallback experimental support ## 5.1.11
- improved offline route algorithm
- tweak logic for guidance ## 5.1.9, 5.1.10
- updates to support Android S
5.1.7, 5.1.8
- transitional beacon snapping override
- increased transitional input logic walking speed
5.1.5, 5.1.6
- database migration fixes
- experimental Route constructor to create and start routes calculated by OSRM from application level,
- fix UnitCoversion crash for exotic languages,
5.1.3
- changed behaviour on token change, will now automatically clean database in this case; previously SDK expected a logout method to be called before setting new token and threw error if this was not met
- various minor crash/bugfixes
- fixed few mixed up finnish and english translations,
5.1.1
- OSRM routing fixes for older devices
- heading correction crash fix for devices without compass
- improved static instructions for route (now a list of strings, should contain main and secondary instruction, if available)
5.1.0
Outside navigation using OSRM
Navigation can now route outside of defined places and paths, leveraging OSRM. Navigating outside requires active internet connection. With this change, areas called "Campus" were added (managed via portal https://portal.proximi.fi/#/locations/campuses). Campuses define domains where local routing (Wayfinding) is to be used. Defining campuses is required for proper functionality. Note that if you did not set up any campus, the SDK will only use indoor Wayfinding.
If using outside navigation, please contact us to check for supported areas (contries).
IMPORTANT: With this version, the library repository was renamed from io.proximi.proximiiolibrary:mapbox
to io.proximi.library:mapbox
. You need to only update
the package import in your gradle file.
Waypoints
In addition to outside routing, navigation can now be requested to route via Waypoints. Two kinds of waypoints were introduced: - SimpleWaypoint: a waypoint the route has to route through unconditionally - VariableWaypoint: list of waypoints, from which the one with shortest route is selected.
Navigation methods
In conjunctions with other changes in this release, the methods to request navigation (route) were changed. SDK provides three methods: - routeFind - attempts to find a route according to parameters, returns route object in a callback, - routeFindAndPreview - additionally, the route is previewed on map, - routeFindAndStart - finds and returns route via callback, shows it on map, and initiates navigation immediatelly.
All methods accept the same parameters, that is two objects:
- RouteConfiguration
- object describing all the requirements of the route.
- RouteCallback
- callback object (receives route and other navigation events).
5.0.6
- Route simulation feature - map will simulate position updates along a navigated route.
5.0.5
- Modified behaviour to properly allow authentication token change (added
ProximiioMapbox.logout()
method). - Tweaked behaviour and documentation to better handle and explain android component (activity) lifecycle.
5.0.4
- Updated navigation guidance text when switching floors for more clarity.
- Fixed issue with wayfinding not working with proguard.
5.0.2
- Added support for directional level changers (escalators).
5.0.1
- Wayfinding crash bugfix.
5.0.0
- Unifying versioning between libs to corresponding to API version.
Introduction
Welcome to the Android ProximiioMapbox helper API reference guide. You can use this library to easily hook up Proximi.io with a MapLibre map in your Android application. Note this library is standalone from the Proximi.io Location SDK. To learn how to set up the location library, go here.
Features include:
- Displaying floor image and / or GeoJson Features (defined in Proximii.io map editor).
- Current location marker.
- Guided navigation.
- Floor changing.
- Feature search.
You can find the Android SDK reference here. You should understand the basics before using this library.
Code samples can be found on the right side of the page.
To create and manage your map data, we have created a brand new map editor available here.
Prerequisites
Proximi.io Android SDK
- Use Proximi.io Android SDK version of 2.8.20+. If you are using an older version, please upgrade before proceeding.
Multidex
This SDK requires the use of multidexing.
Please set up your application to use multidex, as guided here.
Adding the SDK
repositories {
...
maven {
url "https://maven.proximi.io/repository/android-releases/"
}
}
Add the Proximi.io repository to either your module's or project's build.gradle
.
In your app module's build.gradle
file, add a library dependency and set Java compatibility to 1.8.
Due to an issue with JS engine library, please include the library in your top-level (app) module as shown in sample.
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
...
// Add SDK, exclude JS engine library (due to multidex issue when included by the library)
implementation ('io.proximi.library:mapbox:5.1.9') { exclude group: 'com.eclipsesource.j2v8' }
// Include JS engine manually
implementation ('com.eclipsesource.j2v8:j2v8:6.1.0@aar')
}
Replace the dependency version number with the latest published version available here:
Getting Started
Seting up libraries
Dependency for Mapbox is included in this library. However, Mapbox map view must be set up in the application.
1) Use map view in your activity layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
...
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_height="match_parent"
android:layout_width="match_parent"/>
...
</LinearLayout>
2) Initialize Mapbox and Proximiio libraries
const val PROXIMIIO_AUTH_TOKEN = "[your authentication token]"
class MainActivity : AppCompatActivity() {
// Proximiio mapbox helper instance
private lateinit var proximiioMapbox: ProximiioMapbox
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Init Mapbox
Mapbox.getInstance(this, PROXIMIIO_AUTH_TOKEN)
setContentView(R.layout.activity_main)
// Init ProximiioMapbox helper
proximiioMapbox = ProximiioMapbox.getInstance(baseContext, PROXIMIIO_AUTH_TOKEN, NULL)
// Mapbox Mapview onCreate callback; note mapview corresponds to the MapView we added to layout in the section above
mapView.onCreate(savedInstanceState)
// Get map and pass it to ProximiioMapbox helper
mapView.getMapAsync { mapboxMap ->
proximiioMapbox.onMapReady(mapboxMap)
}
}
override fun onStart() {
super.onStart()
mapView.onStart()
proximiioMapbox.onStart()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
mapView.onPause()
super.onPause()
}
override fun onStop() {
proximiioMapbox.onStop()
mapView.onStop()
super.onStop()
}
override fun onDestroy() {
proximiioMapbox.onDestroy()
mapView.onDestroy()
super.onDestroy()
}
override fun onLowMemory() {
mapView.onLowMemory()
super.onLowMemory()
}
override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
super.onSaveInstanceState(outState, outPersistentState)
mapView.onSaveInstanceState(outState)
}
fun onLogoutClicked() {
proximiioMapbox.logout()
finish()
}
}
- During mapbox initialization you can simply pass null instead of an access token.
- For ProximiioMapbox helper, be sure to pass your Proximi.io application token to the library.
- Make sure to pass all the necessary lifecycle callbacks to the libraries.
getInstance()
returns instance of the library, internally, the object is singleton.onStart()
resumes active processes of the library (updates of data, map)onStop()
informs the library UI is suspended, stops data synchronization.onDestroy()
stops most of activity of the library (guidance during active navigation is still working).logout()
additional callback if you wish to clear data and/or change the authentication token on next start.
At this point, running the application should display the map properly.
Using ProximiioMapbox Helper
Managing user's location on map
val apiListener = object: ProximiioListener() {
/**
* 1) Update current user level (to keep current location updated for navigation)
* 2) Update display level (will show / hide geojson based on its level)
*/
override fun changedFloor(floor: ProximiioFloor?) {
proximiioMapbox.updateUserLevel(floor?.floorNumber ?: 0)
proximiioMapbox.updateDisplayLevel(floor?.floorNumber ?: 0)
}
/**
* Update user's current location to:
* 1) to keep current location updated for navigation
* 2) to move user's location marker on map
*/
override fun position(location: Location) {
proximiioMapbox.updateUserLocation(it)
}
}
As mentioned above, Proximi.io Location SDK is a standalone library. To update user's location on map, location updates must be passed to the ProximiioMapbox helper.
The updateUserLocation(location: Location): Location
method processes the location internally and returns new (altered) Location object. The returned location object:
- is used to update user's location on map,
- has location altered by snapping to current route during navigation,
- has bearing set based on current route part. (Can be useful when setting Mapbox's render and / or camera mode to GPS, so it will follow route direction instead of compass).
Accessing data
PoI(Feature) search
You can search for features using the search
methods.
The simpler search(String filterInput)
method will return list of features with title containing the input string.
Alternatively, you can use search(ProximiioSearchFilter filter, String ...filterInputs)
to pass a custom ProximiioSearchFilter object and desired number of inputs to create custom search experiences.
See below for the ProximiioSearchFilter class documentation.
Raw data access
You can also access raw data using the following methods:
LiveData<List<AmenityCategory>> getAmenityCategories()
- get list of amenity categoriesLiveData<List<Amenity>> getAmenities()
- get list of amenitiesLiveData<List<Feature>> getFeatures()
- get list of featuresLiveData<String> getStyle()
- get mapbox style in string form (might be null before first data is downloaded)LiveData<SyncStatus> getSyncStatus()
- get current sync status
Note that these methods return the Android's LiveData object to enable observation of data updates.
Navigation
The ProximiioMapbox helper is capable of providing full guidance experience to the user. That is:
- finding a route,
- displaying a route and user's progress on map,
- providing step by step guidance,
- voice guidance,
- and other features.
Starting and managing navigation
There are three methods available to initiate routing:
- routeFind
- attempts to find a route according to parameters, returns route object in a callback,
- routeFindAndPreview
- additionally, the route is previewed on map,
- routeFindAndStart
- finds and returns route via callback, shows it on map, and initiates navigation immediatelly.
All methods accept the same parameters, that is two objects:
- RouteConfiguration
- object describing all the requirements of the route.
- RouteCallback
- callback object (receives route and other navigation events).
To start navigation, there are several methods available. Generally, the method you should use is routeFind(@NotNull String poiId, @NotNull RouteOptions options, boolean previewRoute, boolean startRoute, RouteCallback routeCallback)
. This method allows you to find a route:
- from user's current location to the feature identified by poiId
- with accessibility options set by options parameter (e.g.: avoid stairs), see
RouteOptions
docs for more info, - can preview route on map when previewRoute is set to true (will not start navigation itself),
- immediately start navigation when startRoute is set to true,
- return route and navigation updates in a callback.
- automatically read out directions using TTS.
- will include destination metadata in TTS.
The RouteCallback
object give you access to current status of navigation (gives you access to information about the route and current state of navigation). For more info see the docs for RouteCallback
.
There are several other convenience methods that:
- automatically start or preview the route:
routeFindAndStart(@NotNull Location to, int toLevel, String toTitle, RouteOptions options, RouteCallback routeCallback)
androuteFindAndPreview(@NotNull Location to, int toLevel, String toTitle, RouteOptions options, RouteCallback routeCallback)
- allow you to specify custom destination:
routeFind(@NotNull Location to, int toLevel, String toTitle, @NotNull RouteOptions options, boolean previewRoute, boolean startRoute, RouteCallback routeCallback)
- allow you to specify custom start location:
routeFind(@NotNull Location from, int fromLevel, @NotNull Location to, int toLevel, String toTitle, @NotNull RouteOptions options, boolean previewRoute, boolean startRoute, RouteCallback routeCallback)
Note that calling the routeFind(...)
method repeatedly will cancel the current route (only the last route is kept).
When route was calculated, you can manage the navigation state using following methods:
routePreview()
: preview the route on maprouteStart()
: start the navigationrouteCancel()
: stop navigation, removes the path from map.
During navigation, you can register several callbacks:
landmarksCallback(NavigationInterface.LandmarkCallback callback)
: will notify when entered or left range of a landmark feature (features of following types are included: landmark, poi, elevator, escalator, staircase as long as their range was set)hazardCallback(@Nullable NavigationInterface.HazardCallback callback)
: will notify when entered or left range of a hazard featuresegmentCallback(NavigationInterface.SegmentCallback callback)
: will notify when entered or left a segment (defined as polygon in editor)decisionCallback(@Nullable NavigationInterface.DecisionCallback callback)
: will notify when entered or left range of a landmark decision feature
In case you need to just calculate a 'throwaway' route, you can use the alternative routeCalculate(..)
method.
Keep in mind you can not start navigation on route calculated by this method but this calculation will not interfere with the currenly running navigation.
Configuration
The ProximiioMapbox helper provides a broad configuration options to customize your navigation experience.
You can configure TTS with following options:
- Enable or disable TTS using
ttsEnable(@NotNull TextToSpeech tts)
(you must provide TTS engine) andttsDisable()
- Enable heading correction warnings using
ttsHeadingCorrectionEnabled(boolean enabled)
. This will enable two spoken warnings - tell user starting orientation of route, and when the user is walking the wrong way. - Enable reassurance instructions (meaning TTS will speak even if there is not a direction change to keep user confidence about current direction) using
ttsReassuranceInstructionEnabled(boolean enabled)
and configure distance between reassurance updates withttsReassuranceInstructionDistance(double distanceInMeters)
- Repeat last instruction using
ttsRepeatLastInstruction()
- Toggle spoken information about:
- entering hazards -
ttsHazardAlert(boolean enabled, @NotNull List<Integer> metadataKeys)
orttsHazardAlert(boolean enabled)
(note: this method will not read any metadata) - entering segments -
ttsSegmentAlert(boolean enterEnabled, boolean exitEnabled, @NotNull List<Integer> metadataKeys)
orttsSegmentAlert(boolean enterEnabled, boolean exitEnabled)
(note: this method will not read any metadata) - entering decision points
ttsDecisionAlert(boolean enabled, @NotNull List<Integer> metadataKeys)
orttsDecisionAlert(boolean enabled)
(note: this method will not read any metadata) - entering landmark range -
ttsLandmarkAlert(boolean enabled, @NotNull List<Integer> metadataKeys)
orttsLandmarkAlert(boolean enabled)
(note: this method will not read any metadata) - additional information about level changers when the user should change floor:
ttsLevelChangerMetadataKeys(@NotNull List<Integer> metadataKeys)
- additional information about destination when the user reaches end of the route:
ttsDestinationMetadataKeys(@NonNull List<Integer> metadataKeys)
- entering hazards -
Navigation - you can customize the navigation experience using the following settings:
setUnitConversion(@NotNull String unitName, double conversionCoefficient)
: configure unit that should be used for guidance (please make sure you have defined this unit in guidance translations in editor)setStepImmediateThreshold(double thresholdInMeters)
: set distance before a change in direction when the instruction should be considered 'immediate',setStepPreparationThreshold(double thresholdInMeters)
: set distance before a change in direction when the instruction should be considered comming 'soon' and possibly warn the user about upcoming event,setRouteFinishThreshold(double thresholdInMeters)
: set a threshold which regulates how far from a destination user must be to be considered he arrived,setRerouteEnabled(Boolean enabled)
: enable rerouting if the user strays from path,setRerouteThreshold(double thresholdInMeters)
: configure threshold when the user is considered strayed from path,setUserLocationToRouteSnappingEnabled(boolean enabled)
: toggle snapping the user's location on map to the current routesetUserLocationToRouteSnappingThreshold(double thresholdInMeters)
: user's location will snap to route if distance to route is within this threshold,setRoutePathFixDistance(Double distanceInMeters)
: when distance of a start / destination point to any of the defined routes is higher than this threshold, the missing path will be automatically added.
You can also customize the current location marker (mapbox's location component) by supplying a MapboxLocationComponentActivator
object during map initialization with onMapReady(MapboxMap mapboxMap, MapboxLocationComponentActivator activator)
alternate method. Overriding its method allows you full ability to change mapbox's location component to your desired configuration.
Custom Markers
In order to add custom markers on the map we can refer to MapLibre legacy logic. In our demo application you can find a full implementation that makes use of LiveData logic to be reactive.
Another example can be found in MapLibre demo app.
Here a little snippet you can use:
mapboxMap.getStyle { style ->
style.addImage("marker-icon-id",
BitmapFactory.decodeResource(
this.getResources(), R.drawable.iss))
val source = GeoJsonSource("marker-source-id").apply { style.addSource(this) }
style.addLayer(SymbolLayer("marker-layer-id", "marker-source-id").withProperties(
iconImage("marker-icon-id"),
iconIgnorePlacement(true),
iconAllowOverlap(true),
iconSize(1.0f)
))
source.setGeoJson(FeatureCollection.fromFeatures(listOf(com.mapbox.geojson.Feature.fromGeometry(
Point.fromLngLat(24.921695923476054, 60.1671950369849)
))))
}
If you want to interecpt a tap on a marker, we need to introduce a check in our addOnMapClickListener
.
An example of handler can be like this:
mapboxMap.queryRenderedFeatures(
mapboxMap.projection.toScreenLocation(point),
"layer.${MARKER_ID}"
)
.map { poi -> viewModel.markersLiveData.value!!.firstOrNull { poi.id() == it.id() } }
.firstOrNull()?.let { feature ->
// handle here tap on marker
Log.d("MARKER", feature.toString())
} != null
If you need to manage also data a good advice is to create a data class
with feature as parameter, an identifier to be able to easily reconsolidate the data on tap as above and your extra data needed.
A detailed example is available in the demo app.
Documentation
See documentation here.