Flash 1.1.0 — voice/video calling, and a session that heals itself.
dependencies {
implementation("com.github.Kali452345.Flash:core-engine:v1.1.0")
implementation("com.github.Kali452345.Flash:core-calling:v1.1.0") // calls
implementation("com.github.Kali452345.Flash:ui-callui:v1.1.0") // in-call screen
}New: voice & video calls
core-calling and ui-callui are published for the first time. Calling is deliberately
not reachable through Flash.create — it needs a signaling channel the app already owns,
runtime mic/camera grants, and a microphone|camera foreground service in the app's own
manifest. Wire two seams (sendFrame out, onInboundText in) and calling.activeCall drives
a navigation route. See README → Voice & video calls and docs/architecture/public-api.md §7.
Audio is now prioritised over video. Previously there was no priority at all: the audio
RtpSender was discarded and only video was tuned, so 8 Mbps of video on a shared half-duplex
phone hotspot made voice unintelligible. Audio is pinned (Priority.HIGH, bitratePriority
4.0), video demoted, and the ceiling dropped to 2.5 Mbps. MaxBundle + rtcpMux put both
streams on one 5-tuple, so DSCP cannot separate them — the priority lives in the bandwidth
allocator. A quality governor on the 1 Hz stats loop sheds video bitrate, then resolution, then
the stream itself when audio degrades, with asymmetric hysteresis (2 bad samples to concede,
5 clean to recover) and a user-visible reason. Controlled by a default-on Prioritise voice
quality setting.
Fixed: sessions that looked alive and weren't
A peer could hold a zombie session — present in activeSessions, dead on the wire — that
nothing could reap or replace. It made one device latch Offline, the other read falsely Online,
showed a pending clock under an Online header, and let a message tick once and never arrive.
Force-stopping both apps was the only recovery.
- Glare arbitration compared originator ids, so two same-direction sessions for one peer
always tied — and a tie kept the incumbent, so a stale session rejected the peer's fresh
reconnect forever. Now scoped to genuine simultaneous glare; same-direction means newest wins. - Keepalive forgave late scheduler ticks without bound, so the liveness watchdog never
fired under repeated Doze freezes. Forgiveness is now episode-scoped. - Buffered frames on a replaced or rejected connection were discarded, losing inbound
delivery receipts. They now transfer to the surviving session. - The outbox treated "the kernel accepted the bytes" as delivery and deleted its row. Rows
now survive until the peer acknowledges, so a write into a half-open socket retries instead
of vanishing. Delivery status can no longer walk backwards. - Presence gained a real
Connectingstate — Online / Reconnecting / Offline — replacing a
hold that every upstream emission silently cancelled. - Recovery paths treated any session as a working one; they now require recent inbound
traffic before declining to reconnect. - A refused foreground promotion no longer tears down the screen-on re-arm with the
service instance.
Packaging
Twelve modules publish, up from eight — core-calling, ui-theme, ui-chat and ui-callui
join the core set. core-calling now ships consumer ProGuard rules for WebRTC's JNI, without
which a minified consumer build crashed inside PeerConnectionFactory init; the WebRTC AAR
ships none of its own. The ui-* artifacts had been hardcoding version 1.0.0 in their own
publication blocks and now track the root version.
core-calling bundles native WebRTC (~30 MB per ABI). Nothing else in Flash depends on it, so
an app that does not call does not pay for it.
Requires Android 8.0 (API 24)+, AGP 9.3+, Gradle 9.5+, Kotlin 2.2+.
Full changelog: v1.0.0...v1.1.0