Usage Guide¶
Master the MINE - Indoor Navigation Engine with comprehensive usage examples, best practices, and advanced implementation patterns. This guide covers everything from basic setup to advanced customization.
Prerequisites
Before diving into this guide, make sure you've completed:
- β Installation - Library setup and configuration
- β Quick Start - Basic implementation
Overview¶
The Indoor Navigation Engine provides multiple approaches for integration:
-
Traditional Views
Use XML layouts and Activities/Fragments for traditional Android development
-
Jetpack Compose
Modern declarative UI with Compose integration
-
Programmatic
Full programmatic control for dynamic use cases
Choose the approach that best fits your project architecture.
Working with Map Data¶
Loading Map Configuration¶
The engine uses JSON configuration files to define map structure, floors, and metadata. Place your configuration file in the assets folder.
import com.machinestalk.indoornavigationengine.util.JsonUtil
import com.machinestalk.indoornavigationengine.models.MapBuild
import android.content.Context
class MapDataManager(private val context: Context) {
fun loadMapConfiguration(fileName: String): MapBuild? {
return try {
JsonUtil.LoadJsonFromAsset(context, fileName)
} catch (e: Exception) {
Log.e("MapDataManager", "Failed to load map: ${e.message}")
null
}
}
// Load with validation
fun loadMapConfigurationSafe(fileName: String): Result<MapBuild> {
return runCatching {
val mapData = JsonUtil.LoadJsonFromAsset(context, fileName)
?: throw IllegalStateException("Map data is null")
// Validate map data
require(mapData.floors.isNotEmpty()) { "Map must have at least one floor" }
require(mapData.modelPath.isNotEmpty()) { "Model path cannot be empty" }
mapData
}
}
}
import com.machinestalk.indoornavigationengine.util.JsonUtil;
import com.machinestalk.indoornavigationengine.models.MapBuild;
import android.content.Context;
import android.util.Log;
public class MapDataManager {
private Context context;
public MapDataManager(Context context) {
this.context = context;
}
public MapBuild loadMapConfiguration(String fileName) {
try {
return JsonUtil.LoadJsonFromAsset(context, fileName);
} catch (Exception e) {
Log.e("MapDataManager", "Failed to load map: " + e.getMessage());
return null;
}
}
public boolean validateMapData(MapBuild mapData) {
if (mapData == null) return false;
if (mapData.getFloors().isEmpty()) return false;
if (mapData.getModelPath().isEmpty()) return false;
return true;
}
}
Map Configuration Structure¶
Here's an example of a well-structured map configuration JSON:
{
"id": "shopping-mall-001",
"name": "Grand Shopping Mall",
"version": "1.0.0",
"modelPath": "models/mall_floor_plan.glb",
"floors": [
{
"id": "ground",
"name": "Ground Floor",
"level": 0,
"defaultFloor": true,
"bounds": {
"minX": -50.0,
"maxX": 50.0,
"minY": -30.0,
"maxY": 30.0
}
},
{
"id": "first",
"name": "First Floor",
"level": 1,
"defaultFloor": false,
"bounds": {
"minX": -50.0,
"maxX": 50.0,
"minY": -30.0,
"maxY": 30.0
}
}
],
"pointsOfInterest": [
{
"id": "poi-001",
"name": "Main Entrance",
"floorId": "ground",
"position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"category": "entrance"
}
],
"metadata": {
"timezone": "UTC+3",
"locale": "en_US"
}
}
Traditional Android Views¶
Using in Activities¶
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.machinestalk.indoornavigationengine.ui.MineSceneView
import com.machinestalk.indoornavigationengine.util.JsonUtil
class MapActivity : AppCompatActivity() {
private lateinit var sceneView: MineSceneView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize scene view
sceneView = MineSceneView(this).apply {
// Load map data
val mapData = JsonUtil.LoadJsonFromAsset(this@MapActivity, "maps/venue.json")
mapData?.let { setMapData(it) }
}
setContentView(sceneView)
}
override fun onResume() {
super.onResume()
sceneView.onResume()
}
override fun onPause() {
super.onPause()
sceneView.onPause()
}
override fun onDestroy() {
super.onDestroy()
sceneView.onDestroy()
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.machinestalk.indoornavigationengine.ui.MineSceneView
android:id="@+id/sceneView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- Floor selector -->
<com.google.android.material.chip.ChipGroup
android:id="@+id/floorSelector"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:singleSelection="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
Using in Fragments¶
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.machinestalk.indoornavigationengine.ui.MineSceneView
import com.machinestalk.indoornavigationengine.util.JsonUtil
class MapFragment : Fragment() {
private var sceneView: MineSceneView? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
sceneView = MineSceneView(requireContext()).apply {
// Load map configuration
val mapData = JsonUtil.LoadJsonFromAsset(
requireContext(),
"maps/museum.json"
)
mapData?.let { setMapData(it) }
}
return sceneView!!
}
override fun onResume() {
super.onResume()
sceneView?.onResume()
}
override fun onPause() {
super.onPause()
sceneView?.onPause()
}
override fun onDestroyView() {
super.onDestroyView()
sceneView?.onDestroy()
sceneView = null
}
companion object {
fun newInstance() = MapFragment()
}
}
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import com.machinestalk.indoornavigationengine.ui.MineSceneView;
import com.machinestalk.indoornavigationengine.util.JsonUtil;
public class MapFragment extends Fragment {
private MineSceneView sceneView;
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
sceneView = new MineSceneView(requireContext());
// Load map configuration
MapBuild mapData = JsonUtil.LoadJsonFromAsset(
requireContext(),
"maps/museum.json"
);
if (mapData != null) {
sceneView.setMapData(mapData);
}
return sceneView;
}
@Override
public void onResume() {
super.onResume();
if (sceneView != null) {
sceneView.onResume();
}
}
@Override
public void onPause() {
super.onPause();
if (sceneView != null) {
sceneView.onPause();
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (sceneView != null) {
sceneView.onDestroy();
sceneView = null;
}
}
}
Jetpack Compose Integration¶
Basic Compose Implementation¶
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import com.machinestalk.indoornavigationengine.ui.IndoorNavigationScene
import com.machinestalk.indoornavigationengine.util.JsonUtil
@Composable
fun MapScreen(
mapConfigFile: String = "maps/venue.json",
modifier: Modifier = Modifier
) {
val context = LocalContext.current
val mapData = remember {
JsonUtil.LoadJsonFromAsset(context, mapConfigFile)
}
mapData?.let {
IndoorNavigationScene(
mapBuild = it,
modifier = modifier
)
} ?: run {
// Show error state
ErrorLoadingMap()
}
}
@Composable
fun ErrorLoadingMap() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text("Failed to load map", style = MaterialTheme.typography.h6)
}
}
Advanced Compose with State Management¶
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import com.machinestalk.indoornavigationengine.ui.IndoorNavigationScene
import com.machinestalk.indoornavigationengine.models.MapBuild
import com.machinestalk.indoornavigationengine.models.CameraConfig
import com.machinestalk.indoornavigationengine.models.ThemeConfig
import com.machinestalk.indoornavigationengine.util.JsonUtil
// ViewModel for map state
class MapViewModel : ViewModel() {
var currentFloor by mutableStateOf(0)
private set
var selectedPOI by mutableStateOf<String?>(null)
private set
fun selectFloor(floor: Int) {
currentFloor = floor
}
fun selectPOI(poiId: String?) {
selectedPOI = poiId
}
}
@Composable
fun AdvancedMapScreen(
mapConfigFile: String = "maps/venue.json",
viewModel: MapViewModel = viewModel()
) {
val context = LocalContext.current
// Load map data
val mapData = remember {
JsonUtil.LoadJsonFromAsset(context, mapConfigFile)
}
// Custom camera configuration
val cameraConfig = remember {
CameraConfig(
initialPosition = floatArrayOf(0f, 15f, 15f),
lookAt = floatArrayOf(0f, 0f, 0f),
fov = 45f,
near = 0.1f,
far = 1000f
)
}
// Custom theme
val themeConfig = remember {
ThemeConfig(
primaryColor = 0xFF2196F3.toInt(),
accentColor = 0xFFFF5722.toInt(),
backgroundColor = 0xFFFFFFFF.toInt(),
pathColor = 0xFF4CAF50.toInt()
)
}
Box(modifier = Modifier.fillMaxSize()) {
mapData?.let { data ->
IndoorNavigationScene(
mapBuild = data,
cameraConfig = cameraConfig,
themeConfig = themeConfig,
currentFloor = viewModel.currentFloor,
onFloorChanged = { floor -> viewModel.selectFloor(floor) },
onPOISelected = { poiId -> viewModel.selectPOI(poiId) },
modifier = Modifier.fillMaxSize()
)
// Floor selector overlay
FloorSelector(
floors = data.floors,
currentFloor = viewModel.currentFloor,
onFloorSelected = { viewModel.selectFloor(it) },
modifier = Modifier
.align(Alignment.TopEnd)
.padding(16.dp)
)
// POI information card
viewModel.selectedPOI?.let { poiId ->
POIInfoCard(
poiId = poiId,
onDismiss = { viewModel.selectPOI(null) },
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(16.dp)
)
}
}
}
}
@Composable
fun FloorSelector(
floors: List<Floor>,
currentFloor: Int,
onFloorSelected: (Int) -> Unit,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier,
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Column(
modifier = Modifier.padding(8.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
floors.forEachIndexed { index, floor ->
FilterChip(
selected = currentFloor == index,
onClick = { onFloorSelected(index) },
label = { Text(floor.name) }
)
}
}
}
}
@Composable
fun POIInfoCard(
poiId: String,
onDismiss: () -> Unit,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = "Location Details",
style = MaterialTheme.typography.titleLarge
)
Text(
text = "ID: $poiId",
style = MaterialTheme.typography.bodyMedium
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
TextButton(onClick = onDismiss) {
Text("Close")
}
Button(
onClick = { /* Navigate to this POI */ },
modifier = Modifier.padding(start = 8.dp)
) {
Text("Navigate Here")
}
}
}
}
}
Advanced Configuration¶
Custom Camera Setup¶
import com.machinestalk.indoornavigationengine.models.CameraConfig
import com.machinestalk.indoornavigationengine.components.CameraController
fun setupCustomCamera(sceneView: MineSceneView) {
val cameraConfig = CameraConfig(
// Initial camera position (x, y, z)
initialPosition = floatArrayOf(10f, 20f, 30f),
// Point camera is looking at
lookAt = floatArrayOf(0f, 0f, 0f),
// Field of view in degrees
fov = 60f,
// Near clipping plane
near = 0.1f,
// Far clipping plane
far = 500f
)
sceneView.setCameraConfig(cameraConfig)
// Advanced camera controller
val cameraController = CameraController(sceneView).apply {
// Set movement speed
movementSpeed = 5.0f
// Set rotation sensitivity
rotationSensitivity = 0.5f
// Enable smooth animations
enableSmoothTransitions = true
transitionDuration = 300 // milliseconds
}
}
// Animate camera to specific location
fun animateCameraToLocation(
cameraController: CameraController,
target: FloatArray
) {
cameraController.animateTo(
position = target,
duration = 1000,
onComplete = {
Log.d("Camera", "Animation complete")
}
)
}
Custom Theme Configuration¶
import com.machinestalk.indoornavigationengine.models.ThemeConfig
import android.graphics.Color
fun createCustomTheme(): ThemeConfig {
return ThemeConfig(
// Primary brand color
primaryColor = Color.parseColor("#2196F3"),
// Accent color for highlights
accentColor = Color.parseColor("#FF5722"),
// Background color
backgroundColor = Color.parseColor("#FAFAFA"),
// Path/route color
pathColor = Color.parseColor("#4CAF50"),
// Path width in pixels
pathWidth = 8f,
// POI marker colors
poiMarkerColor = Color.parseColor("#FFC107"),
poiMarkerSize = 24f,
// Text colors
textPrimaryColor = Color.parseColor("#212121"),
textSecondaryColor = Color.parseColor("#757575"),
// Floor colors (different color per floor)
floorColors = listOf(
Color.parseColor("#E3F2FD"),
Color.parseColor("#FFF3E0"),
Color.parseColor("#F3E5F5")
)
)
}
// Apply theme to scene
fun applyTheme(sceneView: MineSceneView, theme: ThemeConfig) {
sceneView.setThemeConfig(theme)
}
Floor Management¶
import com.machinestalk.indoornavigationengine.models.Floor
import com.machinestalk.indoornavigationengine.components.FloorManager
class FloorNavigationManager(private val sceneView: MineSceneView) {
private val floorManager = FloorManager(sceneView)
fun switchToFloor(floorIndex: Int, animated: Boolean = true) {
if (animated) {
floorManager.switchToFloorAnimated(
floorIndex = floorIndex,
duration = 500,
onComplete = {
Log.d("Floor", "Switched to floor $floorIndex")
}
)
} else {
floorManager.switchToFloor(floorIndex)
}
}
fun getCurrentFloor(): Floor? {
return floorManager.currentFloor
}
fun getFloorByLevel(level: Int): Floor? {
return floorManager.getFloorByLevel(level)
}
fun getAllFloors(): List<Floor> {
return floorManager.getAllFloors()
}
// Floor change listener
fun setupFloorListener() {
floorManager.setOnFloorChangedListener { oldFloor, newFloor ->
Log.d("Floor", "Changed from ${oldFloor?.name} to ${newFloor?.name}")
}
}
}
Navigation and Routing¶
Path Finding¶
import com.machinestalk.indoornavigationengine.components.PathFinder
import com.machinestalk.indoornavigationengine.models.Location
import com.machinestalk.indoornavigationengine.models.Route
class NavigationManager(private val sceneView: MineSceneView) {
private val pathFinder = PathFinder(sceneView)
fun findRoute(
from: Location,
to: Location,
accessible: Boolean = false
): Route? {
return pathFinder.findPath(
start = from,
end = to,
accessibleOnly = accessible,
onRouteCalculated = { route ->
// Display route on map
displayRoute(route)
}
)
}
fun displayRoute(route: Route) {
sceneView.clearRoute() // Clear existing route
sceneView.drawRoute(
route = route,
color = Color.BLUE,
width = 8f,
animated = true
)
// Add waypoint markers
route.waypoints.forEach { waypoint ->
sceneView.addMarker(
position = waypoint.position,
icon = R.drawable.ic_waypoint
)
}
}
fun startNavigation(route: Route) {
sceneView.startNavigation(route) { step ->
// Handle navigation step
showNavigationInstruction(step)
}
}
fun stopNavigation() {
sceneView.stopNavigation()
sceneView.clearRoute()
}
}
Turn-by-Turn Instructions¶
import com.machinestalk.indoornavigationengine.models.NavigationStep
fun showNavigationInstruction(step: NavigationStep) {
val instruction = when (step.action) {
NavigationStep.Action.TURN_LEFT -> "Turn left"
NavigationStep.Action.TURN_RIGHT -> "Turn right"
NavigationStep.Action.GO_STRAIGHT -> "Continue straight"
NavigationStep.Action.ARRIVE -> "You have arrived"
NavigationStep.Action.CHANGE_FLOOR -> {
"Go to ${step.targetFloorName}"
}
}
// Display in UI
showNotification(
title = instruction,
message = step.description,
distance = "${step.distanceToNext}m"
)
}
Points of Interest (POI)¶
Managing POIs¶
import com.machinestalk.indoornavigationengine.models.PointOfInterest
import com.machinestalk.indoornavigationengine.components.POIManager
class POIController(private val sceneView: MineSceneView) {
private val poiManager = POIManager(sceneView)
fun addPOI(
id: String,
name: String,
position: FloatArray,
floorId: String,
category: String,
iconRes: Int
) {
val poi = PointOfInterest(
id = id,
name = name,
position = position,
floorId = floorId,
category = category,
metadata = mapOf(
"icon" to iconRes.toString()
)
)
poiManager.addPOI(poi)
}
fun findNearbyPOIs(
location: FloatArray,
radius: Float,
category: String? = null
): List<PointOfInterest> {
return poiManager.findNearby(
position = location,
radius = radius,
filterCategory = category
)
}
fun highlightPOI(poiId: String) {
poiManager.highlightPOI(
poiId = poiId,
color = Color.YELLOW,
duration = 2000 // milliseconds
)
}
fun setupPOIClickListener() {
poiManager.setOnPOIClickListener { poi ->
// Handle POI click
showPOIDetails(poi)
}
}
}
Performance Optimization¶
Best Practices¶
import com.machinestalk.indoornavigationengine.models.DisplayConfig
fun optimizePerformance(sceneView: MineSceneView) {
sceneView.apply {
// Use appropriate render quality
displayConfig = DisplayConfig(
renderQuality = when {
isHighEndDevice() -> DisplayConfig.RenderQuality.HIGH
isMidRangeDevice() -> DisplayConfig.RenderQuality.MEDIUM
else -> DisplayConfig.RenderQuality.LOW
},
// Disable expensive features on low-end devices
shadowsEnabled = isHighEndDevice(),
ambientOcclusion = isHighEndDevice(),
antiAliasing = !isLowEndDevice()
)
// Limit frame rate if needed
maxFrameRate = 60
// Enable culling
enableFrustumCulling = true
// LOD (Level of Detail) settings
enableLOD = true
lodDistances = floatArrayOf(10f, 25f, 50f)
}
}
fun isHighEndDevice(): Boolean {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
return activityManager.memoryClass >= 512
}
Memory Management¶
class MemoryEfficientMapActivity : AppCompatActivity() {
private var sceneView: MineSceneView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sceneView = MineSceneView(this)
setContentView(sceneView)
}
override fun onLowMemory() {
super.onLowMemory()
// Release non-essential resources
sceneView?.apply {
clearCache()
reduceTextureQuality()
}
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
when (level) {
ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW -> {
sceneView?.clearCache()
}
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> {
sceneView?.onPause()
}
}
}
override fun onDestroy() {
// Properly clean up
sceneView?.onDestroy()
sceneView = null
super.onDestroy()
}
}
Debugging and Logging¶
Enable Debug Mode¶
import com.machinestalk.indoornavigationengine.util.MineLogger
fun setupDebugging(sceneView: MineSceneView) {
if (BuildConfig.DEBUG) {
// Enable verbose logging
MineLogger.setLogLevel(MineLogger.Level.VERBOSE)
// Show FPS counter
sceneView.showFpsCounter = true
// Show debug overlay
sceneView.showDebugOverlay = true
// Enable wireframe mode
sceneView.debugWireframe = false // Set true to see wireframes
// Log render statistics
sceneView.setOnRenderStatsListener { stats ->
Log.d("Render", "FPS: ${stats.fps}, " +
"Draw calls: ${stats.drawCalls}, " +
"Triangles: ${stats.triangles}")
}
}
}
Complete Usage Example¶
Here's a comprehensive example bringing everything together:
package com.example.indoornavapp
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.machinestalk.indoornavigationengine.ui.MineSceneView
import com.machinestalk.indoornavigationengine.util.JsonUtil
import com.machinestalk.indoornavigationengine.models.*
import com.machinestalk.indoornavigationengine.components.*
class FullFeaturedMapActivity : AppCompatActivity() {
private lateinit var sceneView: MineSceneView
private lateinit var navigationManager: NavigationManager
private lateinit var poiController: POIController
private lateinit var floorManager: FloorNavigationManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize scene
setupScene()
// Load map data
loadMapData()
// Setup components
setupComponents()
// Configure interactions
setupInteractions()
}
private fun setupScene() {
sceneView = MineSceneView(this).apply {
// Configure display
displayConfig = DisplayConfig(
renderQuality = DisplayConfig.RenderQuality.HIGH,
antiAliasing = true,
shadowsEnabled = true
)
// Set custom theme
setThemeConfig(createCustomTheme())
// Configure camera
setCameraConfig(CameraConfig(
initialPosition = floatArrayOf(0f, 20f, 20f),
lookAt = floatArrayOf(0f, 0f, 0f),
fov = 55f
))
}
setContentView(sceneView)
}
private fun loadMapData() {
val mapData = JsonUtil.LoadJsonFromAsset(this, "maps/venue.json")
mapData?.let {
sceneView.setMapData(it)
Toast.makeText(this, "Map loaded successfully", Toast.LENGTH_SHORT).show()
} ?: run {
Toast.makeText(this, "Failed to load map", Toast.LENGTH_LONG).show()
}
}
private fun setupComponents() {
navigationManager = NavigationManager(sceneView)
poiController = POIController(sceneView)
floorManager = FloorNavigationManager(sceneView)
// Setup POI click handling
poiController.setupPOIClickListener()
// Setup floor change listener
floorManager.setupFloorListener()
}
private fun setupInteractions() {
val gestureHandler = GestureHandler(sceneView).apply {
isPanEnabled = true
isZoomEnabled = true
isRotationEnabled = true
setOnGestureListener(object : GestureHandler.OnGestureListener {
override fun onSingleTap(x: Float, y: Float) {
handleMapTap(x, y)
}
})
}
}
private fun handleMapTap(x: Float, y: Float) {
sceneView.hitTest(x, y) { result ->
result?.let {
// User tapped on a location
showLocationOptions(it)
}
}
}
private fun createCustomTheme() = ThemeConfig(
primaryColor = getColor(R.color.primary),
accentColor = getColor(R.color.accent),
pathColor = getColor(R.color.path),
backgroundColor = getColor(R.color.background)
)
override fun onResume() {
super.onResume()
sceneView.onResume()
}
override fun onPause() {
super.onPause()
sceneView.onPause()
}
override fun onDestroy() {
super.onDestroy()
sceneView.onDestroy()
}
}
Next Steps¶
-
Navigation Features
Implement advanced routing and turn-by-turn navigation
-
Theme Customization
Create beautiful custom themes for your maps
-
UI Components
Add pre-built UI components to enhance user experience
-
API Reference
Deep dive into the complete API documentation
Common Questions¶
How do I handle multiple maps in one app?
Create separate MineSceneView instances for each map, or reuse a single instance and call setMapData() with different configurations when switching maps.
Can I use this library with other 3D rendering libraries?
The Indoor Navigation Engine uses Filament for rendering. While you can use other libraries in your app, the map rendering is handled exclusively by the engine.
How do I optimize for battery life?
Reduce render quality, disable shadows and ambient occlusion, lower the frame rate, and pause rendering when the app is in the background.
Can I load maps from a remote server?
Yes! Use mapLoader.loadFromUrl() to fetch map data from a remote endpoint. Make sure to handle network errors and implement appropriate caching.
Additional Resources¶
- π API Reference - Complete API documentation
- π― Features Overview - All available features
- π Troubleshooting - Common issues and solutions
- π¬ FAQ - Frequently asked questions
- π§ Support - Get help from our team
You're All Set!
You now have comprehensive knowledge of how to use the Indoor Navigation Engine. Start building amazing indoor navigation experiences!