A server knows its clients by address, so moving between Wi-Fi and mobile
data leaves the connection with nothing to do but time out. The process's
sockets are now bound to the network a connection was made over: on a
change, the goodbye still leaves through the old network while it lasts,
and the connection is made again over the new one, back into the channel
we were in. A drop because the network vanished counts as a change too.
A server still holding the old connection refuses the new one as a clone
until it times out, so attempts are retried with a growing pause; a ban
ends it, and so do five minutes without getting back on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The settings show the level on TeamSpeak's scale, lit while it would be
sent, with the volume gate's threshold marked as the slider moves. While
connected it listens to the connection's own microphone, so it shows
exactly what goes out; otherwise it runs a test microphone of its own for
as long as the settings are on screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Microphone and speaker are chosen separately, so a Bluetooth headset can
play in media quality while the phone's own microphone listens, instead of
the headset dropping into its call profile. Devices are kept by type and
address: Android hands out new ids whenever one reconnects, and a line that
lost its device comes back to it when it returns.
The microphone preset (voice recognition by default, or the platform's
call processing, unprocessed, ...) and whether voices play as media or as
a call are Android's own settings. Changing either reopens the open
streams in place. Communication mode, and the headset's call link that a
Bluetooth microphone needs, are held only while a stream needs them, and
are set up before that stream opens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Switching conversations dropped the message being written, and showed
the next one at the scroll position of the last.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Connect screen with an address or a bookmark, which can be added and
removed there.
- Server screen: the channel tree with each client's status, folding
channels, joining (asking for a password where needed) and
subscribing from a channel's sheet; private message, poke and volume
from a client's sheet.
- Chat: the server log, the channel and private conversations with
their history, unread marks, BBCode and clickable client, channel and
web links. Chat lines are the HTML the desktop shows, turned into
styled text.
- Voice bar: mute, deafen, away, and a hold-to-talk button in
push-to-talk mode. The microphone permission is asked for on connect.
- Settings: nickname, transmission mode, voice activation, noise
suppression, gain control and playback volume, in effect at once.
The session's state reaches Compose as immutable snapshots: the tree
at most every 30 ms, rebuilt only when channels or clients changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The session moves from the connection screen's ViewModel into an
app-wide SessionController, and a foreground service keeps the process
running while connected:
- Its notification shows the server and status and offers Mute, Deafen
and Disconnect.
- It declares microphone use only once the microphone is handed to the
connection, as Android requires that permission to be granted then.
- It holds a low-latency Wi-Fi lock and a CPU wake lock, since voice
goes out every 20 ms.
- When the system moves to another network, the session reconnects to
where it was, as its socket stays bound to the old one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
android/ builds core and ts3j from the same sources as the desktop's
Maven build, so the app shares all of the client's logic:
- audio: Oboe streams behind AudioCapture/AudioPlayback, with a lock-free
ring between the real-time callback and the blocking Java side, and
libopus 1.5.2 built with the NDK behind OpusCodec. Speakers are mixed
onto one stream. Capture uses the VoiceRecognition preset, leaving
noise suppression and gain to the client's own processing.
- app: a plain connection screen to try the port out - connect,
microphone, mute, deafen, channel chat and a log - until the real UI.
Targets Android 13 and up (minSdk 33) on arm64 and x86_64.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>