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.
+185 −12
6 files changed
- M CHANGELOG.md +9 −0
- M README.md +45 −12
- A docs/logo.svg +131 −0
- A docs/screenshots/android-equalizer.png
- A docs/screenshots/android-gestures.png
- A docs/screenshots/android-overview.png
| @@ -1,5 +1,14 @@ | |||
| 1 | 1 | # Changelog | |
| 2 | 2 | ||
| 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 | + | ||
| 3 | 12 | ## 0.1.3 (2026-11-01) | |
| 4 | 13 | ||
| 5 | 14 | The desktop app moves, the Nothing way: short, eased out, fading rather than sliding. | |
| @@ -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> | |
| 2 | 5 | ||
| 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. | |
| 6 | 9 | ||
| 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. | |
| 9 | 13 | ||
| 10 | 14 | **Supported buds:** realme Buds T310. More are added as the author gets them. | |
| 11 | 15 | ||
| @@ -22,8 +26,8 @@ x86_64 and aarch64 (glibc 2.35 or newer). | |||
| 22 | 26 | curl -fsSL https://github.com/krolyxon/budctl/releases/latest/download/install.sh | sh | |
| 23 | 27 | ``` | |
| 24 | 28 | ||
| 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: | |
| 27 | 31 | ||
| 28 | 32 | ```sh | |
| 29 | 33 | 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. | |||
| 139 | 143 | Other options: `--demo` shows made-up buds without Bluetooth; `--debug` and `--device` work as in | |
| 140 | 144 | the command-line tool. | |
| 141 | 145 | ||
| 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 | + | ||
| 142 | 175 | ## Config | |
| 143 | 176 | ||
| 144 | 177 | `~/.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 | |||
| 171 | 204 | ||
| 172 | 205 | GPL-3.0-or-later. See [LICENSE](LICENSE). | |
| 173 | 206 | ||
| 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. | |
| @@ -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> | |
[Binary file]
[Binary file]
[Binary file]