Files
samplez/rippr-src/docs/v1/original-spec.md
uhryniuk 247b9cdb3f Refresh Rippr snapshot and bundle with full project documentation
Re-exported at 46a0726, which adds README.md plus docs/ARCHITECTURE,
DEVELOPMENT, TESTING, v1 history including the original brief, and a v3
backlog. 112 files, and the bundle now carries 19 commits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 08:44:48 -05:00

4.0 KiB

Original v1 brief (verbatim)

Preserved as written, before any of it was implemented or corrected. See README.md for which parts were changed and why — several would not have compiled or would have dropped GPS fixes under load.

The project was called MotoTrack at this point, with package com.example.mototrack. Both were renamed to Rippr / com.rippr during development.


Project Specification: MotoTrack Single-Session Android Recorder

Goal: Build a bare-bones, highly resilient Native Android app (Kotlin) to record GPS telemetry during motorcycle group rides today.

Design Priority: Unbreakable execution over UI beauty. Must run as an un-killable Foreground Service that records telemetry to a local SQLite database and streams updates over HTTP/REST when online.

1. Setup & Installation

Prerequisites: Android Studio (Koala/Ladybug+), a physical Android phone on 8.0+ (API 26+) with USB debugging on, and a cable.

Project setup: New Project → Empty Activity (Jetpack Compose); Name MotoTrack; package com.example.mototrack; Minimum SDK API 26; Kotlin.

Dependencies specified:

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    id("kotlin-kapt")
}

dependencies {
    implementation("com.google.android.gms:play-services-location:21.2.0")
    val roomVersion = "2.6.1"
    implementation("androidx.room:room-runtime:$roomVersion")
    implementation("androidx.room:room-ktx:$roomVersion")
    kapt("androidx.room:room-compiler:$roomVersion")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")
    implementation("androidx.compose.material3:material3:1.2.1")
}

2. Task breakdown

Task 1 — Manifest & permissions. ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, FOREGROUND_SERVICE, FOREGROUND_SERVICE_LOCATION, POST_NOTIFICATIONS, INTERNET; MainActivity exported with a LAUNCHER intent filter; TrackingService declared with android:foregroundServiceType="location".

Task 2 — Local storage (Room). TrackPoint entity with id, timestamp, latitude, longitude, speedKmh, altitudeM. TrackPointDao with insertPoint, getAllPoints, getMaxSpeed, getPointCount. AppDatabase at version 1 with a @Volatile singleton.

Task 3 — Un-killable foreground service. TrackingService holding a FusedLocationProviderClient, a CoroutineScope(Dispatchers.IO + SupervisorJob()), and a LocationCallback that converts loc.speed * 3.6f to km/h and launches a coroutine per fix to insertPoint. startForeground with FOREGROUND_SERVICE_TYPE_LOCATION on Q+, START_STICKY, a LocationRequest at PRIORITY_HIGH_ACCURACY / 1000 ms with a 500 ms minimum interval, and an IMPORTANCE_LOW notification channel.

Task 4 — Simple UI & controller. MainActivity with mutableStateOf fields for isRecording, maxSpeed and pointCount; a RequestMultiplePermissions launcher; a LaunchedEffect polling getMaxSpeed() and getPointCount() every second; and a single button toggling between START and STOP RECORDING.

3. Verification & deployment

  1. Attach device, click Run 'app' in Android Studio
  2. Grant location and notification permissions
  3. Tap START RECORDING; confirm the persistent notification appears
  4. Lock the screen and take a 1-minute test walk/drive
  5. Unlock: verify Max Speed and Points Captured are updating

Notable in hindsight

  • "Streams updates over HTTP/REST" appears in the goal but no task defines it. The uploader was built anyway, since it was clearly intended.
  • The 1 Hz polling loop in Task 4 was replaced by a Room Flow.
  • A coroutine launched per GPS fix (Task 3) offers no back-pressure guarantee; this became the unbounded Channel and single batched writer that still underpins recording.
  • super.onCreate() in Task 4 is missing its argument and would not compile.
  • The verification steps assume Android Studio. Everything ended up driven from the CLI instead — see ../DEVELOPMENT.md.