From dab4f7e48c388a112ec3024fb7fa5d2ab7005ccb Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sun, 10 May 2026 01:31:18 +0700 Subject: [PATCH] feat: migrate to Capacitor wrapper for fully-offline Android Replace the native Kotlin/Compose port with a Capacitor 8 wrapper around the upstream loto SvelteKit PWA. Loto is consumed as a git submodule and its static build is bundled into the APK at sync time. The wrapper has no INTERNET permission, so fully offline is enforced rather than conventional. - loto/ submodule pinned to dfb1c1e - android/ scaffolded by cap add android; signing wired via env vars - All 184 voice MP3s + service worker bundled in assets/public/ - CI checkouts submodule, runs npm build, then Gradle inside android/ - Native port preserved in git history at fe52232 and 9a35686 --- .github/workflows/build-debug.yml | 20 +- .github/workflows/release.yml | 22 +- .gitignore | 40 +- .gitmodules | 3 + README.md | 174 +-- android/.gitignore | 101 ++ android/app/.gitignore | 2 + android/app/build.gradle | 71 + android/app/capacitor.build.gradle | 19 + android/app/proguard-rules.pro | 21 + .../myapp/ExampleInstrumentedTest.java | 26 + android/app/src/main/AndroidManifest.xml | 40 + .../java/com/miti99/loto/MainActivity.java | 5 + .../main/res/drawable-land-hdpi/splash.png | Bin 0 -> 7705 bytes .../main/res/drawable-land-mdpi/splash.png | Bin 0 -> 4040 bytes .../main/res/drawable-land-xhdpi/splash.png | Bin 0 -> 9251 bytes .../main/res/drawable-land-xxhdpi/splash.png | Bin 0 -> 13984 bytes .../main/res/drawable-land-xxxhdpi/splash.png | Bin 0 -> 17683 bytes .../main/res/drawable-port-hdpi/splash.png | Bin 0 -> 7934 bytes .../main/res/drawable-port-mdpi/splash.png | Bin 0 -> 4096 bytes .../main/res/drawable-port-xhdpi/splash.png | Bin 0 -> 9875 bytes .../main/res/drawable-port-xxhdpi/splash.png | Bin 0 -> 13346 bytes .../main/res/drawable-port-xxxhdpi/splash.png | Bin 0 -> 17489 bytes .../drawable-v24/ic_launcher_foreground.xml | 34 + .../res/drawable/ic_launcher_background.xml | 170 +++ android/app/src/main/res/drawable/splash.png | Bin 0 -> 4040 bytes .../app/src/main/res/layout/activity_main.xml | 12 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 2786 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 3450 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 4341 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1869 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 2110 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2725 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 3981 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 5036 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 6593 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6644 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 9793 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10455 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9441 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 15529 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15916 bytes .../res/values/ic_launcher_background.xml | 4 + android/app/src/main/res/values/strings.xml | 7 + android/app/src/main/res/values/styles.xml | 22 + android/app/src/main/res/xml/file_paths.xml | 5 + .../getcapacitor/myapp/ExampleUnitTest.java | 18 + android/build.gradle | 29 + android/capacitor.settings.gradle | 3 + android/gradle.properties | 22 + android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43764 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + android/gradlew | 251 ++++ android/gradlew.bat | 94 ++ android/settings.gradle | 5 + android/variables.gradle | 16 + app/build.gradle.kts | 112 -- app/proguard-rules.pro | 24 - .../loto/MainActivityComposeSmokeTest.kt | 46 - app/src/main/AndroidManifest.xml | 31 - app/src/main/assets/audio/hoai-my/1.mp3 | Bin 7344 -> 0 bytes app/src/main/assets/audio/hoai-my/10.mp3 | Bin 7920 -> 0 bytes app/src/main/assets/audio/hoai-my/11.mp3 | Bin 8928 -> 0 bytes app/src/main/assets/audio/hoai-my/12.mp3 | Bin 9360 -> 0 bytes app/src/main/assets/audio/hoai-my/13.mp3 | Bin 9072 -> 0 bytes app/src/main/assets/audio/hoai-my/14.mp3 | Bin 9216 -> 0 bytes app/src/main/assets/audio/hoai-my/15.mp3 | Bin 9504 -> 0 bytes app/src/main/assets/audio/hoai-my/16.mp3 | Bin 9216 -> 0 bytes app/src/main/assets/audio/hoai-my/17.mp3 | Bin 9504 -> 0 bytes app/src/main/assets/audio/hoai-my/18.mp3 | Bin 9360 -> 0 bytes app/src/main/assets/audio/hoai-my/19.mp3 | Bin 9216 -> 0 bytes app/src/main/assets/audio/hoai-my/2.mp3 | Bin 7776 -> 0 bytes app/src/main/assets/audio/hoai-my/20.mp3 | Bin 9072 -> 0 bytes app/src/main/assets/audio/hoai-my/21.mp3 | Bin 10368 -> 0 bytes app/src/main/assets/audio/hoai-my/22.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/23.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/hoai-my/24.mp3 | Bin 10368 -> 0 bytes app/src/main/assets/audio/hoai-my/25.mp3 | Bin 10368 -> 0 bytes app/src/main/assets/audio/hoai-my/26.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/hoai-my/27.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/28.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/29.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/hoai-my/3.mp3 | Bin 7776 -> 0 bytes app/src/main/assets/audio/hoai-my/30.mp3 | Bin 9216 -> 0 bytes app/src/main/assets/audio/hoai-my/31.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/hoai-my/32.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/33.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/hoai-my/34.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/35.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/36.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/37.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/38.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/39.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/4.mp3 | Bin 7776 -> 0 bytes app/src/main/assets/audio/hoai-my/40.mp3 | Bin 9504 -> 0 bytes app/src/main/assets/audio/hoai-my/41.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/hoai-my/42.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/43.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/44.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/45.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/46.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/47.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/hoai-my/48.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/49.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/5.mp3 | Bin 7776 -> 0 bytes app/src/main/assets/audio/hoai-my/50.mp3 | Bin 9504 -> 0 bytes app/src/main/assets/audio/hoai-my/51.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/hoai-my/52.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/hoai-my/53.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/54.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/55.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/56.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/57.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/hoai-my/58.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/59.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/6.mp3 | Bin 7776 -> 0 bytes app/src/main/assets/audio/hoai-my/60.mp3 | Bin 9504 -> 0 bytes app/src/main/assets/audio/hoai-my/61.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/62.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/hoai-my/63.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/64.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/hoai-my/65.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/66.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/67.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/hoai-my/68.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/hoai-my/69.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/7.mp3 | Bin 8208 -> 0 bytes app/src/main/assets/audio/hoai-my/70.mp3 | Bin 9504 -> 0 bytes app/src/main/assets/audio/hoai-my/71.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/72.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/73.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/74.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/hoai-my/75.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/76.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/hoai-my/77.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/hoai-my/78.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/79.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/8.mp3 | Bin 7632 -> 0 bytes app/src/main/assets/audio/hoai-my/80.mp3 | Bin 9216 -> 0 bytes app/src/main/assets/audio/hoai-my/81.mp3 | Bin 10368 -> 0 bytes app/src/main/assets/audio/hoai-my/82.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/83.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/84.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/85.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/hoai-my/86.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/87.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/hoai-my/88.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/hoai-my/89.mp3 | Bin 10368 -> 0 bytes app/src/main/assets/audio/hoai-my/9.mp3 | Bin 7488 -> 0 bytes app/src/main/assets/audio/hoai-my/90.mp3 | Bin 9072 -> 0 bytes app/src/main/assets/audio/hoai-my/cho.mp3 | Bin 7776 -> 0 bytes app/src/main/assets/audio/hoai-my/kinh.mp3 | Bin 7344 -> 0 bytes app/src/main/assets/audio/manifest.json | 16 - app/src/main/assets/audio/nam-minh/1.mp3 | Bin 8784 -> 0 bytes app/src/main/assets/audio/nam-minh/10.mp3 | Bin 8064 -> 0 bytes app/src/main/assets/audio/nam-minh/11.mp3 | Bin 9648 -> 0 bytes app/src/main/assets/audio/nam-minh/12.mp3 | Bin 9936 -> 0 bytes app/src/main/assets/audio/nam-minh/13.mp3 | Bin 9792 -> 0 bytes app/src/main/assets/audio/nam-minh/14.mp3 | Bin 10224 -> 0 bytes app/src/main/assets/audio/nam-minh/15.mp3 | Bin 9936 -> 0 bytes app/src/main/assets/audio/nam-minh/16.mp3 | Bin 10080 -> 0 bytes app/src/main/assets/audio/nam-minh/17.mp3 | Bin 10224 -> 0 bytes app/src/main/assets/audio/nam-minh/18.mp3 | Bin 10080 -> 0 bytes app/src/main/assets/audio/nam-minh/19.mp3 | Bin 9936 -> 0 bytes app/src/main/assets/audio/nam-minh/2.mp3 | Bin 8208 -> 0 bytes app/src/main/assets/audio/nam-minh/20.mp3 | Bin 9216 -> 0 bytes app/src/main/assets/audio/nam-minh/21.mp3 | Bin 10224 -> 0 bytes app/src/main/assets/audio/nam-minh/22.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/23.mp3 | Bin 10368 -> 0 bytes app/src/main/assets/audio/nam-minh/24.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/25.mp3 | Bin 10368 -> 0 bytes app/src/main/assets/audio/nam-minh/26.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/nam-minh/27.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/28.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/29.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/3.mp3 | Bin 8208 -> 0 bytes app/src/main/assets/audio/nam-minh/30.mp3 | Bin 9504 -> 0 bytes app/src/main/assets/audio/nam-minh/31.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/nam-minh/32.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/33.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/34.mp3 | Bin 11232 -> 0 bytes app/src/main/assets/audio/nam-minh/35.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/36.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/37.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/38.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/39.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/4.mp3 | Bin 8496 -> 0 bytes app/src/main/assets/audio/nam-minh/40.mp3 | Bin 9648 -> 0 bytes app/src/main/assets/audio/nam-minh/41.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/42.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/43.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/44.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/45.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/46.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/47.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/nam-minh/48.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/49.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/nam-minh/5.mp3 | Bin 8928 -> 0 bytes app/src/main/assets/audio/nam-minh/50.mp3 | Bin 9504 -> 0 bytes app/src/main/assets/audio/nam-minh/51.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/nam-minh/52.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/53.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/54.mp3 | Bin 11376 -> 0 bytes app/src/main/assets/audio/nam-minh/55.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/56.mp3 | Bin 11664 -> 0 bytes app/src/main/assets/audio/nam-minh/57.mp3 | Bin 11520 -> 0 bytes app/src/main/assets/audio/nam-minh/58.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/59.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/nam-minh/6.mp3 | Bin 8640 -> 0 bytes app/src/main/assets/audio/nam-minh/60.mp3 | Bin 9792 -> 0 bytes app/src/main/assets/audio/nam-minh/61.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/62.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/63.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/64.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/65.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/66.mp3 | Bin 11232 -> 0 bytes app/src/main/assets/audio/nam-minh/67.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/68.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/69.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/nam-minh/7.mp3 | Bin 8640 -> 0 bytes app/src/main/assets/audio/nam-minh/70.mp3 | Bin 9792 -> 0 bytes app/src/main/assets/audio/nam-minh/71.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/nam-minh/72.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/73.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/74.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/75.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/76.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/77.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/nam-minh/78.mp3 | Bin 11088 -> 0 bytes app/src/main/assets/audio/nam-minh/79.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/8.mp3 | Bin 8208 -> 0 bytes app/src/main/assets/audio/nam-minh/80.mp3 | Bin 9216 -> 0 bytes app/src/main/assets/audio/nam-minh/81.mp3 | Bin 10224 -> 0 bytes app/src/main/assets/audio/nam-minh/82.mp3 | Bin 10368 -> 0 bytes app/src/main/assets/audio/nam-minh/83.mp3 | Bin 10368 -> 0 bytes app/src/main/assets/audio/nam-minh/84.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/85.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/nam-minh/86.mp3 | Bin 10944 -> 0 bytes app/src/main/assets/audio/nam-minh/87.mp3 | Bin 10656 -> 0 bytes app/src/main/assets/audio/nam-minh/88.mp3 | Bin 10800 -> 0 bytes app/src/main/assets/audio/nam-minh/89.mp3 | Bin 10512 -> 0 bytes app/src/main/assets/audio/nam-minh/9.mp3 | Bin 8064 -> 0 bytes app/src/main/assets/audio/nam-minh/90.mp3 | Bin 9216 -> 0 bytes app/src/main/assets/audio/nam-minh/cho.mp3 | Bin 8064 -> 0 bytes app/src/main/assets/audio/nam-minh/kinh.mp3 | Bin 8064 -> 0 bytes app/src/main/java/com/miti99/loto/LotoApp.kt | 65 - .../main/java/com/miti99/loto/MainActivity.kt | 22 - .../com/miti99/loto/audio/VoiceManifest.kt | 28 - .../java/com/miti99/loto/audio/VoicePlayer.kt | 129 -- .../java/com/miti99/loto/game/GameLogic.kt | 166 --- .../com/miti99/loto/game/VietnameseNumber.kt | 38 - .../com/miti99/loto/settings/SettingsKeys.kt | 20 - .../loto/settings/SettingsRepository.kt | 141 -- .../com/miti99/loto/settings/SettingsState.kt | 34 - .../java/com/miti99/loto/state/CallBus.kt | 26 - .../java/com/miti99/loto/state/DeckState.kt | 15 - .../java/com/miti99/loto/state/DrawEvent.kt | 4 - .../java/com/miti99/loto/state/GameStorage.kt | 59 - .../miti99/loto/state/MasterPanelUiState.kt | 9 - .../miti99/loto/state/MasterPanelViewModel.kt | 119 -- .../miti99/loto/state/PlayerBoardUiState.kt | 27 - .../miti99/loto/state/PlayerBoardViewModel.kt | 277 ---- .../miti99/loto/state/SettingsViewModel.kt | 34 - .../com/miti99/loto/state/ViewModelFactory.kt | 38 - .../java/com/miti99/loto/ui/LotoAppRoot.kt | 170 --- .../java/com/miti99/loto/ui/LotoTopBar.kt | 31 - .../main/java/com/miti99/loto/ui/Wordmark.kt | 56 - .../java/com/miti99/loto/ui/board/ChoToast.kt | 75 -- .../java/com/miti99/loto/ui/board/Confetti.kt | 103 -- .../com/miti99/loto/ui/board/GhostPreview.kt | 104 -- .../com/miti99/loto/ui/board/KinhModal.kt | 116 -- .../miti99/loto/ui/board/PlayerBoardGrid.kt | 138 -- .../miti99/loto/ui/board/PlayerBoardScreen.kt | 176 --- .../com/miti99/loto/ui/board/PlayerCell.kt | 109 -- .../loto/ui/common/LocalEmptyCellColor.kt | 12 - .../miti99/loto/ui/master/CalledHistory.kt | 93 -- .../loto/ui/master/CurrentNumberHero.kt | 88 -- .../com/miti99/loto/ui/master/MasterBoard.kt | 47 - .../loto/ui/master/MasterBoardLayout.kt | 32 - .../com/miti99/loto/ui/master/MasterCell.kt | 110 -- .../miti99/loto/ui/master/MasterControls.kt | 118 -- .../loto/ui/master/MasterPanelScreen.kt | 154 --- .../loto/ui/settings/AutoCallControls.kt | 69 - .../loto/ui/settings/ColorSwatchTile.kt | 65 - .../loto/ui/settings/EmptyCellColorPicker.kt | 97 -- .../miti99/loto/ui/settings/ExcelPresets.kt | 48 - .../miti99/loto/ui/settings/RgbSliderRow.kt | 103 -- .../miti99/loto/ui/settings/SettingsSheet.kt | 154 --- .../loto/ui/settings/ThemeModePickers.kt | 72 - .../miti99/loto/ui/settings/VoicePicker.kt | 59 - .../miti99/loto/ui/settings/VoiceToggles.kt | 90 -- .../java/com/miti99/loto/ui/theme/Color.kt | 54 - .../java/com/miti99/loto/ui/theme/Theme.kt | 62 - .../java/com/miti99/loto/ui/theme/Type.kt | 41 - .../res/drawable/ic_launcher_background.xml | 5 - .../res/drawable/ic_launcher_foreground.xml | 15 - .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - app/src/main/res/values/strings.xml | 77 -- app/src/main/res/values/themes.xml | 12 - app/src/main/res/xml/backup_rules.xml | 15 - .../com/miti99/loto/game/GameLogicTest.kt | 166 --- .../miti99/loto/game/VietnameseNumberTest.kt | 34 - .../loto/settings/SettingsRepositoryTest.kt | 133 -- build.gradle.kts | 7 - capacitor.config.json | 11 + gradle.properties | 9 - gradle/libs.versions.toml | 75 -- loto | 1 + package-lock.json | 1165 +++++++++++++++++ package.json | 21 + ...ch-260510-0055-wrap-loto-as-android-app.md | 254 ++++ plans/todo.md | 100 +- settings.gradle.kts | 24 - 315 files changed, 2672 insertions(+), 4836 deletions(-) create mode 100644 .gitmodules create mode 100644 android/.gitignore create mode 100644 android/app/.gitignore create mode 100644 android/app/build.gradle create mode 100644 android/app/capacitor.build.gradle create mode 100644 android/app/proguard-rules.pro create mode 100644 android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/java/com/miti99/loto/MainActivity.java create mode 100644 android/app/src/main/res/drawable-land-hdpi/splash.png create mode 100644 android/app/src/main/res/drawable-land-mdpi/splash.png create mode 100644 android/app/src/main/res/drawable-land-xhdpi/splash.png create mode 100644 android/app/src/main/res/drawable-land-xxhdpi/splash.png create mode 100644 android/app/src/main/res/drawable-land-xxxhdpi/splash.png create mode 100644 android/app/src/main/res/drawable-port-hdpi/splash.png create mode 100644 android/app/src/main/res/drawable-port-mdpi/splash.png create mode 100644 android/app/src/main/res/drawable-port-xhdpi/splash.png create mode 100644 android/app/src/main/res/drawable-port-xxhdpi/splash.png create mode 100644 android/app/src/main/res/drawable-port-xxxhdpi/splash.png create mode 100644 android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 android/app/src/main/res/drawable/splash.png create mode 100644 android/app/src/main/res/layout/activity_main.xml create mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/values/ic_launcher_background.xml create mode 100644 android/app/src/main/res/values/strings.xml create mode 100644 android/app/src/main/res/values/styles.xml create mode 100644 android/app/src/main/res/xml/file_paths.xml create mode 100644 android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java create mode 100644 android/build.gradle create mode 100644 android/capacitor.settings.gradle create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100755 android/gradlew create mode 100644 android/gradlew.bat create mode 100644 android/settings.gradle create mode 100644 android/variables.gradle delete mode 100644 app/build.gradle.kts delete mode 100644 app/proguard-rules.pro delete mode 100644 app/src/androidTest/java/com/miti99/loto/MainActivityComposeSmokeTest.kt delete mode 100644 app/src/main/AndroidManifest.xml delete mode 100644 app/src/main/assets/audio/hoai-my/1.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/10.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/11.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/12.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/13.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/14.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/15.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/16.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/17.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/18.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/19.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/2.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/20.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/21.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/22.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/23.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/24.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/25.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/26.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/27.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/28.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/29.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/3.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/30.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/31.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/32.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/33.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/34.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/35.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/36.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/37.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/38.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/39.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/4.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/40.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/41.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/42.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/43.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/44.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/45.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/46.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/47.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/48.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/49.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/5.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/50.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/51.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/52.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/53.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/54.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/55.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/56.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/57.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/58.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/59.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/6.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/60.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/61.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/62.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/63.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/64.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/65.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/66.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/67.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/68.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/69.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/7.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/70.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/71.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/72.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/73.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/74.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/75.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/76.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/77.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/78.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/79.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/8.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/80.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/81.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/82.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/83.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/84.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/85.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/86.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/87.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/88.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/89.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/9.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/90.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/cho.mp3 delete mode 100644 app/src/main/assets/audio/hoai-my/kinh.mp3 delete mode 100644 app/src/main/assets/audio/manifest.json delete mode 100644 app/src/main/assets/audio/nam-minh/1.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/10.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/11.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/12.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/13.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/14.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/15.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/16.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/17.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/18.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/19.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/2.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/20.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/21.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/22.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/23.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/24.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/25.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/26.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/27.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/28.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/29.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/3.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/30.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/31.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/32.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/33.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/34.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/35.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/36.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/37.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/38.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/39.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/4.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/40.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/41.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/42.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/43.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/44.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/45.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/46.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/47.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/48.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/49.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/5.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/50.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/51.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/52.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/53.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/54.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/55.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/56.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/57.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/58.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/59.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/6.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/60.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/61.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/62.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/63.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/64.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/65.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/66.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/67.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/68.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/69.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/7.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/70.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/71.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/72.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/73.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/74.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/75.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/76.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/77.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/78.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/79.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/8.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/80.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/81.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/82.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/83.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/84.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/85.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/86.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/87.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/88.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/89.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/9.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/90.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/cho.mp3 delete mode 100644 app/src/main/assets/audio/nam-minh/kinh.mp3 delete mode 100644 app/src/main/java/com/miti99/loto/LotoApp.kt delete mode 100644 app/src/main/java/com/miti99/loto/MainActivity.kt delete mode 100644 app/src/main/java/com/miti99/loto/audio/VoiceManifest.kt delete mode 100644 app/src/main/java/com/miti99/loto/audio/VoicePlayer.kt delete mode 100644 app/src/main/java/com/miti99/loto/game/GameLogic.kt delete mode 100644 app/src/main/java/com/miti99/loto/game/VietnameseNumber.kt delete mode 100644 app/src/main/java/com/miti99/loto/settings/SettingsKeys.kt delete mode 100644 app/src/main/java/com/miti99/loto/settings/SettingsRepository.kt delete mode 100644 app/src/main/java/com/miti99/loto/settings/SettingsState.kt delete mode 100644 app/src/main/java/com/miti99/loto/state/CallBus.kt delete mode 100644 app/src/main/java/com/miti99/loto/state/DeckState.kt delete mode 100644 app/src/main/java/com/miti99/loto/state/DrawEvent.kt delete mode 100644 app/src/main/java/com/miti99/loto/state/GameStorage.kt delete mode 100644 app/src/main/java/com/miti99/loto/state/MasterPanelUiState.kt delete mode 100644 app/src/main/java/com/miti99/loto/state/MasterPanelViewModel.kt delete mode 100644 app/src/main/java/com/miti99/loto/state/PlayerBoardUiState.kt delete mode 100644 app/src/main/java/com/miti99/loto/state/PlayerBoardViewModel.kt delete mode 100644 app/src/main/java/com/miti99/loto/state/SettingsViewModel.kt delete mode 100644 app/src/main/java/com/miti99/loto/state/ViewModelFactory.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/LotoAppRoot.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/LotoTopBar.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/Wordmark.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/board/ChoToast.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/board/Confetti.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/board/GhostPreview.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/board/KinhModal.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/board/PlayerBoardGrid.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/board/PlayerBoardScreen.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/board/PlayerCell.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/common/LocalEmptyCellColor.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/master/CalledHistory.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/master/CurrentNumberHero.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/master/MasterBoard.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/master/MasterBoardLayout.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/master/MasterCell.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/master/MasterControls.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/master/MasterPanelScreen.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/settings/AutoCallControls.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/settings/ColorSwatchTile.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/settings/EmptyCellColorPicker.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/settings/ExcelPresets.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/settings/RgbSliderRow.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/settings/SettingsSheet.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/settings/ThemeModePickers.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/settings/VoicePicker.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/settings/VoiceToggles.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/theme/Color.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/theme/Theme.kt delete mode 100644 app/src/main/java/com/miti99/loto/ui/theme/Type.kt delete mode 100644 app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 app/src/main/res/values/strings.xml delete mode 100644 app/src/main/res/values/themes.xml delete mode 100644 app/src/main/res/xml/backup_rules.xml delete mode 100644 app/src/test/java/com/miti99/loto/game/GameLogicTest.kt delete mode 100644 app/src/test/java/com/miti99/loto/game/VietnameseNumberTest.kt delete mode 100644 app/src/test/java/com/miti99/loto/settings/SettingsRepositoryTest.kt delete mode 100644 build.gradle.kts create mode 100644 capacitor.config.json delete mode 100644 gradle.properties delete mode 100644 gradle/libs.versions.toml create mode 160000 loto create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 plans/reports/research-260510-0055-wrap-loto-as-android-app.md delete mode 100644 settings.gradle.kts diff --git a/.github/workflows/build-debug.yml b/.github/workflows/build-debug.yml index 28495dc..c52e5bb 100644 --- a/.github/workflows/build-debug.yml +++ b/.github/workflows/build-debug.yml @@ -11,6 +11,13 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Node 20 + uses: actions/setup-node@v4 + with: + node-version: '20' - name: Set up JDK 17 uses: actions/setup-java@v4 @@ -21,12 +28,19 @@ jobs: - name: Set up Gradle uses: gradle/actions/setup-gradle@v3 - - name: Lint + unit tests + assemble debug - run: ./gradlew lint test :app:assembleDebug + - name: Install wrapper deps + run: npm ci + + - name: Build loto + sync into Android + run: npm run build + + - name: Assemble debug APK + working-directory: android + run: ./gradlew :app:assembleDebug - name: Upload debug APK uses: actions/upload-artifact@v4 with: name: debug-apk - path: app/build/outputs/apk/debug/*.apk + path: android/app/build/outputs/apk/debug/*.apk retention-days: 7 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b367664..a074f02 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,13 @@ jobs: steps: - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Node 20 + uses: actions/setup-node@v4 + with: + node-version: '20' - name: Set up JDK 17 uses: actions/setup-java@v4 @@ -20,20 +27,27 @@ jobs: - name: Set up Gradle uses: gradle/actions/setup-gradle@v3 + - name: Install wrapper deps + run: npm ci + + - name: Build loto + sync into Android + run: npm run build + - name: Decode keystore run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > keystore.jks - - name: Build release bundle and APK - run: ./gradlew :app:bundleRelease :app:assembleRelease + - name: Build signed AAB + APK + working-directory: android env: LOTO_KEYSTORE_PATH: ${{ github.workspace }}/keystore.jks LOTO_KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} LOTO_KEY_ALIAS: ${{ secrets.KEY_ALIAS }} LOTO_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + run: ./gradlew :app:bundleRelease :app:assembleRelease - name: Upload to GitHub Release uses: softprops/action-gh-release@v2 with: files: | - app/build/outputs/apk/release/*.apk - app/build/outputs/bundle/release/*.aab + android/app/build/outputs/apk/release/*.apk + android/app/build/outputs/bundle/release/*.aab diff --git a/.gitignore b/.gitignore index 43413b9..8cea5ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,35 @@ -*.iml -.gradle -.idea -local.properties -.DS_Store -build -captures -.externalNativeBuild -.cxx +# Node +node_modules/ +npm-debug.log* + +# Loto build (regenerated by `npm run build:loto`) +loto/build/ +loto/.svelte-kit/ + +# Android build outputs +android/.gradle/ +android/build/ +android/app/build/ +android/capacitor-cordova-android-plugins/ +android/local.properties +android/.idea/ +android/captures/ +android/.externalNativeBuild/ +android/.cxx/ + +# Capacitor sync artifacts (regenerated by `npx cap sync`) +android/app/src/main/assets/public/ +android/app/src/main/assets/capacitor.config.json +android/app/src/main/assets/capacitor.plugins.json + +# Signing *.apk *.aab *.keystore *.jks google-services.json -proguard/ + +# Editor / OS +*.iml +.idea/ +.DS_Store diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0ca4bf6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "loto"] + path = loto + url = https://github.com/tiennm99/loto.git diff --git a/README.md b/README.md index f687401..72a997a 100644 --- a/README.md +++ b/README.md @@ -1,97 +1,127 @@ -# Lô tô — Android +# Lô tô — Android (Capacitor wrapper) ![build-debug](https://github.com/tiennm99/loto-android/actions/workflows/build-debug.yml/badge.svg) -Native Android port of [tiennm99/loto](https://github.com/tiennm99/loto) (SvelteKit web app). +Fully-offline Android wrapper around [tiennm99/loto](https://github.com/tiennm99/loto) +(SvelteKit PWA). All assets — HTML, JS, CSS, and 184 voice MP3s — are bundled +into the APK at build time. No network is required at runtime. -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). +> **Note:** the previous native Kotlin/Compose port lives in git history at +> commit `9a35686` and earlier. This repo is now a thin wrapper; loto evolves +> upstream and we just rebuild + ship. + +## How it works + +``` +loto/ (git submodule, pinned SHA) + └── npm run build → loto/build/ (SvelteKit static output) + ↓ + npx cap sync + ↓ +android/app/src/main/assets/public/ (bundled into APK) + ↓ + WebView serves https://localhost/* off-disk +``` + +Capacitor's bridge serves the bundled site from `https://localhost`, which is +loopback only (no INTERNET permission requested). Workbox precache, IndexedDB, +and the `