feat: UxUrl — native URL / phone / email detection + tap launcher
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.
This commit is contained in:
58
lib/src/_ffi.dart
Normal file
58
lib/src/_ffi.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
DynamicLibrary? _open() {
|
||||
if (kIsWeb) return null;
|
||||
try {
|
||||
if (Platform.isIOS || Platform.isMacOS) return DynamicLibrary.process();
|
||||
if (Platform.isAndroid) return DynamicLibrary.open('libux.so');
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// `null` on web, unsupported platforms, or load failure.
|
||||
final DynamicLibrary? uxLib = _open();
|
||||
|
||||
double Function()? uxLookupDouble(String name) {
|
||||
final lib = uxLib;
|
||||
if (lib == null) return null;
|
||||
try {
|
||||
return lib.lookup<NativeFunction<Double Function()>>(name).asFunction();
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int Function()? uxLookupInt32(String name) {
|
||||
final lib = uxLib;
|
||||
if (lib == null) return null;
|
||||
try {
|
||||
return lib.lookup<NativeFunction<Int32 Function()>>(name).asFunction();
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void Function()? uxLookupVoid(String name) {
|
||||
final lib = uxLib;
|
||||
if (lib == null) return null;
|
||||
try {
|
||||
return lib.lookup<NativeFunction<Void Function()>>(name).asFunction();
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void Function(double)? uxLookupVoidDouble(String name) {
|
||||
final lib = uxLib;
|
||||
if (lib == null) return null;
|
||||
try {
|
||||
return lib.lookup<NativeFunction<Void Function(Double)>>(name).asFunction();
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user