Skip to content

Building from Source

deviloufr-ai edited this page Oct 9, 2026 · 3 revisions

Building from source

Toolchain

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.

Two editions

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).

Modules

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

Source map

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

Architecture

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]
Loading

Build

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

CI and releases

  • .github/workflows/build.yml builds the release APKs on every push to main and publishes a GitHub Release v1.0.<run number>; the commit messages become the release notes.
  • .github/workflows/lint.yml runs Android Lint and the unit tests for both editions.
  • .github/workflows/pi-image.yml builds 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.

Release signing (needed for updates)

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.b64

KEYSTORE_BASE64, KEYSTORE_PASSWORD, KEY_ALIAS (openauto), KEY_PASSWORD. Without them CI falls back to a debug key.

Clone this wiki locally