NUSV

NUSV LITE — technical documentation

The official NUSV Android client. Jetpack Compose + Material 3, Room-backed content store, and a versioned sync pipeline that keeps the app's content fresh without app updates.

01 Stack & targets

ItemDetail
TargetAndroid only — minSdk 26 (Android 8.0), target/compile SDK 36
UIJetpack Compose (BOM 2026.06.00) + Material 3 + material-icons-extended
PersistenceRoom 2.8.4 (via KSP) — categories / items / docs tables
NetworkOkHttp (content sync, update check)
Serializationkotlinx.serialization (JSON, ignoreUnknownKeys)
Navigationandroidx.navigation-compose 2.9.8; lifecycle-viewmodel-compose 2.10.0
Release buildR8 minification (isMinifyEnabled) with a custom proguard-rules.pro
LicenseMIT

02 Architecture & code map

Single app module with the classic layer split. UI is plain Compose (no ViewModels for the games/tools — state is local to each screen); the content hub goes through Room-backed repositories.

data/AppDatabase (Room), ContentLoader (asset seeding), SyncManager (remote sync + update check)
model/@Serializable JSON DTOs and @Entity tables: ContentJson/ItemJson/CategoryJson, DocsJson/DocJson, UpdateInfo
repository/AppRepository — single access point for DB reads/writes
ui/navigationNusvNavHost — app routes
ui/screensHome, Browse, Discover, Search, Detail, Docs, DocDetail, ThemeShop, Settings, Achievements
ui/screens/minigames11 games incl. Gomoku (threat-aware AI, two difficulties), Tetris, Sudoku, Minesweeper, 2048, Snake, Wordle
ui/screens/tools60+ utilities across Everyday / Dev / Creative categories
ui/componentsGlassCard, NusvChip — shared visual language

03 Content pipeline (offline → sync)

Content flows in two stages, so the app is fully usable offline and only downloads what changed:

  • Seeding (first launch): ContentLoader.loadIfNeeded() reads bundled assets content.json and docs.json from the APK and inserts them into Room (skipped if the tables already have rows).
  • Sync (on demand): SyncManager.syncAll() fetches the same files from a remote base URL. Each file carries an integer version; the app stores the last applied version in SharedPreferences("sync_meta") and only replaces its DB rows when remote.version > localVersion. The default base URL is raw.githubusercontent.com/NUSV/nusv-lite-sync/main and can be overridden per-install in settings.
  • Update check: the same sync URL serves version.json (UpdateInfo { latestVersion, downloadUrl, changelog }); the Detail screen shows an update action when the remote version is newer.

The versioned replace strategy means sync is destructive but atomic per file: categories/items are wiped and re-inserted in one transaction per successful fetch. Errors (timeouts, HTTP failures) return SyncResult.ERROR and never touch local data.

04 JSON & database schemas

content.json

{
  "version": 12,                          // bump to trigger a client refresh
  "updatedAt": "2026-08-01T00:00:00Z",
  "categories": [{ "id": "news", "name": "News", "color": "#22d3ee" }],
  "items": [{
    "id": "post-1",
    "title": "…", "description": "…", "url": "…",
    "categoryId": "news",
    "tags": ["nusv"],
    "isFeatured": false,
    "createdAt": "2026-08-01T00:00:00Z"
  }]
}

docs.json

{
  "version": 3,
  "updatedAt": "2026-08-01T00:00:00Z",
  "docs": [{ "id": "d1", "title": "…", "content": "…", "createdAt": "…" }]
}

version.json

{
  "latestVersion": "1.10.0",
  "downloadUrl": "https://raw.githubusercontent.com/NUSV/nusv-lite-sync/main/NUSV-LITE-v1.10.0-alpha.apk",
  "changelog": "auto deploy 2026-08-01"
}

Room mirrors these as three tables — categories, items, docs — with colors converted to Long and createdAt to epoch millis; tags are flattened to a comma-joined string.

05 Modules: content, games, tools

  • Content hub: Browse (categorized), Discover (featured), Search (tags + titles), Detail (deep link + update check), Docs + DocDetail.
  • 11 games: Tic-Tac-Toe, 2048, Minesweeper, Memory Match, Snake, Wordle, Simon Says, Whack-a-Mole, Tetris, Gomoku (vs a threat-aware AI with two difficulty levels) and Sudoku. Wins earn points; per-game high scores are persisted.
  • 60+ tools across Everyday (calculators, converters, timers, world clock), Dev (JSON formatter, Base64, hash, UUID, converters, regex tester, Markdown preview, QR generator, text encryption) and Creative (drawing pad, kaomoji keyboard, biorhythm, Morse, metronome, breathing exercise).
  • Personalization: ThemeShop (themes), widgets, and a daily check-in points system (see repository/AppRepository.kt).

06 Deploy pipeline

scripts/deploy-sync.sh stages a sync release locally:

  1. Builds the debug APK (./gradlew assembleDebug).
  2. Copies the latest content.json / docs.json from assets and the new APK into deploy/.
  3. Generates version.json from the versionName in app/build.gradle.kts (APK name pattern NUSV-LITE-v{version}-alpha.apk).
  4. Commits and pushes deploy/ to the nusv-lite-sync repository, which the clients poll.

APK releases are attached to GitHub Releases; the sync repository itself is content infrastructure, not a download portal.

07 Building from source

./gradlew assembleDebug          # debug APK
./gradlew assembleRelease        # minified release APK (R8 + proguard-rules.pro)

Prerequisites: JDK 17+, Android SDK platform 36. Output APKs land in app/build/outputs/apk/<flavor>/.