- Gate buildFfmpegJni + jniLibs packaging on `ux: enable_ffmpeg` in the
consuming app's pubspec (default off) -- no LGPL / H.264-patent
exposure unless explicitly enabled
- appInfoBuilder generates kUxEnableFfmpeg from the same flag so apps
register the FFmpeg LGPL notice eagerly, pubspec-only (no dart-define)
- Add registerFfmpegLicense() + bundled LGPL-2.1 text asset
- FFmpeg compliance docs (LICENSES-3RDPARTY.md, android/ffmpeg/README.md)
- Network video streaming: XVideoPlayerController.network
Sync FFI from Dart into platform detectors:
- iOS / macOS: NSDataDetector(.link | .phoneNumber) + a tight bare-domain
pass that requires `/` or `?` (so `etc.` / `v1.2.3` don't false-positive
while `example.com/path` does match). NFKD-fold the phone capture so
full-width / Arabic-Indic digits collapse to ASCII; stop the digit run
at the first letter so `+1 555 1234 ext.99` doesn't fuse the extension.
- Android: JNI into android.util.Patterns (WEB_URL / EMAIL_ADDRESS / PHONE)
via a cached JavaVM, std::call_once for init, full per-call
ExceptionCheck coverage. UTF-16→UTF-8 conversion is hand-rolled to dodge
the Modified-UTF-8 / CESU-8 incompatibility with Dart's utf8.decode.
`UxUrl.launch(url)` is the matching tap action. Channel side dispatches via
UIApplication / NSWorkspace / Intent.ACTION_VIEW. Dart-side gates the URL
against a scheme allowlist (http, https, mailto, tel, sms, banlu, tg),
rejects bidi-override controls (U+202A..E / U+2066..9) to prevent visual
spoofs, and blocks USSD / MMI tel: codes containing `*` or `#`.
Library/native cleanup along the way:
- Renamed libux_keyboard.so to libux.so (also covers sensor + url).
- Collapsed three near-identical FFI loader stanzas across keyboard / sensor
/ url into a shared lib/src/_ffi.dart with `uxLib` + typed `uxLookupX`
helpers.
Fix several race conditions in the iOS interactive dismiss flow:
- Don't skip keyboard close notifications during pan tracking, which
left Dart unaware the keyboard was dismissed
- Guard resignFirstResponder with generation check so reopened keyboards
aren't killed by stale dismiss completions
- Block pan tracking from starting during an in-flight dismiss animation
- Always reset keyboard view bounds when the keyboard opens, not just
when the dismiss animator is still running
- Handle duration=0 keyboard notifications by snapping immediately
- Gate adaptive learning debug output behind kDebugMode
Rewrite the example as a chat UI demonstrating ListenableBuilder,
scroll freeze during interactive dismiss, and proper bottom inset
handling with max(keyboardHeight, safeBottom).
Modernize example Android project from v1 to v2 embedding with
current AGP/Gradle versions.