From 8832c45d8a65eacb977a10ed5061a2e2b710f911 Mon Sep 17 00:00:00 2001 From: xodapi <4956501+xodapi@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:52:01 +0700 Subject: [PATCH] feat(android): add Slint Android spike gate (closes #91) Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- Cargo.lock | 175 +++++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 5 +- README.md | 2 +- README.ru.md | 2 +- ROADMAP.md | 1 + docs/android.md | 55 +++++++++++++++ 6 files changed, 228 insertions(+), 12 deletions(-) create mode 100644 docs/android.md diff --git a/Cargo.lock b/Cargo.lock index 4f95aa5..f7d43dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,27 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android-activity" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" +dependencies = [ + "android-properties", + "bitflags 2.13.0", + "cc", + "cesu8", + "jni 0.21.1", + "jni-sys 0.3.1", + "libc", + "log", + "ndk 0.8.0", + "ndk-context", + "ndk-sys 0.5.0+25.2.9519653", + "num_enum", + "thiserror 1.0.69", +] + [[package]] name = "android-activity" version = "0.6.1" @@ -79,16 +100,25 @@ dependencies = [ "android-properties", "bitflags 2.13.0", "cc", - "jni", + "jni 0.22.4", "libc", "log", - "ndk", + "ndk 0.9.0", "ndk-context", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", "thiserror 2.0.18", ] +[[package]] +name = "android-build" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fc9904ad2ad097c3c1cfe2eacaaf0fc24710936fa9ed941cb310b7c6ed2ab7" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "android-properties" version = "0.2.2" @@ -679,6 +709,12 @@ dependencies = [ "shlex 2.0.1", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -2332,6 +2368,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "i-slint-backend-android-activity" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a318b5aebc2ad8e6fdbac8588984d9ea2e99a210449954717073d8a246e12fe7" +dependencies = [ + "android-activity 0.5.2", + "android-activity 0.6.1", + "android-build", + "i-slint-core", + "i-slint-renderer-skia", + "jni 0.22.4", + "ndk 0.9.0", + "raw-window-handle", +] + [[package]] name = "i-slint-backend-linuxkms" version = "1.16.1" @@ -2358,6 +2410,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93c9c4203e0a2c5c396b8f1686160816f73e9b9cadaf3915aa3b888a83448aa" dependencies = [ "cfg-if", + "i-slint-backend-android-activity", "i-slint-backend-linuxkms", "i-slint-backend-winit", "i-slint-common", @@ -2932,6 +2985,22 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + [[package]] name = "jni" version = "0.22.4" @@ -3402,6 +3471,20 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c" +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.13.0", + "jni-sys 0.3.1", + "log", + "ndk-sys 0.5.0+25.2.9519653", + "num_enum", + "thiserror 1.0.69", +] + [[package]] name = "ndk" version = "0.9.0" @@ -3411,7 +3494,7 @@ dependencies = [ "bitflags 2.13.0", "jni-sys 0.3.1", "log", - "ndk-sys", + "ndk-sys 0.6.0+11769913", "num_enum", "raw-window-handle", "thiserror 1.0.69", @@ -3423,6 +3506,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys 0.3.1", +] + [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -5321,6 +5413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d46bc502cc6b113bfd1a7e9a0876532a9454ff4ee905afe09784eaa2ec3f3f33" dependencies = [ "const-field-offset", + "i-slint-backend-android-activity", "i-slint-backend-selector", "i-slint-common", "i-slint-core", @@ -5498,7 +5591,7 @@ dependencies = [ "fastrand", "js-sys", "memmap2", - "ndk", + "ndk 0.9.0", "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", @@ -6686,7 +6779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" dependencies = [ "core-foundation 0.10.1", - "jni", + "jni 0.22.4", "log", "ndk-context", "objc2 0.6.4", @@ -6842,6 +6935,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -6887,6 +6989,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -6944,6 +7061,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -6962,6 +7085,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -6980,6 +7109,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -7010,6 +7145,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -7028,6 +7169,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -7046,6 +7193,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -7064,6 +7217,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -7089,7 +7248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d" dependencies = [ "ahash", - "android-activity", + "android-activity 0.6.1", "atomic-waker", "bitflags 2.13.0", "block2 0.5.1", @@ -7104,7 +7263,7 @@ dependencies = [ "js-sys", "libc", "memmap2", - "ndk", + "ndk 0.9.0", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", diff --git a/Cargo.toml b/Cargo.toml index 4925c99..3369a3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,8 @@ strip = true [features] default = [] -gui = ["dep:slint", "dep:tray-icon"] +gui = ["dep:slint", "dep:tray-icon", "slint/std", "slint/backend-winit", "slint/renderer-software", "slint/compat-1-2"] +android-gui = ["dep:slint", "slint/std", "slint/backend-android-activity-06", "slint/renderer-software", "slint/compat-1-2"] [dependencies] chrono = "0.4" @@ -39,7 +40,7 @@ reqwest = { version = "0.12", default-features = false, features = ["blocking", redb = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" -slint = { version = "1.16.1", optional = true, default-features = false, features = ["std", "backend-winit", "renderer-software", "compat-1-2"] } +slint = { version = "1.16.1", optional = true, default-features = false } tray-icon = { version = "0.24.0", optional = true } toml = "0.8" rand = "0.9" diff --git a/README.md b/README.md index 124d65d..05419c1 100644 --- a/README.md +++ b/README.md @@ -376,7 +376,7 @@ See [ROADMAP.md](ROADMAP.md) for the current improvement backlog. - Windows (x86_64) - macOS (aarch64) - Linux (x86_64, aarch64) -- Android/Termux — see [docs/termux.md](docs/termux.md) +- Android/Termux — see [docs/termux.md](docs/termux.md); native Slint Android spike is tracked in [docs/android.md](docs/android.md) ## Tests diff --git a/README.ru.md b/README.ru.md index ae803a0..3caa2ec 100644 --- a/README.ru.md +++ b/README.ru.md @@ -366,7 +366,7 @@ https://github.com/xodapi/vimit/discussions - Windows (x86_64) - macOS (aarch64) - Linux (x86_64, aarch64) -- Android/Termux — см. [docs/termux.md](docs/termux.md) +- Android/Termux — см. [docs/termux.md](docs/termux.md); native Slint Android spike описан в [docs/android.md](docs/android.md) ## Тесты diff --git a/ROADMAP.md b/ROADMAP.md index fabf8be..80a8e96 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -19,6 +19,7 @@ https://github.com/xodapi/vimit/discussions - Termux install guide. - Termux:Widget example that runs `vimit --demo` or `vimit --json`. - Optional Android notification through Termux:API. +- Native Slint Android spike, see [docs/android.md](docs/android.md). ## Living Token Creature / Живое существо diff --git a/docs/android.md b/docs/android.md new file mode 100644 index 0000000..69c3e0c --- /dev/null +++ b/docs/android.md @@ -0,0 +1,55 @@ +# Android Slint Spike + +This spike records the Android path for vimit quota monitoring. + +## Goal + +Bring VibeMode spend and limit control to Android in two stages: + +1. Android-friendly monitor: native app or notification/widget that shows quota + percent, status, reset time, and the living creature state. +2. Native overlay: optional always-visible layer only if Android permissions and + UX are acceptable on a real device. + +## Feasibility + +Slint supports Android through the `backend-android-activity-06` backend. The +project now has a separate Cargo feature: + +```bash +cargo build --features android-gui --target aarch64-linux-android +``` + +The local Windows environment has Rust Android targets installed, but does not +currently have the Android SDK/NDK compiler tools, `cargo-apk`, `cargo-ndk`, +`adb`, or Gradle. A target check reaches native dependency compilation and then +fails because `aarch64-linux-android-clang` is missing, so APK validation must +be done after installing Android SDK/NDK tooling. + +## Proposed architecture + +- Reuse existing quota parsing, API failover, thresholds, and creature state + logic. +- Split desktop-only GUI parts from portable UI state: + - keep tray, Windows sounds, and window positioning desktop-only; + - expose a small Android model: status, active endpoint, four quota windows, + reset text, creature state, and rate text. +- First Android UI should be a normal Slint Activity, not an overlay. +- Android overlay should be a second step. + +## Overlay constraints + +Android overlays require special "draw over other apps" permission and can be +blocked or hidden by sensitive apps. A stable production design likely also +needs a foreground service or notification entry point so polling is transparent +to the user and compliant with Android background execution limits. + +## Acceptance path + +1. Install Android SDK/NDK, `cargo-apk` or `cargo-ndk`, and + `aarch64-linux-android` Rust target. +2. Add a minimal Android entrypoint that calls the shared quota/creature model + and renders a compact Slint view. +3. Validate a demo APK on a device or emulator. +4. Add notification/widget mode. +5. Only then test optional native overlay permission flow.