## version-5.0 ### API Reference # API Reference ## Latest Version Mappedin SDK for Android v5.9.0 ## Previous Versions v5.8.0v5.7.2v5.7.1v5.7.0v5.6.0v5.5.0v5.4.1v5.4.0v5.3.0v5.2.5v5.2.4v5.2.3v5.2.2v5.2.1v5.2.0v5.1.0v5.0.4v5.0.3v5.0.0v5.0.0-beta.7v5.0.0-beta.0 ### Blue Dot # Blue Dot > Displaying user position inside a building is not accurate with just GPS. However, there are a multitude of Indoor Positioning Systems utilizing different technological approaches to providing an accurate and reliable positioning inside. Mappedin works with any IPS capable of providing the SDK with latitude, longitude and floor level information. > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: BlueDot.kt ## Enabling Blue Dot You can initialize and enable the Blue Dot when the map first loads by using the `onFirstMapLoaded`-function implemented by `MPIMapViewDelegate`. The `MPIBlueDotManager` can be easily enabled with default settings by calling `mapView.blueDotManager.enable(MPIOptions.BlueDot())`. There are two useful events to listen to: position update and state change. For debugging purposes, you could add print statements to the following functions. Position updates could be useful for reacting to a user entering or leaving a certain radius of a store. Status updates help handle situations where the position is no longer available. ```kotlin override fun onFirstMapLoaded() { mapView.blueDotManager.enable(MPIOptions.BlueDot()) } override fun onBlueDotPositionUpdate(update: MPIBlueDotPositionUpdate) { Log.d("Position", update.position.toString()) } override fun onBlueDotStateChange(stateChange: MPIBlueDotStateChange) { Log.d("Name", stateChange.name.toString()) Log.d("Reason", stateChange.reason.toString()) } ``` ## Simulating Blue Dot To display a Blue Dot with the SDK, you'll need the position information in the form of `MPICoordinates`>). The following is a point in the fictional Mappedin Demo Mall with an accuracy of 2 meter radius on the floor level `0`. ```kotlin val coords = MPIPosition.MPICoordinates(43.52012478635707, -80.53951744629536, 2.0, 0) ``` Using our sample coordinate, we can draw the Blue Dot on the map by creating an `MPIPosition` and passing it to `blueDotManager.updatePosition()`. ```kotlin override fun onFirstMapLoaded() { mapView.blueDotManager.enable(MPIOptions.BlueDot()) val coords = MPIPosition.MPICoordinates(43.52012478635707, -80.53951744629536, 2.0, 0) val position = MPIPosition(1.0, coords) mapView.blueDotManager.updatePosition(position) } ``` !Android Blue Dot ## Blue Dot Display States - When the Blue Dot position given to Mappedin JS has a high accuracy (within a few meters) and is on the floor currently displayed, it is displayed as bright blue. !Blue Dot - Accurate - If the Mappedin Blue Dot is able to detect that a given position update is inaccurate, a semi-transparent blue shadow is displayed underneath the Blue Dot to indicate uncertainty range. !Bluedot Inaccurate - When the BlueDot is on another floor it will look semi-transparent. !Blue Dot - Accurate on another floor - The uncertainty range is shown also when displaying the Blue Dot on another floor than the currently viewed one. !Blue Dot Inaccurate on another floor - 30 seconds after the latest position update, the BlueDot is displayed as greyed out as it's possible that the user has moved but no updates have been received. !Blue Dot - Outdated ## Wayfinding From Blue Dot A common map feature is to provide a user with directions from their current location. Mappedin JS is able to provide the nearest node to the user’s location, which can be used as the starting point for wayfinding. Pass the user’s destination to the `getDirections` method to create a directions object that can be used to draw the path. ```kotlin override fun onBlueDotPositionUpdate(update: MPIBlueDotPositionUpdate) { val departure = update.nearestNode val destination = mapView.venueData?.locations?.first { it.name == "Destination Name" } if (departure == null || destination == null) return mapView.getDirections(to = destination, from = departure) { if (it != null) { mapView.journeyManager.draw(directions = it) } } } ``` ## Camera Follow Mode In some instances, a user may pan the camera away and lose track of their own Blue Dot position. An app may want to snap the camera to the user's location to reorient themselves. While this could also be done using camera controls, an app can also leverage the map STATE to pin the camera to the Blue Dot's location. By default, the map starts in EXPLORE mode allowing the user to freely control the camera position. To pin the camera to the Blue Dot, set the camera state to FOLLOW with Blue Dot enabled. ```kotlin mapView.blueDotManager.enable(options = MPIOptions.BlueDot(smoothing = true)) mapView.blueDotManager.setState(MPIState.FOLLOW) ``` The camera will animate to the Blue Dot position whenever it is updated, however, it doesn't zoom or rotate. When providing wayfinding directions, it may be beneficial to orient the camera along the route. Enable this by setting useRotationMode of MPIOptions.BlueDot to true to when calling MPIBlueDotManager.enable(). > Rotation mode only works when using the MPIJourneyManager. The camera will not rotate with a path created using MPIPathManager. You can read more about path differences in the Wayfinding guide. ```kotlin mapView.blueDotManager.enable( options = MPIOptions.BlueDot(smoothing = true, useRotationMode = true)) mapView.blueDotManager.setState(MPIState.FOLLOW) ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: BlueDot.kt ### Building & Level Selection # Building & Level Selection > This guide demonstrates how to populate Spinner widgets with Mappedin venue data to create a building and level selector. This will allow listing of all buildings, the levels of each building and the ability to switch between them. !Building & Level Selection - Android v5 > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: LevelSelector.kt The code below populates the spinners with the names of MPIMapGroups (buildings). This code can be called after the `MPIMapViewListener.onDataLoaded`>) method has been called, which indicates the venue data has been loaded and is ready to be used. ```kotlin val controlLayout = findViewById(R.id.controlsLinearLayout) val buildingSpinner = Spinner(this) buildingSpinner.setPadding(12,16,12,16) controlLayout.addView(buildingSpinner) val mapGroupNames = mapView.venueData?.mapGroups?.map { it.name } mapGroupNames?.let { buildingSpinner.adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, it) } ``` The contents of the mapNames spinner will change based on which map group is selected. An OnItemSelectedListener is used to listen for that event. The following code implements OnItemSelectedListener and populates the map spinner with the MPIMap names (levels) within the map group (buildings). The onItemSelected method will also fire when the buildingSpinner is first populated, triggering the mapNames spinner to also be populated. ```kotlin buildingSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onNothingSelected(parent: AdapterView<*>?) {} // When a new map group (building) is selected, update the level // spinner with the list of maps for that building. override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { val selectedMapGroupName = parent?.getItemAtPosition(position).toString() val mapNames = mapView.venueData?.maps?.filter { it.group?.name.contentEquals(selectedMapGroupName) }?.map { it.name } mapNames?.let { mapSpinner.adapter = parent?.context?.let { it1 -> ArrayAdapter( it1, android.R.layout.simple_spinner_item, it, ) } } } } ``` The next event to monitor is the user selecting a new map from the mapNames spinner, which should change the displayed map. An MPIMap with matching name is retrieved from the `venueData`%20:%20WebView,%20MPIMapViewInterface#-838081766%2FProperties%2F292400059>) of the MPIMapView. The MPIMap is then passed to the `MPIMapView.setMap`%20-%3E%20Unit?)>) method and is displayed to the user. ```kotlin mapSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onItemSelected( parent: AdapterView<*>?, view: View?, position: Int, id: Long, ) { parent?.getItemAtPosition(position)?.let { mapName -> mapView.venueData?.maps?.first { it.name == mapName }?.let { map -> mapView.setMap(map) { err -> err?.message?.let { message -> Log.e("setMap", message) } } } } } } ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: LevelSelector.kt ### Camera Controls # Camera Controls > To create rich experiences on top of the Mappedin SDK for Android, it's useful to be able to control the map view programmatically. This guide shows how to focus the map view on targets, how to listen to camera events and how to control the camera. > A complete class that uses the code snippet in this guide can be found in the Mappedin Android Github repo: CameraControls.kt ## Focus the Camera on Targets To focus on a polygon after it has been touched, we use the `onPolygonClicked()` function in the `MPIMapViewListener`. In this case, we are targeting only the touched polygon. However, we can also give coordinates or nodes as MPIOptions.CameraTargets>). ```kotlin override fun onPolygonClicked(polygon: MPINavigatable.MPIPolygon) { mapView?.cameraManager?.focusOn(MPIOptions.CameraTargets(polygons = listOf(polygon))) } ``` ## Listening to Camera Events We can use the `onCameraChanged()` function in the `MPICameraListener`>) to monitor changes to the camera position, rotation, tilt, and zoom. First, implement the interface on your existing class and assign the listener `this`. For more information on how to initialize your `MPIMapView`, read the Getting Started guide ```kotlin class MyClass : AppCompatActivity(), MPIMapViewListener, MPICameraListener { private var mapView: MPIMapView? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... other setup mapView?.cameraManager?.listener = this } } ``` The interface implements the `onCameraChanged()` function which passes in an `MPICameraTransform` whenever the camera moves. We can log the values within it to track the camera. ```kotlin override fun onCameraChanged(cameraTransform: MPICameraTransform) { Log.d("Position", cameraTransform.position.toString()) Log.d("Rotation", cameraTransform.rotation.toString()) Log.d("Tilt", cameraTransform.tilt.toString()) Log.d("Zoom", cameraTransform.zoom.toString()) } ``` ## Controlling the Camera Controlling the camera to set it to a specific tilt, rotation or zoom can be done with `mapView.cameraManager.set()`. It takes either `MPIOptions.CameraTransformNode`>) or `MPIOptions.CameraTransformCoordinate`>) which take either an `MPINavigatable.MPINode` or `MPIMap.MPICoordinate` respectively as the position. Tilt and rotation are set as radians, where as zoom is in the camera distance in meters from the target. ```kotlin mapView?.cameraManager?.set( MPIOptions.CameraTransformNode( zoom = 1234.0, tilt = 0.3, rotation = 1.5 ) ) // or mapView?.cameraManager?.set( MPIOptions.CameraTransformCoordinate( zoom = 800.0, position = mapView.currentMap?.createCoordinate( 43.519881426957596, -80.53906704663625 ) ) ) ``` It is possible to limit the tilting of the camera by listening to the change event and setting it to a desired value in `onCameraChanged()`. ```kotlin override fun onCameraChanged(cameraTransform: MPICameraTransform) { val tilt = 0.0 if (cameraTransform.tilt != tilt) { mapView?.cameraManager?.set(MPIOptions.CameraTransformNode(tilt = tilt)) } } ``` If you want to be able to zoom in closer than the default limit, use the setter `mapView?.cameraManager?.setMinZoom(zoomLevel = 200.0)`>). ## Animation The camera can also be animated to zoom, tilt and rotate to a MPINode or MPIMap.MPICoordinate. This works similar to the set methods described in the Controlling the Camera section above but instead of jumping directly to a camera position, the camera moves gradually to the new position. The MPICameraManager.animate() methods are use to accomplish this. Animating to the node of a location: ```kotlin val zoomTarget = mapView.venueData?.locations?.first { it.name == "Sunglass Hut" } val cameraTransform = MPIOptions.CameraTransformNode( zoom = 50.0, tilt = 2.0, rotation = 180.0, position = zoomTarget?.nodes?.first()) val cameraAnimation = MPIOptions.CameraAnimation( duration = 3000.0, easing = MPIOptions.EASING_MODE.EASE_IN) mapView.cameraManager.animate(cameraTransform, cameraAnimation) ``` Animating to latitude and longitude coordinates: ```kotlin val zoomCoordinate = mapView?.currentMap?.createCoordinate( latitude = 43.86147923972817, longitude = -78.94671703394187) val cameraTransform = MPIOptions.CameraTransformCoordinate( zoom = 50.0, tilt = 2.0, rotation = 180.0, position = zoomCoordinate) val cameraAnimation = MPIOptions.CameraAnimation( duration = 3000.0, easing = MPIOptions.EASING_MODE.EASE_IN) mapView.cameraManager.animate(cameraTransform, cameraAnimation) ``` ## Reset Camera To reset the camera, store the initial camera position in the onFirstMapLoaded() method. Then, at a later time these values can be used to reset the camera. ```kotlin // Store the default camera values when the map is loaded. override fun onFirstMapLoaded() { defaultTilt = mapView.cameraManager.tilt defaultZoom = mapView.cameraManager.zoom defaultRotation = mapView.cameraManager.rotation defaultPosition = mapView.cameraManager.position } // Use the default camera values to reposition the camera. fun resetCamera() { mapView.cameraManager.set(MPIOptions.CameraTransformCoordinate( zoom = defaultZoom, tilt = defaultTilt, rotation = defaultRotation, position = defaultPosition)) } ``` > A complete class that uses the code snippet in this guide can be found in the Mappedin Android Github repo: CameraControls.kt ### Flat Labels # Flat Labels > Flat Labels are painted on the polygon and fill the available space. They automatically flip to always face toward the camera. Flat labels can be added with default styles using `mapView.flatLabelsManager.labelAllLocations()`. This call should be made after `MPIMapViewListener.onFirstMapLoaded` is called, which indicates that the first map is loaded and can be modified. Note that Floating Labels are enabled by default. Floating Labels should be disabled using ShowVenue options before enabling Flat Labels. Flat Labels can be added and removed all at once as mentioned above or individually using the `MPIFlatLabelsManager.add()` and `MPIFlatLabelsManager.remove()` methods. > A complete class that uses the code snippet in this guide can be found in the Mappedin Android Github repo: FlatLabels.kt ## Styling Options Flat Labels are highly customizable, allowing control of size, color and spacing of each label. Flat Label Appearance - FlatLabelAppearance - `height` is the upper bounds of the Polygon by default. Height can be set if not using a Polygon or a custom height is desired. - `margin` is the amount of space to leave before starting the text from the edge of the bound of the canvas bound. These bounds are set in the Mappedin CMS. The default value is 5 pixels. - `color` is a string format of a hexadecimal color representation, such as `#ff00000` for red. - `fontSize` in pixels, by default 12 - `scaleMin` sets the minimum percentage to shrink the label to if it won't fit within the bounds at 100%. If it doesn't fit at that level, it won't be created. - `scaleStep` describes how much to decrement the scale each time it doesn't fit, so the map doesn't end up with too many different font sizes on screen at once. If only labelling a few Polygons rather than everything, set this and scaleMin to 0.1 to attempt to fit all text. The default value is 0.25. ```kotlin val flatLabelAppearance: MPIOptions.FlatLabelAppearance = MPIOptions.FlatLabelAppearance ( fontSize = 8f, color = "#0a0dbf") val flatLabelLocations: MPIOptions.FlatLabelAllLocations = MPIOptions.FlatLabelAllLocations(appearance = flatLabelAppearance) mapView.flatLabelsManager.labelAllLocations(flatLabelLocations) ``` > A complete class that uses the code snippet in this guide can be found in the Mappedin Android Github repo: FlatLabels.kt ### Floating Labels # Floating Labels > Map geometry alone is not enough to have a sense of a location. Adding labels to show the names of locations and other points of interest can help users navigate a space. > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: FloatingLabels.kt FloatingLabels anchor to polygon entrance nodes and automatically handle rotation and repositioning when the camera moves. Floating Labels are enabled by default with default styling. When customizing Floating Labels, disable them from appearing on startup in `ShowVenue` options. Floating Labels can be added to the map by calling `mapView.floatingLabelsManager.labelAllLocations()`. This call should be made after MPIMapViewListener.onFirstMapLoaded is called, which indicates that the first map is loaded and can be modified. Floating Labels can be added all at once as shown above or individually using the MPIFloatingLabelsManager.add() and MPIFloatingLabelsManager.remove() methods. ## Styling Options Floating Labels are highly customizable, allowing control of size, colour, spacing and the ability to display both a Marker (icon) and text in each label. Margin - `margin` in pixels around the label and marker. This will affect label density. Marker Appearance - MPIOptions.FloatingLabelAppearance.Marker - `backgroundColor` & `foregroundColor` - The marker takes both active and inactive variants of its foreground and background colors. - `size` of the marker in pixels. - `icon` is the SVG of the icon to place inside the Floating Label. - `iconVisibilityThreshold` defines when the icon becomes visible relative to the current zoom level. Values should be between 0 and 1. 0 appears at maximum zoom and 1 causes the marker to always be shown. Text Appearance - MPIOptions.FloatingLabelAppearance.Text - `foregroundColor` and `backgroundColor` can be set using CSS colors, as Hex strings or the CSS name of a color such as crimson. - `maxWidth` defines the maximum width of text in pixels. - `numLines` sets the maximum number of lines to display if the text spans multiple lines. - `size` is the text size in pixels. ## Adding Icons Icons can be added to Floating Labels to highlight certain categories or to make locations more eye-catching. These icons automatically adjust, becoming visible as the user zooms the map to view more detail. The supplied icon must be an SVG image and is provided to `MPIOptions.FloatingLabelAppearance.Marker`. Note that the SVG must be encapsulated using triple double quotes (“””SVG”””) to avoid double escaping characters within the string. ```kotlin val svgIcon: String = """ """.trimIndent() // Define colors using RGB values. val foreGroundColor: MPIOptions.FloatingLabelAppearance.Color = MPIOptions.FloatingLabelAppearance.Color(active = "#BF4320", inactive = "#7E2D16") val backgroundColor: MPIOptions.FloatingLabelAppearance.Color = MPIOptions.FloatingLabelAppearance.Color(active = "#FFFFFF", inactive = "#FAFAFA") val markerAppearance: MPIOptions.FloatingLabelAppearance.Marker = MPIOptions.FloatingLabelAppearance.Marker( foregroundColor = foreGroundColor, backgroundColor = backgroundColor, icon = svgIcon, ) val markerTheme: MPIOptions.FloatingLabelAppearance = MPIOptions.FloatingLabelAppearance(marker = markerAppearance) val themeOptions: MPIOptions.FloatingLabelAllLocations = MPIOptions.FloatingLabelAllLocations(markerTheme) mapView.floatingLabelsManager.removeAll() mapView.floatingLabelsManager.labelAllLocations(themeOptions) ``` ## Preset Themes The Mappedin SDK for Android provides two preset Floating Label themes that can be easily applied to a map. They are `lightOnDark` and `darkOnLight`. These two contrasting themes provide options for both light and dark maps. _Applying the lightOnDark Theme_ ```kotlin val themeOptions: MPIOptions.FloatingLabelAllLocations = MPIOptions.FloatingLabelAllLocations(MPIOptions.FloatingLabelAppearance.lightOnDark) mapView.floatingLabelsManager.removeAll() mapView.floatingLabelsManager.labelAllLocations(themeOptions) ``` _Applying the darkOnLight Theme_ ```kotlin val themeOptions: MPIOptions.FloatingLabelAllLocations = MPIOptions.FloatingLabelAllLocations(MPIOptions.FloatingLabelAppearance.darkOnLight) mapView.floatingLabelsManager.removeAll() mapView.floatingLabelsManager.labelAllLocations(themeOptions) ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: FloatingLabels.kt ### Getting Started # Getting Started > Mappedin SDK for Android helps to deliver the rich indoor mapping experience of a venue, inside Android apps. The Mappedin SDK for Android is a native interface to Mappedin JS. The SDK is a dependency built using Kotlin, and it automatically handles any authentication, network communication, fetching of map data, its display, and basic user interaction, such as panning, tapping, and zooming. The SDK allows a developer to build their own interactions. Additional layers can be rendered on top of the map rendered by the SDK. > Mappedin SDK for Android is supported on Android versions 8.0 and above ## Concepts - After the quick initial setup and configuration, the SDK renders the maps associated with the provided keys, inside an MPIMapView instance. - The SDK allows rendering a venue's maps in 3D. The SDK pulls the most up to date data from Mappedin CMS for all points of interest within a venue. - The SDK supports drawing navigation paths and providing instructions to and from any point of interest within the venue. Both accessible and non-accessible optimized routes are supported. You can build more functionality on top of this experience, such as searching or switching venues. Further pieces of this guide give you more details about how to setup the SDK and customize it as per your liking. ## Coding with AI Mappedin SDK for Android provides an llms-mappedin-android.txt file that can be used to help with coding when using Large Language Models (LLMs). ## Quickstart ### Add Permissions Add the following permissions to the AndroidManifest.xml below ``. ```xml ``` Ensure that hardware acceleration not turned off by setting it to `true`. ```xml ``` ### Add Application Dependency to Gradle Add the Mappedin SDK dependency to the application's `build.gradle` and sync the changes to download the SDK. The latest version can be found in in Maven. ``` implementation("com.mappedin.sdk:mappedin:5.9.0") ``` > Refer to the Mappedin Github repository to view the sample app created by Mappedin developers to understand how to embed a map into your app. ### Display the Venue Developers building an app using Jetpack Compose should switch to the Using Jetpack Compose guide. Developers building an app using XML based layouts should continue reading this guide. #### Create an MPIMapView Add an `MPIMapView` view to `activity_main.xml` and give it an id `mapView`: ```xml ``` In `MainActivity.kt`, add the following imports: ```kotlin import com.mappedin.sdk.MPIMapView import com.mappedin.sdk.listeners.MPIMapViewListener import com.mappedin.sdk.models.* import com.mappedin.sdk.web.MPIOptions ``` Get a handle for the map view in the `activity_main` layout: ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val mapView = findViewById(R.id.mapView) } ``` #### Load the Venue `loadVenue` is a `MPIMapView` function that renders the venue in an app by passing in an `options` object and an optional `showVenueOptions` object. The `options` object contains a `venue`, `clientId`, `clientSecret`, and optional `headers`. To get started use the Mappedin Id and Secret that has access to demo venues. To start using your own venues with the SDK you will need to contact a Mappedin representative to get your own unique Id and Secret. | Property | Value | | -------------- | --------------------------------------------------------------------------- | | `venue` | `mappedin-demo-mall` | | `clientId` | See Here | | `clientSecret` | See Here | Optionally, pass in `showVenueOptions` to modify the properties of the map, including `backgroundColor`, `firstMapId`, `labelAllLocationsOnInit` (whether labels appear), `backgroundAlpha`, on the first load. The following sample code shows an example of how to use it: ```kotlin // use loadVenue to load venue mapView.loadVenue( MPIOptions.Init("5eab30aa91b055001a68e996", "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1", "mappedin-demo-mall", headers = listOf(MPIHeader("testName", "testValue"))), MPIOptions.ShowVenue(labelAllLocationsOnInit = true, backgroundColor = "#CDCDCD") ) {} ``` #### Result The app should display something that looks like this in the Android Emulator: !Android SDK v5 - onDataLoaded And zooming in to have a closer look: !Android SDK v5 - onDataLoaded zoomed ### Interactivity # Interactivity > The MPIMapClickListener interface enables an app to receive an MPIMapClickEvent, which include the objects that were clicked. The MPIMapClickEvent includes the following properties, which describe the click event. - Floating Label - Map - Blue Dot - Path - Polygon - Position In order to use MPIMapClickListener, it must be added to an MPIMapView. ```kotlin mapView.mapClickListener = this ``` > A complete class that uses the code snippet in this guide can be found in the Mappedin Android Github repo: AddInteractivity.kt ## Floating Labels To allow floating labels to be clicked, they must be set to be interactive. ```kotlin mapView.floatingLabelsManager.labelAllLocations( MPIOptions.FloatingLabelAllLocations(interactive = true)) ``` When clicked, the MPIMapClickEvent provides a List of Pairs of all floating labels that were clicked. The Pair contains the floating label's location as an MPINavigatable.MPINode and it's text as a String. The code sample below checks if the floating label list is empty and if not, appends the text of the first floating label clicked to a StringBuilder. ```kotlin override fun onClick(mapClickEvent: MPIMapClickEvent) { val message = StringBuilder() if (mapClickEvent.floatingLabels.isNotEmpty()) { message.append("Floating Label Clicked: ") message.append(mapClickEvent.floatingLabels.first().second) message.append("\n") } } ``` ## Maps MPIMapClickEvent includes a list of MPIMaps corresponding to every map that a user's click passed through. These are in order of first to last intersected. For example, if the user clicked on a map showing the second floor of a building, the second floor map would be first in the list followed by the first floor. The code sample below checks if the maps list is empty and if not, stores the first map's name in a String variable called `title`. ```kotlin override fun onClick(mapClickEvent: MPIMapClickEvent) { var title = String() if (mapClickEvent.maps.isNotEmpty()) { title = mapClickEvent.maps.first().name } } ``` ## Blue Dot MPIMapClickEvent contains a boolean value that is set to true if the user clicked on or near the Blue Dot representing their current location. The code sample below demonstrates detecting if the user clicked near the Blue Dot. ```kotlin override fun onClick(mapClickEvent: MPIMapClickEvent) { if (mapClickEvent.nearBlueDot) { // The user clicked near the Blue Dot. } } ``` ## Paths MPIMapClickEvent includes a list of paths the user clicked on. Multiple paths are included if the user clicked on a junction where paths cross. These are in order of first to last intersected by the click point and will be empty if no paths were clicked. When drawing a path, MPIOptions.Path must be used to enable a path to be clickable by setting interactive to true. This applies when using both MPIJourneyManager and MPIPathManager. ```kotlin // Creating a clickable path using MPIJourneyManager val journeyOpts = MPIOptions.Journey(pathOptions = MPIOptions.Path(interactive = true)) mapView.journeyManager.draw(directions = it, options = journeyOpts) // Creating a clickable path using MPIPathManager mapView.pathManager.add(directions.path, MPIOptions.Path(interactive = true)) ``` The code sample below detects if the user clicked on an interactive path. ```kotlin override fun onClick(mapClickEvent: MPIMapClickEvent) { val message = StringBuilder() if (mapClickEvent.paths.isNotEmpty()) { message.append("You clicked a path.\n") } } ``` ## Polygons MPIMapClickEvent provides a list of MPINavigatable.MPIPolygons that the user's click event passes through. These are in order of first to last intersected and will be empty if no polygons were clicked. The code sample below detects if a polygon was clicked and if it was, appends the location name of the polygon to a StringBuilder. ```kotlin override fun onClick(mapClickEvent: MPIMapClickEvent) { val message = StringBuilder() if (mapClickEvent.polygons.isNotEmpty()) { message.append("Polygon clicked: ") message.append(mapClickEvent.polygons.first().locations.first().name) message.append("\n") } } ``` ## Position / Coordinates MPIMapClickEvent can be used to determine the exact point on the map where the user clicked by observing the MPIMap.MPICoordinate it contains. The `MPICoordinate's` properties provide the x and y coordinates and latitude and longitude of the user's click. The code sample below illustrates how to extract the `MPICoordinate` values from `MPIMapClickEvent` and append them to a StringBuilder. ```kotlin override fun onClick(mapClickEvent: MPIMapClickEvent) { val message = StringBuilder() message.append("Coordinate Clicked: \nLatitude: ") message.append(mapClickEvent.position?.latitude) message.append("\nLongitude: ") message.append(mapClickEvent.position?.longitude) } ``` > A complete class that uses the code snippet in this guide can be found in the Mappedin Android Github repo: AddInteractivity.kt ### Locations # Locations > An MPILocation represents a specific area of interest such as a store location, meeting room or washroom. They contain a rich amount of metadata about a location such as a its name, description, operating hours, logo, phone number and more. > A complete class that uses the code snippet in this guide can be found in the Mappedin Android Github repo: ListLocations.kt An MPILocation's MPINodes and MPIPolygons can be on multiple MPIMaps, or in multiple non-adjacent places on the same `MPIMap`. For example, all washrooms at a given venue could belong to one `MPILocation`. A washroom location might a have a few `MPIPolygons` spread throughout the venue for each washroom that exists. On the other hand, a department store could live on multiple floors. A single store can just have one presence, and therefore one MPIPolygon. Some Locations have a single point with `MPINodes`. ## Location Types Each location has a single MPILocation.type, which allows filtering or grouping locations of interest to the user. Location types can be unique for each venue. For example a mall may use types of amenity, landmark and tenant. A stadium could use gate, seat and restaurant. An office could use desk, meeting room and washroom. ## Location Categories Each location has 0 to many MPILocation.categories. Categories provide another way to filter and group locations. Location categories are also unique for each venue. Examples of categories for a mall could be Clothing, Electronics, Footwear and Food & Drink. ## Code Sample The code sample below retrieves all locations for a venue, filters those that have a type of tenant, a description, a small logo and sorts them alphabetically by name. ```kotlin // sortedLocations is a listOf() sortedLocations = mapView.venueData?.locations?.filter { it.type == "tenant" && // Filter locations with a type of tenant. it.description != null && // Filter locations with a description. it.logo?.small != null // Filter locations with a small logo. }?.sortedBy { it.name } ?: listOf() ``` > A complete class that uses the code snippet in this guide can be found in the Mappedin Android Github repo: ListLocations.kt ### Markers # Markers > The Mappedin SDK for Android allows you to add markers to the map. These are elements containing arbitrary HTML that the SDK will anchor to an `MPINode` or a `MPICoordinate` on the map, and automatically handle rotation and repositioning to face the camera. > A complete class that uses the code snippet in this guide can be found in the Mappedin Android Github repo: Markers.kt ## Drawing Markers Use the `createMarker()`):%20String>) -method of the `MPIMapView` to draw a marker at a map node. ```kotlin mapView?.createMarker(node = someNode, contentHtml = "
Marker
") ``` The `MPIMapViewListener` exposes an `onPolygonClick()` function which we can use to create markers on user interaction. We can use the polygon entrance node from the touched polygon to position the marker and display the location name in the contentHtml. ```kotlin override fun onPolygonClicked(polygon: MPINavigatable.MPIPolygon) { val location = polygon.locations[0] val entrance = polygon.entrances[0] mapView?.createMarker(node = entrance, contentHtml = "
${location.name}
") } ``` !Android Markers Alternatively, pass a `MappedinCoordinate` to draw a marker at a specified latitude/longitude. ```kotlin val coordinate = mapView?.currentMap?.createCoordinate( latitude = 43.51905183293411, longitude = -80.53701846381122 ) coordinate?.let { mapView?.createMarker(coordinate = it, contentHtml = "
Marker
") } ``` !Android Coordinate Marker Using a coordinate doesn't require a pre-configured node and the marker can be placed at any arbitrary location on the map. ## Removing Markers Markers can be removed from the map by passing the marker id returned from `createMarker()`):%20String>) to the `removeMarker()`>) method. ```kotlin mapView?.removeMarker(markerId) ``` We can adjust the `onPolygonClicked()` example to store the marker id after creating. If a marker exists when the map is touched subsequently then the existing marker is removed and replaced with the new marker. ```kotlin var markerId: String? = null override fun onPolygonClicked(polygon: MPINavigatable.MPIPolygon) { val location = polygon.locations[0] val entrance = polygon.entrances[0] markerId?.let { mapView?.removeMarker(it) } mapView?.createMarker( node = entrance, contentHtml = "
${location.name}
" ) } ``` ## Marker Options Individual marker anchoring and rank can be tweaked via MPIOptions.Marker):%20String>). ```kotlin mapView?.createMarker( node = entrance, contentHtml = "
Marker
", MPIOptions.Marker( rank = 4.0, anchor = MPIOptions.MARKER_ANCHOR.CENTER ) ) ``` The `rank` parameter enables you to set the priority or rank of the markers. The ranks are closely associated with Mappedin JS enum MPIOptions.COLLISION_RANK. The default rank is `3.0` (HIGH). The `anchor` option sets the default position of the marker. The positions are defined in the MPIOptions.MARKER_ANCHOR enum. The default position is CENTER. > A complete class that uses the code snippet in this guide can be found in the Mappedin Android Github repo: Markers.kt ### Migration Guide # Migration Guide On December 7th, 2022 we released the Mappedin SDK for Android v5. This release focuses on improving the developer experience by making methods more accessible, simplifying complex methods, and reducing code required. This short migration guide explains the steps needed to migrate from version 4. - Mappedin SDK for Android is available from https://mvnrepository.com/artifact/com.mappedin.sdk/mappedin - API reference can be found at https://docs.mappedin.com/android-sdk-api/v5/latest/ ## MPIMapView `labelAllLocations` has been divided into two individual objects with their own methods. ### Floating Labels ```kotlin // Before mapView.labelAllLocations(MPIOptions.FloatingLabelAllLocations()) // After mapView.floatingLabelsManager.labelAllLocations(MPIOptions.FloatingLabelAllLocations()) ``` ### Flat Labels ```kotlin // Before mapView.labelAllLocations(MPIOptions.FlatLabelAllLocations()) // After mapView.flatLabelsManager.labelAllLocations(MPIOptions.FlatLabelAllLocations()) ``` `enableBlueDot` has been deprecated in favor of `MPIMapView.blueDotManager.enable`. `disableBlueDot` has been deprecated in favor of `MPIMapView.blueDotManager.disable`. `updatePosition` has been deprecated in favor of `MPIMapView.blueDotManager.updatePosition`. `drawJourney` has been deprecated in favor of `MPIMapView.journeyManager.draw`. `clearJourney` has been deprecated in favor of `MPIMapView.journeyManager.clear`. ## MPIMapViewListener `onBlueDotUpdated` has been deprecated in favor of `onBlueDotPositionUpdate` and `onBlueDotStateChange`. ## MPIMapViewManager `setPolygonColor` has been updated: - The `polygonID` parameter has been changed to `polygon`, and now requires a polygon object (`MPINavigatable.MPIPolygon`) instead of a string. - The `opacity` parameter has been removed. - The `textColor` parameter has been removed. - ```kotlin mapView.setPolygonColor(polygon, "blue") ``` ## MPIWebData `OnBlueDotPositionUpdate` has been deprecated in favor of `OnBlueDotPositionUpdate` and `OnBlueDotStateChange`. ## Camera Controls `mapView.focusOn` has been deprecated in favor of `mapView.cameraManager.focusOn` ```kotlin // Before mapView.focusOn(MPIOptions.Focus(polygons = listOf(polygon))) // After mapView.cameraManager.focusOn(targets = MPIOptions.CameraTargets(polygons = listOf(polygon))) ``` The callback of `setRotation`, `setTilt`, and `setZoom` now takes `MPIError` as a parameter. ## Models `MPICameraControlsManager` has been deprecated in favor of `MPICameraManager`. `MPIBlueDot` has been deprecated in favor of `MPIBlueDotPositionUpdate` and `MPIBlueDotStateChange`. The `color` attribute of the `MPINavigatable.MPILocation` class has been deprecated. The `map` attribute of the `MPINavigatable.MPIPolygon` class is now of type `MPIMap` instead of `String`. The `map` attribute of the `MPINavigatable.MPINode` class is now of type `MPIMap` instead of `String`. The `categories` attribute of the `MPINavigatable.MPILocation` class is now of type `List` instead of `List`. ## Events The event `BLUE_DOT_UPDATED` has been deprecated in favor of `BLUEDOT_POSITION_UPDATE` and `BLUEDOT_STATE_CHANGE` instead. ### Outdoor View # Outdoor View > > Outdoor view is currently an experimental component of the Mappedin SDK. While in this state, some features may not behave as intended and APIs may be changed. Experimental features are being tested together with early adopters to fine-tune them. The Mappedin SDK’s outdoor view capability allows an application to display a Mappedin map on top of an outdoor tile set. Currently the Mappedin SDK makes use of OpenStreetMap vector images retrieved from a Mappedin hosted tile server. Use of outdoor view requires MultiBufferRendering to be enabled. !Mobile-SDK-Outdoor-View ## Enable Outdoor View To enable outdoor view, set OutdoorView.enabled and ShowVenue.multiBufferRendering to `true` as shown in the code snippet below. ```kotlin highlight={8-9} // See Trial API key Terms and Conditions // https://developer.mappedin.com/docs/demo-keys-and-maps/ mapView.loadVenue( MPIOptions.Init( "5eab30aa91b055001a68e996", "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1", "mappedin-demo-mall", ), MPIOptions.ShowVenue(shadingAndOutlines = true, multiBufferRendering = true, outdoorView = MPIOptions.OutdoorView(enabled = true) )) { Log.e(javaClass.simpleName, "Error loading map view") } ``` ### Release Notes # Release Notes Mappedin SDK for Android release notes are posted here and this page will be kept up-to-date as updates are released. The SDK is available by adding the following to your `build.gradle`: `implementation 'com.mappedin.sdk:mappedin:5.9.0'` { // Release notes template: // https://keepachangelog.com/en/1.0.0/#how // ## vXX.XX.XX - Month Day, Year // _ Added // _ Changed // _ Deprecated // _ Removed // _ Fixed // _ Security } ## v5.9.0 - April 15, 2025 - Added support for MPIOptions.FloatingLabelAppearance.Marker.iconSize, iconFit, iconPadding, & iconScaleInterpolation. ## v5.8.0 - April 8, 2025 - Added - Support for MPICameraManager.setSafeAreaInsets. - Added - Return FloatingLabel ID when adding a FloatingLabel. - Fixed - FloatingLabels added using a coordinate are not clickable. - Fixed - MPIOptions.AddFloatingLabel.interactive is missing. ## v5.7.2 - March 20, 2025 - Fixed - MPIOptions.FocusOnOptions.padding is not working. ## v5.7.1 - March 12, 2025 - Changed - Version bump only to redeploy and resolve artifact issues on MavenCentral. ## v5.7.0 - February 28, 2025 - Added support for specifying other `things` to be included in the map using `MPIOptions.Init.things`. - Added `MPILocation.extra`, `MPIVenue.extra`, `MPIPolygon.extra` and `MPINode.extra`, which are used to access custom map data. ## v5.6.0 - January 17, 2025 - Added support for `MPILocation.instances`. - Added new options to `MPIOptions.OutdoorView` to allow more customization of the outdoor view and to support EU tile servers. - Multiple bug fixes. ## v5.5.0 - Jun 19, 2024 - Added the ability to move markers. - Added the ability to programmatically disable user triggered camera interactions. - Added method getMappedinCoordinateAtScreenCoordinate that gets the `MPIMap.MPICoordinate` using a MapView's screen x and y coordinate. ## v5.4.1 - Jun 7, 2024 - Fixed Floating Label appearance on Intel integrated GPUs. - Fixed images not appearing on invisible polygons. - Fixed a crash when using live polygons with no rendered perspectives. - Fixed an issue where Dynamic Focus would stop working after venue language change. - Fixed cases where exterior walls would not render. - Fixed `changeLanguage` and `getVenue` functions to accept language codes case-insensitive. - Fixed a potential Blue Dot failure when a floor has no nodes. - Fixed an issue in some venues where Blue Dot state would be `OUTSIDE_MAP` while within the map's bounds. - Fixed an issue where directions may be missing distances between instructions. - Fixed an issue where hydrating the map could crash without rendered perspectives. - Fixed an issue where unattached nodes could cause a map crash. - Fixed an issue causing some analytics events to not fire. - Fixed an issue where layer visibility was still determined by perspectives when `useLivePolygons: true`. ## v5.4.0 - March 7, 2024 - Added the ability to change languages without recreating the map. - Added interactivity to Markers and Labels. They can now be clicked. - Added the ability to override the Blue Dot’s bearing. - Added the ability to draw paths using coordinates. - Added ambient occlusion. - Added outdoor view. - Added shadingAndOutlines, which makes the tops and edges of walls more visible. - Added Dynamic performance, which will attempt to maintain above 25 FPS. - Fixed noisy ambient occlusion when monitor DPI is less than 2. - Fixed FloatingLabels.labelAllLocations() not respecting polygon logo images. - Fixed map failing to load when image textures cannot be downloaded. - Fixed the MappedinPolygon locations getter returning duplicate results. - Fixed special characters not yielding search results with OfflineSearch. - Fixed FloatingLabels not positioning at their correct height. - Fixed polygon hover color for polygons that have set color. - Fixed default value for automatically changing maps in dynamic focus mode to math indoor being fully visible. - Fixed image flipping when maps changed. - Fixed floating label rendering height. - Fixed default ambient occlusion performance. - Fixed clearAllPolygonColors not working. ## v5.3.0 - Dec 18, 2023 - Added unique context for Android analytics - Improved analytics events. - Improved collider performance. - Fixed focusOn animation performing redundant rotations. - Fixed Let MPIBlueDotManager.enabled be used without a default MPIOptions.BlueDot object. - Fixed cases where textures would fail to load. - Fixed Journey pulse animation when arrows were turned off. - Fixed BlueDot showing on incorrect floor. - Fixed invalid error message when adding a FloatingLabel to a polygon with an entrance. - Fixed Tooltips not appearing within their colliders. - Fixed Tooltips created at the edge of the screen becoming hidden. ## v5.2.5 - Nov 10, 2023 - Docs - Updated MPIOptions to include defaults - Added - Add floating labels to coordinates - Added - COLLISION_RANK - Added - Tooltips - Added - Venue Language Selection - Fixed - Instances where a duplicate path would be returned in the click event. - Fixed - Initial Marker placement - Fixed - Performance when adding multiple labels one-by-one - Fixed - A crash when modifying paths during map transition ## v5.2.4 - Oct 6, 2023 - Fixed Map could crash if changed while a journey is being drawn. - Fixed Blue Dot appeared darker when multibufferRendering is enabled. ## v5.2.3 - Sept 26, 2023 - Added MPIClickListener and MPIClickEvent to allow detecting the coordinates of the user's click as well as the maps, paths and/or floating labels they clicked on. - Added MPIPathManager to allow adding and removing interactive paths. - Added Multibuffer rendering. - Added xRayPaths. - Fixed setting backgroundAlpha to 0 would result in it being set to 1. - Fixed flat labels and geometry showing for two floors at once. ## v5.2.2 - Sept 14, 2023 - Added MPIOptions.BlueDot.useRotationMode - Changed default accessibility value of getDirections & getDistance to false to match Mappedin JS. - Fixed the appearance of paths when the last node is on a different map. - Fixed an issue with BlueDot when using STATE.FOLLOW and Journey. - Fixed BlueDot exiting follow mode after receiving position updates. - Fixed an issue where focusOn would not fit targets within viewbox if tilt and rotation changed. - Fixed the extra warnings in the console. - Fixed a bug in polygon material loading. - Fixed end caps of routes to be level with the path instead of sinking them to the floor. ## v5.2.1 - July 5, 2023 - Added `getDistance` - Added `useDraftData` flag to `MPIOptions.Init` - Fixed an error with BlueDot when using `useRotationMode: true` and `setState(STATE.FOLLOW)`. ## v5.2.0 - April 12, 2023 - Added `getPolygonsAtScreenCoordinate`. - Added `getPolygonsAtCoordinate`. ## v5.1.0 - March 15, 2023 - Add support for icons in Floating Labels. ## v5.0.4 - February 27, 2023 - Providing options for `labelAllLocations` is now optional - Marked deprecated camera functions as such - Improved documentation for Markers ## v5.0.3 - January 18, 2023 - Fix issues with removing and relabelling Flat Labels ## v5.0.0 - December 7th, 2022 > This release includes changes from all previous beta versions. - Improved camera API. Now uses a single class, `MPICameraManager` - Deprecated old functions in favor of new API structure - `labelAllLocations` has been split into two separate functions: `MPIFlatLabelsManager.labelAllLocations` and `MPIFloatingLabelsManager.labelAllLocations` - Add `emitAnalyticsEvents` flag to `loadVenue` - Add method to control `Camera.maxTilt` Migrating from v4? See migration guide for more details. ## v5.0.0-beta.7 - November 14, 2022 - Add `emitAnalyticsEvents` flag to `loadVenue` - Add method to control `Camera.maxTilt` ### Search # Search > The Mappedin SDK for Android provides a way to search for locations and categories using their names, descriptions and tags. The search is performed locally on the device, meaning it can be used when a device is offline. > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: Search.kt ## Methods for Searching MPISearchManager offers two different methods to perform a search, `MPISearchManager.search` and `MPISearchManager.suggest`. The `search` method is used to search for locations and categories based on a query string. Results are returned as a List containing MPIOfflineSearchResultLocations and MPIOfflineSearchResultCategorys, which both extend from MPIOfflineSearchResultCommon. The `suggest` method is used to generate suggested search terms for a given query string and returns a List of MPIOfflineSearchSuggestions that contains a String with the suggestion. ## Understanding Search Results Search results contain a score and details about how it was matched. When displaying the results to a user, the results should be sorted to show the highest score first. ## Code Example Below is a code sample that performs the following operations: 1. Performs a search using the String `query`. 2. Filters the results that contain locations (`MPIOfflineSearchResultLocation`), removing categories (`MPIOfflineSearchResultCategory`). 3. Filters locations that contain the type of “tenant”. Note that location types will be unique to each venue and are chosen by the map administrator. 4. Sorts the results by score with the highest score first. ```kotlin mapView.searchManager.search(query) { results -> val searchLocations = results // Filter the results that contain locations. ?.filterIsInstance() // Filter locations that contain the type of “tenant”. ?.filter { it.location.type == "tenant" } // Sort the results by score with the highest score first. ?.sortedByDescending { it.score } } ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: Search.kt ### Tooltips # Tooltips > A Tooltip is similar to a marker, except, it has a mechanism to avoid collisions with other items on the map by positioning itself relative to the point where it's added. This is done automatically and makes it useful for long text with icons. Tooltips can also be clickable, which makes them ideal for showing connection points such as elevators and switching to the following map after an interaction from the user. Tooltips could be used as helpful hints for a user to navigate, with prompts such as "Take elevator to Floor 2" at a certain node on the map. > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: Tooltips.kt ## Drawing Tooltips When creating a Tooltip, the app specifies some HTML to display and the node to attach it too. The tooltip will be anchored to a specified node or coordinate in one of eight anchor orientations, which are defined in MPIOptions.TooltipAnchor. The possible anchor types are `top`, `bottom`, `left`, `right`, `topLeft`, `topRight`, `bottomLeft`, and `bottomRight`. This describes the position of the tooltip relative to the node or coordinate to which it is attached. Tooltip options such as interactivity and collision rank are specified using MPIOptions.Tooltip. The code below uses createTooltip to create a tooltip and store its Id: ```kotlin mapView.createTooltip( node = nodeToAttachTooltip, contentHtml = """
This is a tooltip!
""", ) { id -> if (id != null) { tooltipId = id } } ``` ## Custom Tooltips While regular Tooltips come with some base styling, custom tooltips allow an app to have complete control over the element's appearance. Creation is similar to a regular tooltip, with the addition of a CSS selector string that allows the SDK to target the tooltip. The code below uses createCustomTooltip to create a tooltip and store its Id: ```kotlin mapView.createCustomTooltip( node = nodeToAttachTooltip, contentHtml = """
Tool Tip
""", selector = ".my-tooltip-wrapper", MPIOptions.Tooltip( collisionRank = MPIOptions.COLLISION_RANK.MEDIUM, )){ id -> if (id != null) { tooltipId = id } } ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: Tooltips.kt ### Turn by Turn Directions # Turn by Turn Directions > Text-based turn-by-turn directions can be a helpful aid to the map view and Blue Dot. Mappedin SDK for Android offers text directions whenever a route is requested. ```kotlin var instructions = listOf() mapView.getDirections(to = destination, from = departure) { directions -> directions?.instructions?.let { instructions = it } } ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: TurnByTurnDirections.kt ## Directions and Instructions To request directions, call MPIMapView.getDirections() , which returns an MPIDirections object. MPIDirections provides the path as a list of MPINodes, the total walking distance in meters and a list of text-based instructions for navigation. MPIInstruction list is used for turn-by-turn directions. The key components are: - `instruction` text used to guide the user - `distance` in meters for this instruction segment The `instruction` may include a store or a location name. That happens when the SDK is able to determine a relevant location nearby the instruction that could be helpful in navigating the venue. The MPIAction includes an MPIActionType and MPIBearingType. These types represent what the user should do and the direction to do it. The `action` object can be used to localize instructions. The Mappedin SDK provides the following action and bearing text in English only. The text could be substituted for another language before being displayed to a user. MPIActionType - MPIActionType.ARRIVAL = Arrival - MPIActionType.DEPARTURE = Departure - MPIActionType.EXITVORTEX = ExitVortex - MPIActionType.TAKEVORTEX = TakeVortex - MPIActionType.TURN = Turn MPIBearingType - MPIBearingType.LEFT = Left - MPIBearingType.RIGHT = Right - MPIBearingType.SLIGHTLEFT = SlightLeft - MPIBearingType.SLIGHTRIGHT = SlightRight - MPIBearingType.STRAIGHT = Straight > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: TurnByTurnDirections.kt ### Using Callbacks # Using Callbacks > The Mappedin Mobile SDKs offer callback functions that developers can use to trigger various events based on the completion of certain actions. These methods are useful for modifying UI elements such as loading views, changing floors, changing polygon colours, adding markers etc. An `MPIMapViewListener` will respond to events triggered by the `MPIMapView`. To listen to these events, create or modify a class to fit the `MPIMapViewListener` interface and implement the methods you'd like to use. They will be executed automatically after the corresponding event has occurred. ## onDataLoaded A callback function that is executed when the 3D files have been downloaded for the first map, and are starting to load into memory. The `MPIMapView` is fully functional at this point, but some things will still be popping in. At this point, you can interact with the `MPIMapView`, venue data, call helper functions, call other mapView functions, and set up your application. This function will pass in an `MPIData` parameter, `data`, which allows you to use maps, locations, polygons, nodes, vortexes, and map groups of the venue. For example, you can access the list of a venue’s locations by using the class variable locations of the **data** parameter. You can also call other `mapView` functions here. For example, you can label your polygons by calling labelPolygon and passing in both an `MPIPolygon` parameter and various customization options for your labels. ## onFirstMapLoaded A callback that is executed when the first map is fully loaded. This means `onDataLoaded` has already been fired (if specified) and all the textures and other resources have finished popping in. ## onMapChanged A callback function that is executed when the map of your `MPIMapView` changes. A common use of this callback is to add a level selector for your venue. If you are working with a venue that has multiple maps (such as a venue with multiple floors) you may want to create a dropdown to allow the user to select a level. When you change the map, this callback will be executed, and it will pass in an `MPIMap` parameter, `map`, which is the current map. You can access different properties of `map` using this parameter. ### Sample Code ```kotlin class MainActivity : AppCompatActivity(), MPIMapViewListener { override fun onBlueDotPositionUpdate(update: MPIBlueDotPositionUpdate) { // Called when the blueDot that tracks the user position is updated } override fun onBlueDotStateChange(stateChange: MPIBlueDotStateChange) { // Called when the state of blueDot is changed } override fun onMapChanged(map: MPIMap) { // Called when the map is changed } override fun onPolygonClicked(polygon: MPIPolygon) { // Called when the polygon is clicked } override fun onNothingClicked() { // Called when a tap doesn't hit any spaces } override fun onDataLoaded(data: MPIData) { // Called when the mapView has finished loading both the view and venue data } override fun onFirstMapLoaded() { // Called when the first map is fully loaded } override fun onStateChanged(state: MPIState) { // Called when the state of the map has changed } } ``` ### Using Jetpack Compose # Using Jetpack Compose > A Composable with an MPIMapView can be created for use with Jetpack Compose by making use of an AndroidView. This allows an app with a Compose based UI to make use of MPIMapView to display a map. To allow attaching of an MPIMapViewListener and to call methods of the MPIMapView, MPIMapView is instantiated outside of the AndroidView and its state is saved by remember. The MPIMapViewListener interface is implemented in the `mapViewListener` object below and later attached to the `mapView` variable. ```kotlin @Composable fun MappedinComposable() { val ctx = LocalContext.current val mapView by remember { mutableStateOf(MPIMapView(ctx)) } val mapViewListener = object : MPIMapViewListener { val tag = "MapViewScreen" override fun onDataLoaded(data: MPIData) { Log.i(tag, "Venue Data Loaded") } override fun onFirstMapLoaded() { Log.i(tag, "First Map Loaded") } //Implement the rest of the MPIMapViewListener methods… } ``` The AndroidView used to hold the MPIMapViewListener is shown below. `mapView` layout parameters are set to allow it to take up the screen space it is provided. Next, `loadVenue` is called. `loadVenue` is an MPIMapView function, which accepts MPIOptions.Init containing the venue details and then renders the map. ```kotlin AndroidView( factory = { mapView.layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, ) mapView.loadVenue( MPIOptions.Init( "5eab30aa91b055001a68e996", "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1", "mappedin-demo-mall", ), ) { Log.e("MappedinComposable", "Error loading map view") } mapView.listener = mapViewListener mapView }, ) ``` The MPIOptions object contains a venue, clientId, clientSecret, and optional headers. To get started use the Mappedin Id and Secret that has access to demo venues named `mapViewListener`, which is defined in the code snippet at the beginning of the guide is set as the mapView listener, which is then returned to the factory. ## Full Code Sample The MappedinComposable is now ready to be used in an app. Complete sample code for the MappedInComposable is shown below. This Composable displays a loading indicator until the first map is ready to be shown to the user and then displays the map. ```kotlin @Composable fun MappedinComposable() { var mapLoaded by remember { mutableStateOf(false) } val ctx = LocalContext.current val mapView by remember { mutableStateOf(MPIMapView(ctx)) } val mapViewListener = object : MPIMapViewListener { val tag = "MapViewScreen" override fun onBlueDotPositionUpdate(update: MPIBlueDotPositionUpdate) { Log.i(tag, "Blue Dot Position Update") } override fun onBlueDotStateChange(stateChange: MPIBlueDotStateChange) { Log.i(tag, "Blue Dot State Change") } override fun onDataLoaded(data: MPIData) { Log.i(tag, "Venue Data Loaded") } override fun onFirstMapLoaded() { Log.i(tag, "First Map Loaded") mapLoaded = true } override fun onMapChanged(map: MPIMap) { Log.i(tag, "Map Changed") } override fun onNothingClicked() { Log.i(tag, "Nothing Clicked") } override fun onPolygonClicked(polygon: MPINavigatable.MPIPolygon) { Log.i(tag, "Polygon Clicked") } override fun onStateChanged(state: MPIState) { Log.i(tag, "State Changed") } } AndroidView( factory = { mapView.layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, ) mapView.loadVenue( MPIOptions.Init( "5eab30aa91b055001a68e996", "RJyRXKcryCMy4erZqqCbuB1NbR66QTGNXVE0x3Pg6oCIlUR1", "mappedin-demo-mall", ), ) { Log.e("MappedinComposable", "Error loading map view") } mapView.listener = mapViewListener mapView }, ) if (!mapLoaded) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { Row(verticalAlignment = Alignment.CenterVertically) { CircularProgressIndicator() } } } } ``` ### Wayfinding # Wayfinding > Wayfinding is one of the most often used features of digital maps and the Mappedin SDK for Android makes it incredibly easy to draw a path from A to B. It only takes a known start and end location to generate directions, which can be passed to `MPIJourneyManager` to draw it on the map. > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: ABWayfinding.kt ## Single-Destination Wayfinding We can use the `onFirstMapLoaded` function of the `MPIMapViewListener` to present wayfinding directions when the map loads. Select your departure and destination locations from `venueData` and supply them as the "to" and "from" for `getDirections()`. In the callback, pass the resulting directions to `journeyManager.draw()` to display the route. ```kotlin override fun onFirstMapLoaded() { val departure = mapView.venueData?.locations?.first { it.name == "Pet World" }!! val destination = mapView.venueData?.locations?.first { it.name == "Microsoft" }!! mapView.getDirections(to = destination, from = departure) { if (it != null) { mapView.journeyManager.draw(directions = it) } } } ``` `journeyManager` exposes the `MPIJourneyManager` helper class to display single and multi-destination wayfinding easily on the map. Functionality of the `Journey` could be replicated by drawing the paths and adding well designed tooltips at connection points. !Android Single Destination ## Wayfinding From A User's Location To find out how to start the journey from the user's current location, refer to Wayfinding from Blue Dot in the Blue Dot Guide. ## Multi-Floor Wayfinding Using `MPIJourneyManager` helper, no additional work is needed to provide wayfinding between floors. Whenever a user needs to switch a floor, an interactive tooltip with an icon indicating the type of connection (such as an elevator or escalator) will be drawn. By tapping the tooltip, the map view switches to the destination floor. !Android Multi-Floor ## Wayfinding Using Accessible Routes When requesting directions for a journey, it is important to consider the needs of the user. Users with mobility restrictions may require a route that avoids stairs and escalators and instead uses ramps or elevators. The MPIMapView.getDirections() method accepts an accessible boolean flag, which allows specifying whether an accessible route should be returned. By default, the shortest available route is chosen. The following code demonstrates how to request directions that make use of accessible routes. ```kotlin mapView.getDirections(to = destination, from = departure, accessible = true) { if (it != null) { mapView.journeyManager.draw(directions = it) } } ``` ## Multi-Destination Wayfinding With the Mappedin SDK, it's possible to draw a path including multiple waypoints. To generate directions, create an `MPIDestinationSet`>) from polygons, locations or nodes and request directions from a starting location with `getDirections()`. Create a couple properties for the UIViewController which we will use to store the current step number and the destination list. Use the `onFirstMapLoaded` function in `MPIMapViewListener` to draw the route once the map is ready. ```kotlin private var step = 0 private var destinations = emptyList() override fun onFirstMapLoaded() { val departure = mapView.venueData?.locations?.first { it.name == "Parking Lot E" }!! destinations = listOf( mapView.venueData?.locations?.first { it.name == "Apple" }, mapView.venueData?.locations?.first { it.name == "Microsoft" }, mapView.venueData?.locations?.first { it.name == "ThinkKitchen" }, mapView.venueData?.locations?.first { it.name == "Parking Lot E" } ) mapView.getDirections(to = MPIDestinationSet(destinations = destinations as List), from = departure) { if (it != null) { mapView.journeyManager.draw(directions = it) } } } ``` Once the user has completed a segment of the journey, the active step can be changed with `journeyManager.setStep()`>). The following will move through the steps when user clicks outside of interactive polygons on the map using the `onNothingClicked` function. ```kotlin override fun onNothingClicked() { step += 1 mapView.journeyManager.setStep(step = step % destinations.size) { it?.message?.let { Log.e("MPIMapViewListener", it) } } } ``` !Android Multi-Destination ## Wayfinding Without Using Journey While `Journey` is a great utility for drawing a single path with all the features, it may sometimes be limiting. For instance, you may only draw one `Journey` at a time, and subsequent calls to `journeyManager.draw()` will overwrite the previous. For full control over the route, draw the path using the `MPIPathManager.add()` method within the `MPIMapView`%20:%20WebView,%20MPIMapViewInterface>) and pass it the `directions.path`. ```kotlin override fun onFirstMapLoaded() { val departure = mapView.venueData?.locations?.first { it.name == "ThinkKitchen" }!! val destination = mapView.venueData?.locations?.first { it.name == "American Eagle" }!! mapView.getDirections(to = destination, from = departure) { it?.let { mapView.pathManager.add(path = it.path) } } } ``` This creates a basic path with no extra features. Using this method, you will need to handle both multi-floor navigation and multi-destination wayfinding manually using markers and by drawing multiple paths. We can draw a secondary path the same way as the first while providing different start and end locations. To make this new path stand out, style it a different color by changing the `color` property. The documentation for MPIOptions.Path covers the other options available to you. ```kotlin mapView.pathManager.add(path = it.path, MPIOptions.Path(color = "green")) ``` !Android Multi-Path ## X-Ray Paths > X-Ray Paths are currently an experimental component of the Mappedin SDK. While in this state, some features may not behave as intended and APIs may be changed. Experimental features are being tested together with early adopters to fine-tune them. Maps with narrow corridors can benefit from using X-Ray Paths for wayfinding. When enabled, X-Ray Paths cause polygons, such as walls or desks, to become transparent when a path is drawn behind them. This allows the path to remain visible. The screenshot below illustrates a path between cabins on a cruise ship with X-Ray Paths disabled on the left and enabled on the right. !X-Ray Paths - Cruise Ship X-Ray Paths are enabled and disabled using the boolean `xRayPath` parameter of MPIOptions.ShowVenue object passed to loadVenue. X-Ray Paths are enabled by default, but they do require `multiBufferRendering` to also be enabled. `multiBufferRendering` is currently disabled by default. The code sample below demonstrates enabling both `xRayPath` and `multiBufferRendering`. ```kotlin highlight={7} mapView.loadVenue( MPIOptions.Init( "clientID", "clientSecret", "venueSlug", ), MPIOptions.ShowVenue(multiBufferRendering = true, xRayPath = true), ) { Log.e(javaClass.simpleName, "Error loading map view") } ``` > A complete class that uses the code snippets in this guide can be found in the Mappedin Android Github repo: ABWayfinding.kt