Repository navigation
Building from Source
| Tool | Version |
|---|---|
| Android Gradle Plugin | 8.13.2 |
| Gradle | 8.13 |
| Kotlin | 2.1.0 (+ Compose compiler plugin) |
| compileSdk / targetSdk / minSdk | 36 / 36 / 29 (Android 16 target, as Google Play requires; runs on Android 10) |
| JDK for Gradle | 17–21 (not 25) |
If Android Studio says Java 25 is not supported: Settings → Build Tools → Gradle → Gradle JDK → download 21, re-sync.
The app and the companion each have two product flavors: github (the full app, com.openauto.dash) and play (Google Play: no root, ADB or PMPatch3 tooling, com.dashwheel.app / com.dashwheel.companion). Gate code on Edition.play. Build tasks carry the flavor: assembleGithubDebug, assemblePlayDebug, testGithubDebugUnitTest…
Dependencies come from Google and Maven Central, plus JitPack for one library only (com.github.mik3y:usb-serial-for-android, the USB OBD drivers).
| Module | |
|---|---|
app/ |
The launcher |
companion/ |
The phone app |
link/ |
Shared protocol: messages, pairing, encrypted channel, car-photo engine (plain Kotlin, unit-tested) |
display/ |
The Raspberry Pi second-screen service (plain JVM) |
tools/ |
Pi installer, translation check, marketing screenshots, car-pack baker |
Dashwheel/
├── build.gradle.kts # Root build: AGP 8.13.2 + Kotlin 2.1.0 plugin versions
├── settings.gradle.kts # Project + repositories
├── gradle.properties # AndroidX / Gradle flags
├── gradlew, gradlew.bat # Gradle wrapper scripts
├── gradle/wrapper/gradle-wrapper.properties # Pins Gradle 8.13 (jar generated on first sync/CI)
├── app/
│ ├── build.gradle.kts # Module dependencies & Android config
│ ├── proguard-rules.pro
│ └── src/main/
│ ├── AndroidManifest.xml # Launcher + HOME filters, permissions, services
│ ├── java/com/openauto/dash/
│ │ ├── MainActivity.kt # Entry point, wake flags, multi-window tracking
│ │ ├── AutomotiveDashboard.kt # Root composable: pages, undo, OBD loop, dialogs
│ │ ├── DashboardModel.kt # Grid model, DashboardItem/BuiltinKind, persistence
│ │ ├── DashboardGrid.kt # Grid rendering, drag-move / drag-resize gestures
│ │ ├── AddSheet.kt / WidgetCatalog.kt # "+" add sheet (live widget previews, apps, windows) + the catalogue's names/icons
│ │ ├── AppLauncher.kt / AppTiles.kt # App enumeration + full-screen app drawer
│ │ ├── TopBar.kt # Minimal bar: Apps, layout menu, clock, OBD dot, ⋮ menu
│ │ ├── DashTheme.kt / DashThemePickerDialog.kt # 12 palettes (incl. Aurora Glass) + picker UI
│ │ ├── Skins.kt / SkinKit.kt # Skin dispatch (background, top bar, widgets, Maps frame) + shared live values
│ │ ├── OrbitSkin.kt / CockpitSkin.kt / HorizonSkin.kt / TapeDeckSkin.kt # The four whole-design skins
│ │ ├── WindowFrameOverlay.kt # Skin frame drawn in an overlay window over the docked Google Maps window
│ │ ├── OriginalTiles.kt # Legacy widget renderers used by the "Original" theme
│ │ ├── MapLibrePanel.kt # In-app MapLibre GL navigator (free, no API key)
│ │ ├── DirectionsTile.kt / NavDirections.kt # Google Maps/Waze turn-by-turn via notification parsing
│ │ ├── ObdBluetoothManager.kt / ObdParser.kt / ObdCodes.kt # ELM327 link, pure reply decoding, DTC table
│ │ ├── McuReader.kt # Rooted CANbox reader: doors, learned fuel mapping
│ │ ├── TelemetryTiles.kt / VehicleTiles.kt / DriveTiles.kt # OBD/CANbox/driving-instrument cards
│ │ ├── CarMediaController.kt / MediaTile.kt # MediaSessionManager bridge + media card
│ │ ├── MediaNotificationListenerService.kt # Shared listener: media + directions + notifications
│ │ ├── InfoTiles.kt / WeatherRepo.kt # Clock/weather/calendar/dial/notifications/audio cards
│ │ ├── SystemWidgetPanel.kt # Hosts real Android AppWidgets inside dashboard tiles
│ │ ├── SplitLauncher.kt / SplitAccessibilityService.kt # System split-screen + pane swap
│ │ ├── AdbInstaller.kt / SystemInstaller.kt # Optional priv-app self-install (Magisk / root ADB)
│ │ ├── PhoneLink.kt / PhoneLinkUi.kt # Phone link: dials the companion app on the hotspot, reply sheet, pairing QR
│ │ ├── PhoneCallOverlay.kt # The phone's calls: caller card with answer / decline / hang up, over any app
│ │ ├── DriveLog.kt # Drive log: each trip + its eco-driving score, saved and sent to the phone
│ │ ├── UpdateManager.kt # GitHub Releases auto-update
│ │ └── AutoDriveReceiver.kt # Auto-launch on Bluetooth connect (see Troubleshooting)
│ └── res/
│ ├── drawable/ # Vector icons + adaptive-icon layers
│ ├── mipmap-anydpi-v26/ic_launcher.xml # Adaptive launcher icon (+ ic_launcher_round)
│ ├── values/ # colors.xml, themes.xml, strings.xml
│ └── xml/ # file_paths.xml, split_accessibility_config.xml
│ └── src/test/java/com/openauto/dash/ # JVM unit tests: grid, OBD decoding, directions, layout JSON
├── companion/ # Dashwheel Companion, the phone app (notification listener + link server)
├── link/ # Plain-Kotlin protocol shared by both apps: messages, pairing, encrypted channel (+ tests)
└── .github/workflows/ # GitHub Actions CI/CD
├── build.yml # Builds + releases the APK on push to main
└── lint.yml # Android Lint + unit tests
graph TB
A[MainActivity] --> B[AutomotiveDashboard]
B --> C[DashboardGrid: 7 dashboards, 12x7 grid]
C --> D[DashboardStore: move/resize/undo]
B --> E[WidgetCatalog: 32 builtin widgets]
E --> F[Navigation: MapLibrePanel + DirectionsTile]
E --> G[Vehicle: OBD + CANbox telemetry]
E --> H[Driving: speed/compass/trip/g-force/parking]
E --> I[Info: clock/weather/calendar/dial/notifications/audio]
E --> J[Media: CarMediaController]
F --> L[NavDirections: parses Maps/Waze notification]
G --> M[ObdBluetoothManager: ELM327 RFCOMM]
G --> N[McuReader: rooted CANbox log tail]
J --> O[MediaSessionManager]
L --> P[MediaNotificationListenerService]
O --> P
B --> Q[SplitLauncher / SplitAccessibilityService]
Q --> R[System split-screen + pane swap]
B --> S[SystemWidgetPanel: hosts real AppWidgets]
B --> T[UpdateManager: GitHub Releases]
B --> U[SystemInstaller / AdbInstaller: optional priv-app install]
gradle wrapper --gradle-version 8.13 # once: the wrapper jar isn't committed
./gradlew assembleGithubDebug # launcher + companion, GitHub edition
./gradlew assemblePlayDebug # the Google Play edition
./gradlew test # unit tests, both editions
./gradlew :display:installDist # the Pi service-
.github/workflows/build.ymlbuilds the release APKs on every push tomainand publishes a GitHub Releasev1.0.<run number>; the commit messages become the release notes. -
.github/workflows/lint.ymlruns Android Lint and the unit tests for both editions. -
.github/workflows/pi-image.ymlbuilds the ready-made Raspberry Pi SD card image for the second screen. - Releases carry the launcher and companion APKs (GitHub edition) only; the Play edition is a build download for the Play Console.
An update installs only over an APK signed with the same key. Add these repository secrets so CI signs with a stable key:
keytool -genkeypair -v -keystore release.keystore -alias openauto -keyalg RSA -keysize 2048 -validity 10000
base64 -w0 release.keystore > release.keystore.b64KEYSTORE_BASE64, KEYSTORE_PASSWORD, KEY_ALIAS (openauto), KEY_PASSWORD. Without them CI falls back to a debug key.
Dashwheel is free software (GPL v3). Enjoying it? ☕ Buy me a coffee on Ko-fi · Releases · Issues
Getting started
Dashboard
Car
Road
Phone and screens
More