7.1 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This is an Android navigation app built with Jetpack Compose that supports multiple routing providers (OSRM, Valhalla, TomTom) and includes Android Auto/Automotive OS integration. The app uses MapLibre for rendering, ObjectBox for local persistence, and Koin for dependency injection.
Build Commands
# Build the app (from repository root)
./gradlew :app:assembleDebug
# Build specific flavor
./gradlew :app:assembleDemoDebug
./gradlew :app:assembleFullDebug
# Run tests
./gradlew test
# Run tests for specific module
./gradlew :common:data:test
./gradlew :common:car:test
# Install on device
./gradlew :app:installDebug
# Clean build
./gradlew clean
Module Structure
The project uses a multi-module architecture:
- app/ - Main Android app with Jetpack Compose UI for phone
- common/data/ - Core data layer with routing logic, repositories, and data models (shared by all modules)
- common/car/ - Android Auto/Automotive OS UI implementation
- automotive/ - Placeholder for future native Automotive OS app
Dependencies flow: app → common:car → common:data
Architecture
Routing Providers (Pluggable System)
The app supports three routing engines that implement the NavigationRepository abstract class:
- OsrmRepository - OSRM routing engine
- ValhallaRepository - Valhalla routing engine
- TomTomRepository - TomTom routing engine
Each provider has a corresponding mapper class (OsrmRoute, ValhallaRoute, TomTomRoute) that converts provider-specific JSON responses to the universal Route data model.
Adding a new routing provider:
- Create
NewProviderRepositoryextendingNavigationRepositoryincommon/data/src/main/java/com/kouros/navigation/data/ - Implement
getRoute()method - Create
NewProviderRoute.ktwithmapToRoute()function - Add provider detection logic in
Route.Builder.route() - Update
NavigationUtils.getViewModel()to return appropriate ViewModel
Data Flow
User Action (search/select destination)
↓
ViewModel.loadRoute() [LiveData]
↓
NavigationRepository.getRoute() [Selected provider]
↓
*Route.mapToRoute() [Convert to universal Route model]
↓
RouteModel.startNavigation()
↓
RouteModel.updateLocation() [On each location update]
↓
UI observes LiveData and displays current step
Key Classes
Navigation Logic:
RouteModel.kt- Core navigation engine (tracks position, calculates distances, manages steps)RouteCarModel.kt- Extends RouteModel with Android Auto-specific formattingViewModel.kt- androidx.ViewModel with LiveData for route, traffic, places, etc.
Data Models:
Route.kt- Universal route structure used by all providersPlace.kt- ObjectBox entity for favorites/recent locationsStepData.kt- Display data for current navigation instruction
Repositories:
NavigationRepository.kt- Abstract base class for all routing providers- Also handles Nominatim geocoding search and TomTom traffic incidents
Android Auto:
NavigationCarAppService.kt- Entry point for Android Auto/Automotive OSNavigationSession.kt- Session managementNavigationScreen.kt- Car screen templates with NavigationType state machineSurfaceRenderer.kt- Handles virtual display and map rendering
External APIs
| Service | Purpose | Base URL |
|---|---|---|
| OSRM | Routing | https://kouros-online.de/osrm/route/v1/driving/ |
| Valhalla | Routing | https://kouros-online.de/valhalla/route |
| TomTom | Traffic incidents | https://api.tomtom.com/traffic/services/5/incidentDetails |
| Nominatim | Geocoding search | https://kouros-online.de/nominatim/ |
| Overpass | POI & speed limits | OpenStreetMap Overpass API |
Important Constants
Located in Constants.kt (common/data):
NEXT_STEP_THRESHOLD = 120.0 m // Distance to show next maneuver
DESTINATION_ARRIVAL_DISTANCE = 40.0 m // Distance to trigger arrival
MAXIMAL_SNAP_CORRECTION = 50.0 m // Max distance to snap to route
MAXIMAL_ROUTE_DEVIATION = 80.0 m // Max deviation before reroute
SharedPreferences keys:
ROUTING_ENGINE- Selected provider (0=Valhalla, 1=OSRM, 2=TomTom)DARK_MODE_SETTINGS- Theme preferenceAVOID_MOTORWAY,AVOID_TOLLWAY- Route preferences
Navigation Flow
- Route Loading: User searches via Nominatim → selects place → ViewModel.loadRoute() calls selected repository
- Route Parsing: Provider JSON → mapper converts to universal Route → RouteModel.startNavigation()
- Location Tracking: FusedLocationProviderClient provides updates → RouteModel.updateLocation()
- Step Calculation: findStep() snaps location to nearest waypoint → updates current step
- UI Updates: currentStep() and nextStep() provide display data (instruction, distance, icon, lanes)
- Arrival: When distance < DESTINATION_ARRIVAL_DISTANCE, navigation ends
Testing Navigation
The app includes mock location support for testing:
- Set
useMock = truein MainActivity - Enable "Mock location app" in Android Developer Options
- Choose test mode:
type = 1- Simulate movement along entire routetype = 2- Test specific step rangetype = 3- Replay GPX track file
ObjectBox Database
ObjectBox is configured in common/data/build.gradle.kts with the kapt plugin. The database stores:
- Recent destinations (category: "Recent")
- Favorite places (category: "Favorites")
- Imported contacts (category: "Contacts")
Queries use ObjectBox query builder pattern with generated Place_ property accessors.
Compose UI Structure
Phone App:
MainActivity.kt- Main entry with permission handling and Navigation ComposeNavigationScreen.kt- Turn-by-turn navigation displaySearchSheet.kt/NavigationSheet.kt- Bottom sheet contentMapView.kt- MapLibre rendering with camera state management
Android Auto:
- Uses CarAppService Screen templates (NavigationTemplate, MessageTemplate, MapWithContentTemplate)
- NavigationType enum controls which template to display (VIEW, NAVIGATION, REROUTE, RECENT, ARRIVAL)
Build Flavors
Two product flavors with dimension "version":
- demo - applicationId:
com.kouros.navigation.demo - full - applicationId:
com.kouros.navigation.full
Common Patterns
Dependency Injection (Koin):
single { OsrmRepository() }
viewModel { ViewModel(get()) }
LiveData Observation:
viewModel.route.observe(this) { routeJson ->
routeModel.startNavigation(routeJson, context)
}
Step Finding Algorithm: RouteModel iterates through all step waypoints, calculates distance to current location, and snaps to the nearest waypoint to determine current step index.
Known Limitations
- Valhalla route mapping is incomplete (search for TODO comments in ValhallaRoute.kt)
- Rerouting logic exists but needs more testing
- Speed limit queries via Overpass API could be optimized for performance
- TomTom implementation uses local JSON file (R.raw.tomom_routing) instead of live API