Android navigator for the Changan Deepal S09 EV that mirrors a Yandex MapKit-powered route to the car's built-in windshield HUD projector.
The main screen runs on the central touchscreen (search address, tap a point on the map, build a route). When you press the Go button, the route is projected onto the windshield as a compact overlay: maneuver icon, distance, street name and ETA on a pure-black background that becomes transparent on the glass.
For deeper architecture notes see CLAUDE.md.
- Android Studio Hedgehog (2023.1) or newer.
- JDK 17. Android Studio ships its own JBR; using it is the easiest path (see commands below).
- Android SDK with platforms 34 and 35 installed via the SDK
Manager.
compileSdk = 35,minSdk = 26. - A device or emulator running Android 8.0+ for testing.
- A Yandex MapKit API key (see step 2 below).
git clone <your-fork-url> car-apps
cd car-apps- Open https://developer.tech.yandex.ru/.
- Sign in with a Yandex account and start the connect-API flow.
- Pick the MapKit Mobile SDK product. This single key covers map rendering, address search, the driving router and turn-by-turn data; no other API needs to be enabled.
- Once approved, copy the key.
This file is gitignored on purpose: it holds your machine's SDK path and the secret API key. It is not created automatically when you clone, you need to add it yourself.
Create local.properties in the project root (next to build.gradle.kts):
# Path to your Android SDK installation:
# macOS: /Users/<you>/Library/Android/sdk
# Linux: /home/<you>/Android/Sdk
# Windows: C:\\Users\\<you>\\AppData\\Local\\Android\\Sdk
sdk.dir=/Users/<you>/Library/Android/sdk
# Yandex MapKit key from step 2
MAPKIT_API_KEY=00000000-0000-0000-0000-000000000000The Gradle build script reads MAPKIT_API_KEY from this file and exposes it
as BuildConfig.MAPKIT_API_KEY, which is then handed to MapKit at startup
(HudNavApp.onCreate). If the value is missing, the map tiles will fail to
load.
Make sure the key is on its own line. It is easy to accidentally paste it at the end of
sdk.dir=and break the SDK path.
- Android Studio:
File > Openand select thecar-appsdirectory. Gradle will sync automatically. - Command line: you can build and install without ever opening the IDE.
The project uses the Gradle wrapper. Set JAVA_HOME to the JDK that ships
with Android Studio so Gradle finds a recent enough Java:
# macOS, Android Studio bundled JBR
export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"
# Linux, typical paths
# export JAVA_HOME="/snap/android-studio/current/android-studio/jbr"./gradlew :app:installDebug./gradlew :app:assembleRelease
# Output: app/build/outputs/apk/release/app-release.apkThe release config currently signs with the standard debug keystore, which is
good enough for personal use. Replace it with your own keystore in
app/build.gradle.kts (buildTypes.release.signingConfig) before publishing.
Driving to the car for every change is impractical. Two tools help.
# Create a 1280x480 overlay window on the device. It appears as displayId 3.
adb shell settings put global overlay_display_devices "1280x480/160"
# Inspect available displays (the HUD will be FLAG_PRESENTATION).
adb shell dumpsys display | grep -E "displayId|FLAG_PRESENTATION"
# Turn it off when done.
adb shell settings put global overlay_display_devices nullIn the app, open the HUD calibration screen and tap the Test on HUD button. The HUD activity opens on the detected secondary display with a fake maneuver populated, so you can adjust the projection insets and scale visually without needing GPS or a real route.
After building a route on the main screen, tap the Simulate button. The foreground service interpolates position along the polyline at about 50 km/h and publishes location updates to the HUD activity, so you can see the route move without driving.
app/src/main/
├── AndroidManifest.xml
├── java/com/example/hudnav/
│ ├── HudNavApp.kt MapKit init
│ ├── MainActivity.kt map, search, tap-to-pin, Go button
│ ├── HudActivity.kt HUD overlay on the projector
│ ├── SettingsActivity.kt HUD calibration sliders
│ ├── NavigationService.kt ForegroundService: GPS + simulation
│ ├── state/ NavigationState + StateFlow repository
│ └── util/ display locator, density, geo helpers
└── res/
├── layout/ 3 activities + reusable slider include
├── drawable/ maneuver glyphs, position arrow, backgrounds
├── raw/hud_map_style.json minimal Yandex map style for the HUD
└── values/ colors / themes / strings
| Symptom | Likely cause / fix |
|---|---|
SDK location not found |
sdk.dir line in local.properties is missing or got concatenated with the next line. Fix the newline. |
| App launches, map area is blank or grey | Wrong or missing MAPKIT_API_KEY in local.properties. Re-check the value and rebuild. |
bad base-64 crash on launch |
Old font_certs.xml left over from downloadable fonts. Should not happen on main; if it does, wipe .gradle/ and rebuild. |
Unable to locate a Java Runtime from ./gradlew |
JAVA_HOME not exported. See Build and run above. |
| HUD activity does not start on the projector | The system blocks launchDisplayId for unsigned apps on stock Android. The Deepal S09 head unit allows it; on a regular phone use the virtual overlay above and pick that display id in the picker. |
wm density 72 -d <id> exits non-zero |
Requires root or a system-signed APK. Safe to ignore on a regular phone, only relevant on the actual car. |
Personal project, no license declared. Yandex MapKit usage is governed by the Yandex Developer Terms (https://yandex.com/legal/mapkit_sdk_agreement/).