mirror of
https://github.com/tiennm99/loto.git
synced 2026-10-15 22:14:55 +00:00
feat: initial Android port of tiennm99/loto
Native Android port of the SvelteKit Lô tô hội chợ Tân Tân web app.
Stack:
- Kotlin 2.1 + Jetpack Compose + Material 3
- Single Activity, single Gradle module (:app)
- Audio: AndroidX Media3 ExoPlayer with bundled MP3 voice clips
- Settings: DataStore Preferences with legacy masterMode migration
- minSdk 24, targetSdk 35, JDK 17
Game logic:
- 9x9 player card with exactly 5 numbers per row AND per column
- Soft constraint: no 3 consecutive filled columns (rejection-sampled)
- 11x9 ones-digit-aligned master tracking board
- Bingo / Cho ("waiting") state machine ported from PlayerBoard.svelte
- Forward-only auto-tick from MasterPanel via app-scoped CallBus
Audio:
- 184 MP3s pre-generated with Microsoft Edge TTS (Hoai My, Nam Minh)
- ExoPlayer playlist for cho + N gapless sequence
- Token-based cancellation matching the web voice.js semantics
CI:
- build-debug.yml: lint + test + assembleDebug on push/PR
- release.yml: signed AAB+APK on v* tag, env-driven keystore
This commit is contained in:
1 parent
36f2f41986
commit
90a9dd1d1a
255 files changed
+4790
No files matched your search
@@ -0,0 +1,122 @@
|
||||
# Lô tô — Android
|
||||
|
||||

|
||||
|
||||
Native Android port of [tiennm99/loto](https://github.com/tiennm99/loto) (SvelteKit web app).
|
||||
|
||||
Lô tô hội chợ TN1 — Vietnamese fairground bingo with host (quản trò) + player modes,
|
||||
called-number speech in Vietnamese (Hoài My / Nam Minh voices).
|
||||
|
||||
## Stack
|
||||
|
||||
- Kotlin 2.1 + Jetpack Compose + Material 3
|
||||
- Single Activity, single Gradle module (`:app`)
|
||||
- Audio: AndroidX Media3 ExoPlayer (bundled MP3s in `assets/audio/`)
|
||||
- Settings persistence: DataStore Preferences
|
||||
- minSdk 24 · targetSdk 35 · JDK 17
|
||||
|
||||
## Build
|
||||
|
||||
### Debug APK (no signing required)
|
||||
|
||||
```bash
|
||||
./gradlew :app:assembleDebug
|
||||
# Output: app/build/outputs/apk/debug/app-debug.apk
|
||||
```
|
||||
|
||||
### Install on connected device/emulator
|
||||
|
||||
```bash
|
||||
./gradlew :app:installDebug
|
||||
```
|
||||
|
||||
### Open in Android Studio
|
||||
|
||||
Open the repo root in Android Studio Ladybug (2024.2) or newer. The IDE picks
|
||||
up the Gradle wrapper automatically. Run the `app` configuration on any API 24+
|
||||
device or emulator.
|
||||
|
||||
## Tests
|
||||
|
||||
### Unit tests (JVM, no device needed)
|
||||
|
||||
```bash
|
||||
./gradlew :app:test
|
||||
```
|
||||
|
||||
Covers: `GameLogicTest`, `VietnameseNumberTest`, `VoiceManifestTest`,
|
||||
`SettingsRepositoryTest`, `CallBusTest`, `PlayerBoardViewModelTest`,
|
||||
`MasterPanelViewModelTest`, `MasterBoardLayoutTest`.
|
||||
|
||||
### Instrumentation tests (requires connected device or emulator)
|
||||
|
||||
```bash
|
||||
./gradlew :app:connectedDebugAndroidTest
|
||||
```
|
||||
|
||||
Includes `MainActivityComposeSmokeTest` — cold-start → generate grid → assert 81 cells.
|
||||
|
||||
## Release signing
|
||||
|
||||
Release builds read signing credentials from environment variables so the
|
||||
keystore is never committed to the repository.
|
||||
|
||||
```bash
|
||||
export LOTO_KEYSTORE_PATH=/path/to/loto-release.jks
|
||||
export LOTO_KEYSTORE_PASSWORD=<store-password>
|
||||
export LOTO_KEY_ALIAS=<key-alias>
|
||||
export LOTO_KEY_PASSWORD=<key-password>
|
||||
|
||||
./gradlew :app:assembleRelease # signed APK
|
||||
./gradlew :app:bundleRelease # signed AAB for Play Store
|
||||
```
|
||||
|
||||
Local convention: store the keystore at `~/.android/loto-release.jks`.
|
||||
|
||||
### GitHub Secrets (for CI)
|
||||
|
||||
Set the following secrets in the repository settings under **Settings → Secrets → Actions**:
|
||||
|
||||
| Secret | Description |
|
||||
|--------|-------------|
|
||||
| `KEYSTORE_BASE64` | `base64 -w0 loto-release.jks` output |
|
||||
| `KEYSTORE_PASSWORD` | Keystore password |
|
||||
| `KEY_ALIAS` | Key alias inside the keystore |
|
||||
| `KEY_PASSWORD` | Key password |
|
||||
|
||||
**Never commit `keystore.jks`, `.env`, or any file containing these values.**
|
||||
|
||||
## CI / CD
|
||||
|
||||
| Workflow | Trigger | Tasks |
|
||||
|----------|---------|-------|
|
||||
| `build-debug` | push to `main`, any PR | `lint test assembleDebug` |
|
||||
| `release` | tag `v*.*.*` | `bundleRelease assembleRelease` + sign + upload to GH Release |
|
||||
|
||||
Tag a release:
|
||||
|
||||
```bash
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
```
|
||||
|
||||
The `release` workflow will sign and attach the APK + AAB to the GitHub Release page.
|
||||
|
||||
## Version bump
|
||||
|
||||
1. Increment `versionCode` and `versionName` in `app/build.gradle.kts`.
|
||||
2. Commit, tag, push tag.
|
||||
|
||||
## App ID
|
||||
|
||||
`com.miti99.loto` — registered in `AndroidManifest.xml` and `app/build.gradle.kts`.
|
||||
|
||||
## Audio
|
||||
|
||||
Bundled MP3 voice clips under `assets/audio/{hoai-my,nam-minh}/{1..90,cho,kinh}.mp3`.
|
||||
Generated from the upstream repo's `scripts/generate-audio.py` using Microsoft Edge TTS.
|
||||
Re-generate by running that script against a Python 3.10+ environment with `edge-tts` installed.
|
||||
|
||||
## License
|
||||
|
||||
Same license as upstream [`tiennm99/loto`](https://github.com/tiennm99/loto).
|
||||
Reference in new issue
Block a user