tahamajs/CPS / Files /ComputerAssignments /CA2 /code /android /docs /MODERNIZATION_SPEC.md
tahamajs's picture
|
download
raw
10.8 kB

Air Mouse — Modernization Specification

Goal: migrate the Android app to a modern, modular, maintainable architecture using current Android best practices: Jetpack Compose, Kotlin coroutines & Flow, Hilt DI, DataStore for preferences, kotlinx-serialization for network messages, and scoped modules for sensors/network/ui so the PC server and Android client interoperate cleanly.

This document contains: high-level architecture, module layout, suggested Gradle/plugin versions (guidance), concrete dependency lists, networking protocol recommendation, testing & CI suggestions, and a migration checklist.


1. High-level Architecture

  • Multi-module project:

    • :app — minimal Android app, Compose UI, navigation, runtime wiring, DI entry points.
    • :core — shared utilities, models, serialization declarations (kotlinx-serialization), common exceptions.
    • :sensors — sensor handling, flows for sensor streams, calibration logic, Madgwick filter implementation (Kotlin), unit-tested.
    • :network — TCP client (coroutine-based), UDP discovery client, ack/retransmit logic, reconnection logic.
    • :ui — Compose screens and components (optionally :ui:compose submodule), theming, accessibility helpers.
    • :data — DataStore wrappers, preferences repository, migrations.
  • DI: Hilt to provide singletons and scoped objects (e.g., SensorManager wrappers, Network client, Repositories).

  • Concurrency: Kotlin Coroutines and Flow for streams and one-off tasks. Use structured concurrency and a clear set of Dispatchers (IO, Default, Main).

  • Serialization: kotlinx-serialization (JSON) for network messages; define stable DTOs in :core.

  • Testing: unit tests for logic (JVM), instrumented tests for sensors/permission flows, Compose UI tests. Use MockK / Turbine for Flow tests.

2. Why these choices

  • Compose: modern declarative UI, smaller code surface, easier accessibility & animations.
  • Hilt: minimal DI boilerplate and standard for Android ecosystem.
  • Coroutines + Flow: natural fit for streaming sensor data and async networking.
  • DataStore: coroutine-friendly replacement for SharedPreferences.
  • kotlinx-serialization: type-safe message serialization and compatibility across Kotlin platforms.

3. Minimum compatibility / plugin guidance (starting point)

  • Android Gradle Plugin (AGP): 8.1+ (match your local setup); example: com.android.application 8.1.0+.
  • Kotlin: 1.9.x (match Kotlin plugin); example: org.jetbrains.kotlin.jvm/kotlin-android 1.9.22 if available.
  • Compose: version compatible with Kotlin 1.9.x — e.g., Compose 1.5.x or later. Use Compose BOM to keep versions in sync.
  • Java/Kotlin compatibility: target Java 11.

Note: Always validate AGP ↔ Kotlin ↔ Compose compatibility matrix before pinning exact versions. If you want, I can patch build.gradle with exact pins that match your environment.

4. Core dependencies (per-module suggestions)

:app (Gradle Kotlin DSL snippet)

  • plugins block:

    • com.android.application
    • org.jetbrains.kotlin.android
    • dagger.hilt.android.plugin
    • kotlinx-serialization plugin (if needed)
  • dependencies:

    • implementation platform("androidx.compose:compose-bom:")
    • implementation "androidx.compose.ui:ui"
    • implementation "androidx.compose.material:material"
    • implementation "androidx.compose.ui:ui-tooling-preview"
    • implementation "androidx.navigation:navigation-compose"
    • implementation "com.google.dagger:hilt-android:2.47" (or latest Hilt)
    • kapt "com.google.dagger:hilt-compiler:2.47"
    • implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" (or latest)
    • implementation "androidx.datastore:datastore-preferences:1.1.0"
    • implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0"
    • implementation project(":network")
    • implementation project(":sensors")
    • testImplementation "junit:junit:4.13.2"
    • androidTestImplementation "androidx.test.ext:junit:1.1.5"
    • androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1"
    • debugImplementation "androidx.compose.ui:ui-tooling"

:core

  • implementation "org.jetbrains.kotlinx:kotlinx-serialization-json"
  • define DTOs and shared constants. Keep this module pure Kotlin (no Android SDK) where possible to ease JVM tests.

:network

  • implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core"
  • implementation "org.jetbrains.kotlinx:kotlinx-serialization-json"
  • (Optional) implementation "io.ktor:ktor-client-cio" if you prefer Ktor for TCP/UDP abstractions.
  • Provide a coroutine-friendly TCP client wrapper and UDP discovery client that returns Flows/SharedFlows for events.

:sensors

  • implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core"
  • Provide Sensor wrappers using callbackFlow: sensor events -> callbackFlow -> map/filter -> shareIn/StateFlow for UI consumption.
  • Implement Madgwick filter in Kotlin; expose Flow<Orientation> from sensor pipeline.

:data

  • implementation "androidx.datastore:datastore-preferences"
  • implementation "androidx.datastore:datastore-core" (if protobuf desired)

5. Networking protocol (recommended canonical format)

Use newline-delimited JSON messages with stable DTOs (kotlinx-serialization). Example messages from phone -> PC:

  • Move: { "type": "move", "dx": 1.23, "dy": -0.44 }

  • Click: { "type": "click", "id": "uuid-or-seq" }

