To get started with CrowdConnected positioning, you can either create your own implementation by using the MPPositionProvider interface from the MapsIndoors SDK to visualize position updates from CrowdConnected or you can use the MapsIndoors CrowdConnected Positioning Provider, which handles this work for you, requiring minimal setup. This guide shows how to make use of it.
This guide assumes you already have an app with MapsIndoors.
Download maven package
First, add the CrowdConnected maven url to your dependency resolution:
Before positioning can be enabled, you must ask the user for permissions. The required permissions are: ACCESS_FINE_LOCATION, BLUETOOTH_SCAN, and BLUETOOTH_CONNECT. Apps below API level 31 should not request BLUETOOTH_SCAN and BLUETOOTH_CONNECT as these were added in 31.
You can decide where to request these permissions, it should be naturally integrated into the flow of opening the map or when the user interacts with the app to enable positioning themselves.
To request the permissions, it is easiest to use ActivityCompat. In this example, we request the permissions in our MainActivity's onCreate method:
The positioning manager should only be initialized when positioning is required. It depends on the necessary permissions (mentioned above) and MapsIndoors being initialized.
MainActivity.kt
import android.util.Log
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import com.mapsindoors.positioning.crowdconnected.MPCrowdConnectedManager
import com.mapsindoors.positioning.crowdconnected.MPCrowdConnectedConfig
import net.crowdconnected.android.core.StatusCallback
class MainActivity : FragmentActivity() {
private var positionManager: MPCrowdConnectedManager? = null
fun startPositioning() {
positionManager = MPCrowdConnectedManager(
getApplication(),
MPCrowdConnectedConfig("your_app_key", "your_token", "your_secret"), // you should not input your secrets directly, fetch them from somewhere secure
object : StatusCallback {
override fun onStartUpSuccess() {
Log.i("IPS", "CrowdConnected started successfully")
runOnUiThread {
Toast.makeText(getApplicationContext(), "CrowdConnected started successfully", Toast.LENGTH_LONG).show()
}
}
override fun onRuntimeError(error: String?) {
Log.i("IPS", "CrowdConnected runtime error: $error")
runOnUiThread {
Toast.makeText(getApplicationContext(), "CrowdConnected runtime error: $error", Toast.LENGTH_LONG).show()
}
}
override fun onStartUpFailure(failure: String?) {
Log.i("IPS", "CrowdConnected startup failure: $failure")
runOnUiThread {
Toast.makeText(getApplicationContext(), "CrowdConnected startup failure: $failure", Toast.LENGTH_LONG).show()
}
}
}
)
MapsIndoors.setPositionProvider(positionManager!!)
}
}
With this, positioning is up and running, and can be tested out in your app. The only thing we are missing is lifecycle handling.
Show it on the map
The last step to get the positioning shown on the map is to enable it in MapControl. This is typically done when creating the MapControl, but it can be turned on at any time. In the following example we turn on positioning when creating our MapControl, by adding it to the MPMapConfig. The example is based on using Mapbox, but this can be done for any supported map.
MainActivity.kt
import android.os.Bundle
import androidx.fragment.app.FragmentActivity
import com.mapsindoors.core.MapControl
import com.mapsindoors.mapbox.MPMapConfig
import com.mapsindoors.core.errors.MIError
import com.mapbox.maps.MapView
import com.mapbox.maps.MapboxMap
class MainActivity : FragmentActivity() {
private var mMapControl: MapControl? = null
private var mMap: MapBoxMap? = null
private var mMapView: MapBoxMapView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// initialize Map
mMapView = requireActivity().findViewById(R.id.mapView)
mMap = mMapView.mapboxMap
// start building MapControl
initMapControl()
}
private fun initMapControl() {
val mapConfig = MPMapConfig.Builder(this, mMap ?: return, mMapView ?: return, "your_mapbox_api_key", true)
.setShowUserPosition(true) // setShowUserPosition to true to show the latest position given to MapsIndoors on the map
.build()
MapControl.create(mapConfig) { mapControl: MapControl?, miError: MIError? ->
if (miError == null) {
mMapControl = mapControl
}
}
}
}
Positioning can also be enabled or disabled on MapControl at any time by calling showUserPosition on MapControl:
MainActivity.kt
import androidx.fragment.app.FragmentActivity
import com.mapsindoors.core.MapControl
class MainActivity : FragmentActivity() {
private var mMapControl: MapControl? = null
private fun showPosition(show: Boolean) {
// positioning can be enabled at any time mapcontrol is accessible
mMapControl?.showUserPosition(show)
}
}
Lifecycle
It is important to ensure that your positioning implementation respects the Android Lifecycle. The position manager should be properly started, paused, and resumed according to the activity lifecycle to prevent memory leaks or unexpected behavior.
How you integrate lifecycle management may vary based on your app’s architecture (e.g., using LifecycleObservers, ViewModel, or manual lifecycle management). Make sure to consider your app’s structure and user experience when deciding how to manage the lifecycle of your position manager.
Example
Here's a basic example of how you can manage lifecycle manually:
MainActivity.kt
import androidx.fragment.app.FragmentActivity
import com.mapsindoors.positioning.crowdconnected.MPCrowdConnectedManager
class MainActivity : FragmentActivity() {
private var positionManager: MPCrowdConnectedManager? = null
override fun onStop() {
super.onStop()
positionManager?.stop()
}
override fun onResume() {
super.onResume()
positionManager?.restart()
}
}
Last updated
Was this helpful?
In the above example we initialized the positionManager using a set of CrowdConnected secrets that we got from their CMS, you can follow the CrowdConnected guide on how to generate them . It is optional to add a StatusCallback, in this example we added it for debug purposes, it will print to the log and create a Toast informing the status of initialization.