# wIRC A native Android IRC client: mIRC colors, timestamps, OP powers (kick/ban/op), Ctrl+K/B/I formatting shortcuts, saved servers with on-connect identify commands, and every channel *and* private message opening as its own independently-styled window (1px black border, adjustable background transparency, black/white text toggle). ## What's built so far (MVP) - Raw socket IRC connection (`irc/IrcConnection.kt`) with auto-reconnect/backoff, optional TLS, auto-join on connect. - Full mIRC control-code parser (`irc/MircColorParser.kt`) — colors, bold, underline, reverse, reset — rendered as a Compose `AnnotatedString`. - Protocol line parser (`irc/IrcLineParser.kt`) for PRIVMSG/JOIN/PART/KICK/MODE/ TOPIC/NOTICE/QUIT/NAMES. - `ConnectionManager` — app-wide singleton so every open window reads from the same shared state (messages, user list) regardless of which window is active. - `IrcConnectionService` — foreground service that keeps the socket(s) alive when the app is backgrounded (Android kills background network connections aggressively without this). - `ChannelScreen` — the actual window UI: scrollback with mIRC colors + per-line timestamps, input bar, long-press a nick for a Kick/Ban/Op/De-op menu, and a settings panel (gear icon) with the transparency slider and text color toggle. - `ChannelWindowActivity` — one instance per channel. Uses `documentLaunchMode="always"` + `resizeableActivity="true"` in the manifest and `FLAG_ACTIVITY_NEW_DOCUMENT` on launch, which is the standard way an app can offer real independent, draggable/resizable OS-level windows on devices that support freeform multi-window (large tablets, Samsung DeX, etc). - `MainActivity` / `MainScreen` — the main window. Lists saved servers as cards (Connect / Edit / Delete), and per-connected-server a box to join + open a channel window. "+ Add Server" opens `ServerEditDialog` with host, port, TLS, nick, auto-join channels, and a multi-line "on-connect / identify commands" field — e.g. `PRIVMSG NickServ :IDENTIFY hunter2` — sent automatically right after registration on every connect, before auto-join. - `ServerRepository` — persists the saved server list (including on-connect commands) to `servers.json` in app-private storage, so it survives app restarts. This is the "file that saves that information" you asked for. - Private messages: `ConnectionManager` now keys PMs by the *other person's* nick (not our own), and emits a `newQueryBuffers` event the first time a PM from someone new arrives. `IrcConnectionService` listens for that and auto-launches a `ChannelWindowActivity` for it — so a PM pops its own window the same way tapping a channel does, no different from every other window in the app. - Ctrl+K / Ctrl+B / Ctrl+I: hardware-keyboard shortcuts wired into the input field in `ChannelScreen.kt`. Ctrl+B/I insert the raw bold (`\u0002`) / italic (`\u001D`) toggle control chars at the cursor. Ctrl+K opens a 16-swatch mIRC color picker dialog (foreground, then optional background) and inserts the real `\u0003FG,BG` wire code — same bytes mIRC/HexChat send. ## Known limitations / what's still rough - **"Minimize" isn't an app feature** — on devices with freeform multi-window (large tablets, Samsung DeX, etc.) the OS itself draws minimize/maximize/ close chrome around any `resizeableActivity="true"` window. Both `MainActivity` and `ChannelWindowActivity` are marked resizable so they get that treatment automatically; there's no button to build on the app side. - **No SASL handshake yet** — `ServerConfig` has `saslUser`/`saslPass` fields but `IrcConnection` only sends `CAP REQ :sasl` and stops there. The "on-connect commands" field is the current way to identify (it just sends a plain `PRIVMSG NickServ :IDENTIFY ...` after registration, which works fine on Rizon/most networks even without real SASL). - **Multi-window is best-effort**, same as before — genuinely dependent on device/OEM support for freeform windowing. This is a platform limitation, not something the app can force. - **No notifications for mentions when a window isn't focused yet.** - **Channel list per server isn't persisted** — only the server configs (including identify commands) are saved; which channels you had open resets each app restart. Worth adding to `ServerRepository` next if it bugs you. ## Building — Claude Code / PowerShell, no Android Studio This project lives at `C:\wallpapers\wIRC`. Since you're picking this up with Claude Code the same way as Cart & Dagger: it needs the Android SDK command-line tools + Gradle, same setup you've already got at `C:\Users\admin\AppData\Local\Android\Sdk`. From PowerShell, in `C:\wallpapers\wIRC`: ```powershell cd C:\wallpapers\wIRC # One-time: point Gradle at your existing SDK install $env:ANDROID_HOME = "C:\Users\admin\AppData\Local\Android\Sdk" "sdk.dir=C:\\Users\\admin\\AppData\\Local\\Android\\Sdk" | Out-File -Encoding ascii local.properties # Generate the Gradle wrapper jar (not shipped in this zip — it's a binary) # Requires a local Gradle install once, just to bootstrap the wrapper: gradle wrapper --gradle-version 8.9 # Build .\gradlew.bat assembleDebug ``` The debug APK lands at `C:\wallpapers\wIRC\app\build\outputs\apk\debug\app-debug.apk`. From there, install it the same way you've been doing for Cart & Dagger — `adb install`, or transfer it over your own hosting and install directly on the tablet/phone. If Claude Code hits a missing `gradle` command for that one-time wrapper bootstrap, the SDK's `cmdline-tools` don't include Gradle itself — grab it separately (`choco install gradle` if you use Chocolatey, or download the binary distribution and add it to PATH) just for that first `gradle wrapper` call; every build after that uses `.\gradlew.bat` and doesn't need a system Gradle install anymore. ## Transferring to tablet/phone Once you've got an APK (built on the laptop in Android Studio, or later directly on-device via Claude Code in Termux/proot once that's sorted out): - Simplest: `adb install app-debug.apk` if the device is plugged in / on the same network with ADB over Wi-Fi enabled. - Or just copy the APK over via your own file transfer (your hosting, a shared folder, whatever you'd normally use) and install it directly on the device — you'll need "install unknown apps" allowed for whichever app you use to open the APK file. ## Suggested next steps 1. Wire up persistence for server/channel lists (Room or DataStore) so `MainActivity` remembers your setup between launches. 2. Finish SASL PLAIN auth in `IrcConnection`. 3. Add a notification path for mentions when a channel window isn't focused. 4. Decide on nick auto-complete / tab-complete in the input bar.