Server -> phone for ACK: { "type": "ack", "id": "..." }

Discovery protocol: UDP broadcast message AIRMOUSE_DISCOVER (ASCII). Server replies with a JSON discovery response containing type:discovery_response, ip, port, mDNS optional.

Rationale: newline-delimited JSON is easy to implement in Kotlin coroutines with BufferedSource (Okio) or kotlinx-serialization + Ktor/Socket wrappers.

6. Sensor pipeline (recommended)

  • Use callbackFlow for Android SensorEventListener.
  • Map raw sensor values into timestamped Kotlin data classes in :sensors.
  • Combine accelerometer+gyro+(magnetometer optional) into a single Flow<Orientation> using combine and Madgwick algorithm running on Dispatchers.Default.
  • Expose processed orientation to UI via StateFlow.
  • Calibration API: expose suspend functions to run calibration steps and emit progress through Flows.

7. ACK, retransmit and reliability

  • Attach a short id (UUID or increasing sequence) to click/scroll packets.
  • Maintain a small in-memory map of pending messages with a retry TTL and backoff (e.g., 3 retries, 500ms initial timeout).
  • When ACK arrives (matching id), cancel retries.
  • Implement this logic inside :network using coroutines (Channels/Flows) rather than raw threads.

8. DI / Hilt setup

  • Provide module bindings for:
    • @Singleton fun provideDataStore(...)
    • @Singleton fun provideNetworkClient(...)
    • @Singleton fun provideSensorRepository(...)
    • @Singleton fun provideMadgwickFilter(...)
  • Use @HiltAndroidApp on Application and @AndroidEntryPoint for Activities/Fragments/Compose entry points.

9. Testing & tooling

  • Unit tests: use JUnit + MockK + Turbine (for flows).
  • Instrumentation: Robolectric for JVM-level sensors, Android instrumentation for lifecycle and permission flows.
  • Linting: add ktlint and detekt configuration.
  • CI: GitHub Actions matrix => runs ./gradlew build (assembleDebug), run unit tests, run lint; optionally run emulator-based integration later.

10. Migration checklist (practical order)

  1. Add Kotlin, AGP, and Hilt plugins to build.gradle and update top-level Gradle config.
  2. Create modules: :core, :network, :sensors, :data, :ui (or :app with ui package) and move existing files into logical places.
  3. Implement core DTOs with @Serializable classes for messages.
  4. Implement network coroutine TCP client and UDP discovery client with tests.
  5. Implement sensors flows, Madgwick conversion, and calibration APIs.
  6. Replace SharedPreferences usage with DataStore wrapper and PreferencesManager refactor.
  7. Replace existing XML UI with Compose screens incrementally (start with NetworkDiscoveryFragment -> NetworkDiscoveryScreen.
  8. Add Hilt modules and integrate DI.
  9. Add tests and CI.
  10. Polish: theming, accessibility, small UX flows, and final performance testing.

11. Example Gradle snippet (top-level build.gradle)

buildscript {
    repositories { google(); mavenCentral() }
}

plugins {
    id 'com.android.application' version '8.1.0' apply false
    id 'org.jetbrains.kotlin.android' version '1.9.22' apply false
    id 'com.google.dagger.hilt.android' version '2.47' apply false
}

And example app/build.gradle (Kotlin DSL users please adapt):

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'com.google.dagger.hilt.android'
}

android {
    namespace 'com.airmouse'
    compileSdk 34
    defaultConfig {
        applicationId 'com.airmouse'
        minSdk 23
        targetSdk 34
        versionCode 1
        versionName '1.0'
    }
    compileOptions { sourceCompatibility JavaVersion.VERSION_11; targetCompatibility JavaVersion.VERSION_11 }
    kotlinOptions { jvmTarget = '11' }
    buildFeatures { compose true }
    composeOptions { kotlinCompilerExtensionVersion '1.5.0' }
}

dependencies {
    implementation platform('androidx.compose:compose-bom:2024.08.00')
    implementation 'androidx.compose.ui:ui'
    implementation 'androidx.compose.material:material'
    implementation 'androidx.navigation:navigation-compose:2.7.0'
    implementation 'com.google.dagger:hilt-android:2.47'
    kapt 'com.google.dagger:hilt-compiler:2.47'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
    implementation 'androidx.datastore:datastore-preferences:1.1.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0'
    // project modules
    implementation project(':core')
    implementation project(':network')
    implementation project(':sensors')
}

(These numbers are suggestions; verify compatibility with your AGP/Kotlin toolchain.)

12. Next steps I can take now

  • Patch settings.gradle and top-level build.gradle to add submodules and plugins.
  • Create :core module with DTOs (Message.kt) and sample serialization tests.
  • Scaffold :network with coroutine TCP client and UDP discovery client.

If you'd like, I can immediately scaffold the modules and implement the core DTOs and the JSON message contracts.


Would you like me to scaffold the modules and add the Message.kt DTOs now? If yes, choose: scaffold core or scaffold network or scaffold sensors and I'll begin implementing that module and wiring Gradle.

Xet Storage Details

Size:
10.8 kB
·
Xet hash:
ef697f19dbf799c9338244db2b2ea6dc6c034373116609926bd0f57dd1f5fe0d

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.