Krolyxon
Commit 090b358

Branches

Refs →

budctl090b358

README: a banner in the logo's dots, and the Android app

- The header is a banner like the owner's other projects': the icon's
  emblem (the LED battery ring around a dot-matrix "b") and "BUDCTL" in
  dot-matrix capitals, docs/logo.svg, over a one-line tagline.
- An "Android app" section: what it is, three screenshots of the demo
  buds (Overview, Equalizer, Gestures), how it uses the channel, the
  missing internet permission, and how to build and install it until a
  release carries an APK.
- CHANGELOG: an Unreleased section with the Android app, the icon and
  the GUI's fuller --debug output.
Author
krolyxon
Date
2026-11-01 23:28 +0530
Parent
1db9790
Commit
090b358630336d628e2651de405bcee69bc09474

+185 −12

6 files changed

  1. M CHANGELOG.md +9 −0
  2. M README.md +45 −12
  3. A docs/logo.svg +131 −0
  4. A docs/screenshots/android-equalizer.png
  5. A docs/screenshots/android-gestures.png
  6. A docs/screenshots/android-overview.png
M CHANGELOG.md View
@@ -1,5 +1,14 @@
11 # Changelog
22
3+## Unreleased
4+
5+- An Android app, in `android/`: the desktop app's pages and look on a phone, with the same Rust
6+ library inside, colors from your wallpaper, and no internet permission. There is no release build
7+ of it yet; the README says how to build it.
8+- budctl has an icon: the LED battery ring around a dot-matrix "b". `install.sh` and the PKGBUILD
9+ install it for the launcher entry, and the README has a banner in the same dots.
10+- With `--debug`, `budctl-gui` also shows each command, connect and failure between the frames.
11+
312 ## 0.1.3 (2026-11-01)
413
514 The desktop app moves, the Nothing way: short, eased out, fading rather than sliding.
M README.md View
@@ -1,11 +1,15 @@
1−# budctl
1+<div align="center">
2+ <img src="docs/logo.svg" width="320" alt="budctl">
3+ <p>Control your wireless earbuds from Linux and Android, without the vendor app.</p>
4+</div>
25
3−Control your wireless earbuds from Linux, without the vendor app: noise control, EQ, game mode,
4−touch controls, battery and more. There is a command-line tool, `budctl`, and a desktop app,
5−`budctl-gui`, both built on the same library.
6+Noise control, EQ, game mode, touch controls, battery and more. There is a command-line tool,
7+`budctl`, a desktop app, `budctl-gui`, and an [Android app](#android-app), all built on the same
8+library.
69
7−- No telemetry and no account. budctl talks to your buds over Bluetooth, through BlueZ.
8−- The settings live on the buds, so budctl works alongside the phone app.
10+- No telemetry and no account. budctl talks to your buds over Bluetooth, through BlueZ on Linux.
11+ The Android app does not even have the internet permission.
12+- The settings live on the buds, so a change made on one device shows on the others.
913
1014 **Supported buds:** realme Buds T310. More are added as the author gets them.
1115
@@ -22,8 +26,8 @@ x86_64 and aarch64 (glibc 2.35 or newer).
2226 curl -fsSL https://github.com/krolyxon/budctl/releases/latest/download/install.sh | sh
2327 ```
2428
25−This puts `budctl` and `budctl-gui` in `~/.local/bin`, and a launcher entry in
26−`~/.local/share/applications`. The download is checked against its SHA-256 checksum. Options:
29+This puts `budctl` and `budctl-gui` in `~/.local/bin`, and a launcher entry and its icon in
30+`~/.local/share`. The download is checked against its SHA-256 checksum. Options:
2731
2832 ```sh
2933 sh install.sh --version 0.1.1 # a given release instead of the latest
@@ -139,6 +143,35 @@ closed) to `~/.local/state/budctl/gui.log`, replacing the last launch's.
139143 Other options: `--demo` shows made-up buds without Bluetooth; `--debug` and `--device` work as in
140144 the command-line tool.
141145
146+## Android app
147+
148+`android/` holds an Android app (Android 12 or newer) with the desktop app's pages and look, on a
149+phone: the same Rust library runs inside it, and only the Bluetooth link and the screen are Kotlin.
150+Its colors come from your wallpaper (Material You), or from the desktop app's near-black palette.
151+
152+<p align="center">
153+ <img src="docs/screenshots/android-overview.png" width="240" alt="The Android app's Overview: the battery rings, noise control and its strength, and the switches">
154+ <img src="docs/screenshots/android-equalizer.png" width="240" alt="The Android app's Equalizer: the presets, and a custom preset's six bands open below">
155+ <img src="docs/screenshots/android-gestures.png" width="240" alt="The Android app's Gestures: what each tap and hold does on each bud, and what a hold steps through">
156+</p>
157+
158+The app talks to the buds only while it is on screen, and frees their control channel 10 s after
159+your last change or when you leave it, so your computer can use it. It has no internet permission,
160+and its build fails if anything adds one. A switch on its App page turns on the debug log, which you
161+can copy from there.
162+
163+There is no release build of it yet. To build it and put it on your phone (with USB debugging on),
164+you need Android Studio or the Android SDK with its NDK, the Rust targets
165+(`rustup target add aarch64-linux-android x86_64-linux-android`) and `cargo install cargo-ndk`:
166+
167+```sh
168+cd android
169+./gradlew installDebug
170+```
171+
172+How it is put together: [`docs/android.md`](docs/android.md). The screenshots show made-up buds
173+(`adb shell am start -n io.github.krolyxon.budctl/.MainActivity --ez demo true`).
174+
142175 ## Config
143176
144177 `~/.config/budctl/config.json` is created with the defaults the first time it is needed. Every key
@@ -171,7 +204,7 @@ how sure, is written down in [`docs/protocol/`](docs/protocol/): the
171204
172205 GPL-3.0-or-later. See [LICENSE](LICENSE).
173206
174−The desktop app carries the Doto, Space Grotesk and Space Mono fonts, under the SIL Open Font
175−License 1.1 ([`crates/budctl-gui/fonts/OFL.txt`](crates/budctl-gui/fonts/OFL.txt)). The Doto in it
176−is a static instance (rounded dots, black weight, Latin only), renamed "Doto Rounded" as the
177−license asks of modified fonts.
207+The desktop and Android apps carry the Doto, Space Grotesk and Space Mono fonts, under the SIL
208+Open Font License 1.1 ([`crates/budctl-gui/fonts/OFL.txt`](crates/budctl-gui/fonts/OFL.txt)). The
209+Doto in them is a static instance (rounded dots, black weight, Latin only), renamed "Doto Rounded"
210+as the license asks of modified fonts.
A docs/logo.svg View
@@ -0,0 +1,131 @@
1+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1080 286" width="1080" height="286">
2+ <!-- budctl: the battery ring (its last lit dot the red live dot) around a dot-matrix b, and the name in dot-matrix capitals. -->
3+ <rect width="1080" height="286" fill="#000000"/>
4+ <circle cx="150.0" cy="41.5" r="8.75" fill="#F2F2F2"/>
5+ <circle cx="176.3" cy="45.0" r="8.75" fill="#F2F2F2"/>
6+ <circle cx="200.8" cy="55.1" r="8.75" fill="#F2F2F2"/>
7+ <circle cx="221.8" cy="71.2" r="8.75" fill="#F2F2F2"/>
8+ <circle cx="237.9" cy="92.2" r="8.75" fill="#F2F2F2"/>
9+ <circle cx="248.0" cy="116.7" r="8.75" fill="#F2F2F2"/>
10+ <circle cx="251.5" cy="143.0" r="8.75" fill="#F2F2F2"/>
11+ <circle cx="248.0" cy="169.3" r="8.75" fill="#F2F2F2"/>
12+ <circle cx="237.9" cy="193.8" r="8.75" fill="#F2F2F2"/>
13+ <circle cx="221.8" cy="214.8" r="8.75" fill="#F2F2F2"/>
14+ <circle cx="200.8" cy="230.9" r="8.75" fill="#F2F2F2"/>
15+ <circle cx="176.3" cy="241.0" r="8.75" fill="#F2F2F2"/>
16+ <circle cx="150.0" cy="244.5" r="8.75" fill="#F2F2F2"/>
17+ <circle cx="123.7" cy="241.0" r="8.75" fill="#F2F2F2"/>
18+ <circle cx="99.2" cy="230.9" r="8.75" fill="#F2F2F2"/>
19+ <circle cx="78.2" cy="214.8" r="8.75" fill="#F2F2F2"/>
20+ <circle cx="62.1" cy="193.8" r="8.75" fill="#D71921"/>
21+ <circle cx="52.0" cy="169.3" r="8.75" fill="#3A3A3E"/>
22+ <circle cx="48.5" cy="143.0" r="8.75" fill="#3A3A3E"/>
23+ <circle cx="52.0" cy="116.7" r="8.75" fill="#3A3A3E"/>
24+ <circle cx="62.1" cy="92.2" r="8.75" fill="#3A3A3E"/>
25+ <circle cx="78.2" cy="71.2" r="8.75" fill="#3A3A3E"/>
26+ <circle cx="99.2" cy="55.1" r="8.75" fill="#3A3A3E"/>
27+ <circle cx="123.7" cy="45.0" r="8.75" fill="#3A3A3E"/>
28+ <circle cx="116.4" cy="92.6" r="7.00" fill="#F2F2F2"/>
29+ <circle cx="116.4" cy="109.4" r="7.00" fill="#F2F2F2"/>
30+ <circle cx="116.4" cy="126.2" r="7.00" fill="#F2F2F2"/>
31+ <circle cx="116.4" cy="143.0" r="7.00" fill="#F2F2F2"/>
32+ <circle cx="133.2" cy="143.0" r="7.00" fill="#F2F2F2"/>
33+ <circle cx="150.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
34+ <circle cx="166.8" cy="143.0" r="7.00" fill="#F2F2F2"/>
35+ <circle cx="116.4" cy="159.8" r="7.00" fill="#F2F2F2"/>
36+ <circle cx="183.6" cy="159.8" r="7.00" fill="#F2F2F2"/>
37+ <circle cx="116.4" cy="176.6" r="7.00" fill="#F2F2F2"/>
38+ <circle cx="183.6" cy="176.6" r="7.00" fill="#F2F2F2"/>
39+ <circle cx="116.4" cy="193.4" r="7.00" fill="#F2F2F2"/>
40+ <circle cx="133.2" cy="193.4" r="7.00" fill="#F2F2F2"/>
41+ <circle cx="150.0" cy="193.4" r="7.00" fill="#F2F2F2"/>
42+ <circle cx="166.8" cy="193.4" r="7.00" fill="#F2F2F2"/>
43+ <circle cx="320.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
44+ <circle cx="340.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
45+ <circle cx="360.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
46+ <circle cx="380.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
47+ <circle cx="320.0" cy="103.0" r="7.00" fill="#F2F2F2"/>
48+ <circle cx="400.0" cy="103.0" r="7.00" fill="#F2F2F2"/>
49+ <circle cx="320.0" cy="123.0" r="7.00" fill="#F2F2F2"/>
50+ <circle cx="400.0" cy="123.0" r="7.00" fill="#F2F2F2"/>
51+ <circle cx="320.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
52+ <circle cx="340.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
53+ <circle cx="360.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
54+ <circle cx="380.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
55+ <circle cx="320.0" cy="163.0" r="7.00" fill="#F2F2F2"/>
56+ <circle cx="400.0" cy="163.0" r="7.00" fill="#F2F2F2"/>
57+ <circle cx="320.0" cy="183.0" r="7.00" fill="#F2F2F2"/>
58+ <circle cx="400.0" cy="183.0" r="7.00" fill="#F2F2F2"/>
59+ <circle cx="320.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
60+ <circle cx="340.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
61+ <circle cx="360.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
62+ <circle cx="380.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
63+ <circle cx="440.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
64+ <circle cx="520.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
65+ <circle cx="440.0" cy="103.0" r="7.00" fill="#F2F2F2"/>
66+ <circle cx="520.0" cy="103.0" r="7.00" fill="#F2F2F2"/>
67+ <circle cx="440.0" cy="123.0" r="7.00" fill="#F2F2F2"/>
68+ <circle cx="520.0" cy="123.0" r="7.00" fill="#F2F2F2"/>
69+ <circle cx="440.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
70+ <circle cx="520.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
71+ <circle cx="440.0" cy="163.0" r="7.00" fill="#F2F2F2"/>
72+ <circle cx="520.0" cy="163.0" r="7.00" fill="#F2F2F2"/>
73+ <circle cx="440.0" cy="183.0" r="7.00" fill="#F2F2F2"/>
74+ <circle cx="520.0" cy="183.0" r="7.00" fill="#F2F2F2"/>
75+ <circle cx="460.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
76+ <circle cx="480.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
77+ <circle cx="500.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
78+ <circle cx="560.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
79+ <circle cx="580.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
80+ <circle cx="600.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
81+ <circle cx="620.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
82+ <circle cx="560.0" cy="103.0" r="7.00" fill="#F2F2F2"/>
83+ <circle cx="640.0" cy="103.0" r="7.00" fill="#F2F2F2"/>
84+ <circle cx="560.0" cy="123.0" r="7.00" fill="#F2F2F2"/>
85+ <circle cx="640.0" cy="123.0" r="7.00" fill="#F2F2F2"/>
86+ <circle cx="560.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
87+ <circle cx="640.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
88+ <circle cx="560.0" cy="163.0" r="7.00" fill="#F2F2F2"/>
89+ <circle cx="640.0" cy="163.0" r="7.00" fill="#F2F2F2"/>
90+ <circle cx="560.0" cy="183.0" r="7.00" fill="#F2F2F2"/>
91+ <circle cx="640.0" cy="183.0" r="7.00" fill="#F2F2F2"/>
92+ <circle cx="560.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
93+ <circle cx="580.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
94+ <circle cx="600.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
95+ <circle cx="620.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
96+ <circle cx="700.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
97+ <circle cx="720.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
98+ <circle cx="740.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
99+ <circle cx="760.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
100+ <circle cx="680.0" cy="103.0" r="7.00" fill="#F2F2F2"/>
101+ <circle cx="680.0" cy="123.0" r="7.00" fill="#F2F2F2"/>
102+ <circle cx="680.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
103+ <circle cx="680.0" cy="163.0" r="7.00" fill="#F2F2F2"/>
104+ <circle cx="680.0" cy="183.0" r="7.00" fill="#F2F2F2"/>
105+ <circle cx="700.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
106+ <circle cx="720.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
107+ <circle cx="740.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
108+ <circle cx="760.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
109+ <circle cx="800.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
110+ <circle cx="820.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
111+ <circle cx="840.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
112+ <circle cx="860.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
113+ <circle cx="880.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
114+ <circle cx="840.0" cy="103.0" r="7.00" fill="#F2F2F2"/>
115+ <circle cx="840.0" cy="123.0" r="7.00" fill="#F2F2F2"/>
116+ <circle cx="840.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
117+ <circle cx="840.0" cy="163.0" r="7.00" fill="#F2F2F2"/>
118+ <circle cx="840.0" cy="183.0" r="7.00" fill="#F2F2F2"/>
119+ <circle cx="840.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
120+ <circle cx="920.0" cy="83.0" r="7.00" fill="#F2F2F2"/>
121+ <circle cx="920.0" cy="103.0" r="7.00" fill="#F2F2F2"/>
122+ <circle cx="920.0" cy="123.0" r="7.00" fill="#F2F2F2"/>
123+ <circle cx="920.0" cy="143.0" r="7.00" fill="#F2F2F2"/>
124+ <circle cx="920.0" cy="163.0" r="7.00" fill="#F2F2F2"/>
125+ <circle cx="920.0" cy="183.0" r="7.00" fill="#F2F2F2"/>
126+ <circle cx="920.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
127+ <circle cx="940.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
128+ <circle cx="960.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
129+ <circle cx="980.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
130+ <circle cx="1000.0" cy="203.0" r="7.00" fill="#F2F2F2"/>
131+</svg>
A docs/screenshots/android-equalizer.png View

[Binary file]

A docs/screenshots/android-gestures.png View

[Binary file]

A docs/screenshots/android-overview.png View

[Binary file]