1305 lines
40 KiB
Dart
1305 lines
40 KiB
Dart
// This file is automatically generated, so please do not edit it.
|
|
// @generated by `flutter_rust_bridge`@ 2.12.0.
|
|
|
|
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
|
|
|
import 'frb_generated.dart';
|
|
import 'lib.dart';
|
|
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
|
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
|
|
part 'api.freezed.dart';
|
|
|
|
// These functions are ignored because they are not marked as `pub`: `install_panic_diagnostic_hook`, `log_file_path`, `log_sink`, `map_join_error_code`, `map_join_sync_state`, `open_log_file`, `permission_events`, `publish_permission_state`, `runtime`, `session`, `task_join_error`, `transmit_mode_from_u8`
|
|
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_fields_are_eq`, `assert_fields_are_eq`, `assert_fields_are_eq`, `assert_fields_are_eq`, `assert_fields_are_eq`, `assert_fields_are_eq`, `assert_fields_are_eq`, `assert_fields_are_eq`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`, `from`
|
|
// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `from_kotlin_str`, `to_permission_gate`
|
|
|
|
/// Return the platform-conventional log-file path as a string, or
|
|
/// an empty string if the platform does not have one (mobile).
|
|
String logFilePathStr() => RustLib.instance.api.crateApiLogFilePathStr();
|
|
|
|
/// Connect to a TeamSpeak-compatible server and return the initial
|
|
/// state snapshot. Honours the DEC-006 single-connection invariant
|
|
/// via [`BridgeError::AlreadyConnected`].
|
|
///
|
|
/// `password` is optional — pass an empty string for servers that
|
|
/// don't require one.
|
|
Future<BridgeSnapshot> connect({
|
|
required String host,
|
|
required String nickname,
|
|
required String password,
|
|
}) => RustLib.instance.api.crateApiConnect(
|
|
host: host,
|
|
nickname: nickname,
|
|
password: password,
|
|
);
|
|
|
|
/// Re-fetch a fresh snapshot from the active connection.
|
|
Future<BridgeSnapshot> snapshot() => RustLib.instance.api.crateApiSnapshot();
|
|
|
|
/// Disconnect from the server. No-op if not connected.
|
|
Future<void> disconnect() => RustLib.instance.api.crateApiDisconnect();
|
|
|
|
/// True if a connection is currently active.
|
|
Future<bool> isConnected() => RustLib.instance.api.crateApiIsConnected();
|
|
|
|
/// Handle iOS AVAudioSession route changes (SDD-100).
|
|
void handleRouteChange({required BridgeAudioRoute route}) =>
|
|
RustLib.instance.api.crateApiHandleRouteChange(route: route);
|
|
|
|
/// Handle iOS AVAudioSession media-services reset with the current
|
|
/// route class. Called by AppDelegate after rebuilding the session.
|
|
///
|
|
/// `route_class` is the Swift-side route class string (e.g. "Speaker").
|
|
void handleMediaServicesResetWithRoute({required String routeClass}) => RustLib
|
|
.instance
|
|
.api
|
|
.crateApiHandleMediaServicesResetWithRoute(routeClass: routeClass);
|
|
|
|
/// Handle iOS AVAudioSession interruption begin (SDD-101).
|
|
void handleInterruptionBegan() =>
|
|
RustLib.instance.api.crateApiHandleInterruptionBegan();
|
|
|
|
/// Handle iOS AVAudioSession interruption end (SDD-101).
|
|
void handleInterruptionEnded({required bool shouldResume}) => RustLib
|
|
.instance
|
|
.api
|
|
.crateApiHandleInterruptionEnded(shouldResume: shouldResume);
|
|
|
|
/// Set the focused/on-screen push-to-talk hold state.
|
|
///
|
|
/// Binding capture chooses which physical key drives PTT, while this
|
|
/// command carries the actual press/release edge for fallback focused
|
|
/// keyboard handling and touch controls.
|
|
Future<void> setPtt({required bool active}) =>
|
|
RustLib.instance.api.crateApiSetPtt(active: active);
|
|
|
|
/// Join a voice channel (SDD-094). Moves the user to `channel_id`,
|
|
/// brings up the audio engine if needed, and emits
|
|
/// `BridgeEvent::VoiceState`. `password` may be empty.
|
|
Future<void> voiceJoin({required BigInt channelId, required String password}) =>
|
|
RustLib.instance.api.crateApiVoiceJoin(
|
|
channelId: channelId,
|
|
password: password,
|
|
);
|
|
|
|
/// Leave the current voice channel (SDD-094). Tears down the audio
|
|
/// engine and emits `BridgeEvent::VoiceState`.
|
|
Future<void> voiceLeave() => RustLib.instance.api.crateApiVoiceLeave();
|
|
|
|
/// Set the active transmit mode (SDD-095).
|
|
Future<void> setTransmitMode({required BridgeTransmitMode mode}) =>
|
|
RustLib.instance.api.crateApiSetTransmitMode(mode: mode);
|
|
|
|
/// Read the active transmit mode.
|
|
Future<BridgeTransmitMode> getTransmitMode() =>
|
|
RustLib.instance.api.crateApiGetTransmitMode();
|
|
|
|
/// Update the release-tail in milliseconds (SDD-096). Values are
|
|
/// clamped to `0..=500` on the Rust side; passing anything larger
|
|
/// silently saturates.
|
|
Future<void> setReleaseTailMs({required int ms}) =>
|
|
RustLib.instance.api.crateApiSetReleaseTailMs(ms: ms);
|
|
|
|
/// Read the current release-tail in milliseconds.
|
|
Future<int> getReleaseTailMs() =>
|
|
RustLib.instance.api.crateApiGetReleaseTailMs();
|
|
|
|
/// Engage or release the hard-mute clamp (SDD-094). When `true`
|
|
/// the audio engine transmits nothing regardless of mode.
|
|
Future<void> setHardMute({required bool muted}) =>
|
|
RustLib.instance.api.crateApiSetHardMute(muted: muted);
|
|
|
|
/// Update the active PTT binding (gen2 v0.9.3 / DEC-026). The
|
|
/// platform_key string is opaque to the bridge — it identifies the
|
|
/// bound key inside the platform backend and never appears in any
|
|
/// log record or diagnostic export (DEC-027 enforced at the
|
|
/// diagnostics-sanitizer layer).
|
|
Future<void> setPttBinding({
|
|
required BridgePttInputClass inputClass,
|
|
required String platformKey,
|
|
}) => RustLib.instance.api.crateApiSetPttBinding(
|
|
inputClass: inputClass,
|
|
platformKey: platformKey,
|
|
);
|
|
|
|
/// Read the current PTT capability descriptor. Matches the privacy-safe
|
|
/// `BridgeEvent::PttCapability` event shape; useful for the initial UI
|
|
/// render before the first event arrives.
|
|
Future<BridgePttDescriptor> pttDescriptor() =>
|
|
RustLib.instance.api.crateApiPttDescriptor();
|
|
|
|
/// Return the persisted PTT binding so the UI can hydrate its display
|
|
/// state at launch (e.g. show "PTT: Space" next to the badge before the
|
|
/// user re-opens the binding dialog). Empty strings mean no binding has
|
|
/// been persisted yet.
|
|
Future<BridgePttBinding> getPttBinding() =>
|
|
RustLib.instance.api.crateApiGetPttBinding();
|
|
|
|
/// Move our own client to `channel_id`. Optional channel password
|
|
/// for password-protected channels — pass an empty string when not
|
|
/// required.
|
|
Future<void> moveToChannel({
|
|
required BigInt channelId,
|
|
required String password,
|
|
}) => RustLib.instance.api.crateApiMoveToChannel(
|
|
channelId: channelId,
|
|
password: password,
|
|
);
|
|
|
|
/// Toggle self input-mute (microphone) on the server. Independent
|
|
/// of push-to-talk: a muted client never transmits regardless of
|
|
/// PTT state.
|
|
Future<void> setInputMuted({required bool muted}) =>
|
|
RustLib.instance.api.crateApiSetInputMuted(muted: muted);
|
|
|
|
/// Toggle self output-mute (speaker). Mutes locally *and* informs
|
|
/// the server. The server uses this for the channel icon next to
|
|
/// the client name; the local mute kicks in immediately even
|
|
/// before the server acknowledges.
|
|
Future<void> setOutputMuted({required bool muted}) =>
|
|
RustLib.instance.api.crateApiSetOutputMuted(muted: muted);
|
|
|
|
/// Set master output gain. `1.0` is unity, `0.0` is silent. Values
|
|
/// above `1.0` amplify and can clip downstream. Errors when audio
|
|
/// is not started.
|
|
Future<void> setOutputGain({required double gain}) =>
|
|
RustLib.instance.api.crateApiSetOutputGain(gain: gain);
|
|
|
|
/// Set per-client output volume (SRS-075). `1.0` is unity, `0.0`
|
|
/// mutes. No-op when client has no active voice queue. Volume is
|
|
/// applied directly to the tsclientlib AudioQueue and takes effect
|
|
/// immediately on the next render callback.
|
|
Future<void> setClientVolume({
|
|
required BigInt clientId,
|
|
required double volume,
|
|
}) => RustLib.instance.api.crateApiSetClientVolume(
|
|
clientId: clientId,
|
|
volume: volume,
|
|
);
|
|
|
|
/// Send a text message to the specified target.
|
|
Future<void> sendChatMessage({
|
|
required String message,
|
|
required BridgeMessageTarget target,
|
|
}) => RustLib.instance.api.crateApiSendChatMessage(
|
|
message: message,
|
|
target: target,
|
|
);
|
|
|
|
/// User-initiated diagnostic export. Returns a multi-line text
|
|
/// blob, redacted per the production policy, that the user can
|
|
/// share or copy. DEC-016 forbids automatic uploads — this is the
|
|
/// only path that surfaces logs.
|
|
///
|
|
/// SDD-112 item 10 / SDD-113 item 7 / SDD-116 item 3: when an
|
|
/// Android voice session is open this export embeds the
|
|
/// `[audio.android]` verification-matrix fragment (requested /
|
|
/// achieved stream config, per-effect engagement, latency tier).
|
|
/// On non-Android targets or before a voice session opens the
|
|
/// section is omitted. Per SDD-090 every field in that section is
|
|
/// a device-side technical scalar — no PII admitted.
|
|
String exportDiagnostics() => RustLib.instance.api.crateApiExportDiagnostics();
|
|
|
|
/// Wire the identity persistence store to a platform-private
|
|
/// directory. Should be called once on app start after Flutter has
|
|
/// resolved `getApplicationSupportDirectory()` (or equivalent).
|
|
///
|
|
/// Subsequent [`connect`] calls will reuse the persisted identity,
|
|
/// or generate-and-persist a fresh one on first use. This keeps the
|
|
/// server-visible UID stable across app restarts.
|
|
///
|
|
/// Beta caveat: the identity is stored as a plain file (mode 0600
|
|
/// on Unix). It is *not* encrypted at rest. RISK-PoC-002 documents
|
|
/// this gap; the v0.4 storage rework lands the proper Secret
|
|
/// Service + Android Keystore + iOS Keychain backends.
|
|
Future<void> initStorage({required String dir}) =>
|
|
RustLib.instance.api.crateApiInitStorage(dir: dir);
|
|
|
|
/// List persisted bookmarks.
|
|
Future<List<BridgeBookmark>> listBookmarks() =>
|
|
RustLib.instance.api.crateApiListBookmarks();
|
|
|
|
/// Insert a bookmark and return its assigned id. The `id` field on
|
|
/// the input is ignored.
|
|
Future<PlatformInt64> addBookmark({required BridgeBookmark b}) =>
|
|
RustLib.instance.api.crateApiAddBookmark(b: b);
|
|
|
|
/// Update an existing bookmark.
|
|
Future<void> updateBookmark({required BridgeBookmark b}) =>
|
|
RustLib.instance.api.crateApiUpdateBookmark(b: b);
|
|
|
|
/// Delete a bookmark by id.
|
|
Future<void> deleteBookmark({required PlatformInt64 id}) =>
|
|
RustLib.instance.api.crateApiDeleteBookmark(id: id);
|
|
|
|
/// Notify the core of the latest OS-reported connectivity state.
|
|
/// Called by the Flutter side from `connectivity_plus` callbacks.
|
|
/// The core's supervisor uses this to (a) pre-charge the watchdog
|
|
/// on Offline and (b) short-circuit reconnect backoff on Online.
|
|
void setNetworkState({required BridgeNetworkState state}) =>
|
|
RustLib.instance.api.crateApiSetNetworkState(state: state);
|
|
|
|
/// Subscribe to lifecycle events. Each call yields a fresh
|
|
/// subscription; multiple subscribers are supported. On slow
|
|
/// consumers, events are dropped rather than blocking the supervisor
|
|
/// (consistent with `tokio::sync::broadcast::Receiver` semantics).
|
|
Stream<BridgeEvent> eventsStream() =>
|
|
RustLib.instance.api.crateApiEventsStream();
|
|
|
|
/// Read audio statistics. Errors if no connection or audio not started.
|
|
Future<BridgeAudioStats> audioStats() =>
|
|
RustLib.instance.api.crateApiAudioStats();
|
|
|
|
/// Apply the P1 audio-processing config.
|
|
Future<void> setAudioProcessingConfig({
|
|
required BridgeAudioProcessingConfig config,
|
|
}) => RustLib.instance.api.crateApiSetAudioProcessingConfig(config: config);
|
|
|
|
/// Read the current audio-processing config.
|
|
///
|
|
/// Returns the live config as last applied to the audio engine.
|
|
/// Returns a default config when no session is active.
|
|
Future<BridgeAudioProcessingConfig> getAudioProcessingConfig() =>
|
|
RustLib.instance.api.crateApiGetAudioProcessingConfig();
|
|
|
|
/// Read P1 audio-processing diagnostics.
|
|
Future<BridgeAudioProcessingStats> audioProcessingStats() =>
|
|
RustLib.instance.api.crateApiAudioProcessingStats();
|
|
|
|
/// List available audio input and output devices from the platform.
|
|
Future<BridgeAudioDeviceList> listAudioDevices() =>
|
|
RustLib.instance.api.crateApiListAudioDevices();
|
|
|
|
/// Set the preferred input device by id. Takes effect on next
|
|
/// `start_audio`.
|
|
Future<void> setInputDevice({String? id}) =>
|
|
RustLib.instance.api.crateApiSetInputDevice(id: id);
|
|
|
|
/// Set the preferred output device by id.
|
|
Future<void> setOutputDevice({String? id}) =>
|
|
RustLib.instance.api.crateApiSetOutputDevice(id: id);
|
|
|
|
/// Configure the VAD model path.
|
|
Future<void> setVadModelPath({required String path}) =>
|
|
RustLib.instance.api.crateApiSetVadModelPath(path: path);
|
|
|
|
/// Enable or disable audio debug WAV dumping.
|
|
Future<void> enableAudioDebugWavDump({required bool enabled}) =>
|
|
RustLib.instance.api.crateApiEnableAudioDebugWavDump(enabled: enabled);
|
|
|
|
/// Select the iOS voice-processing mode.
|
|
Future<void> setIosVoiceProcessingMode({
|
|
required BridgeIosVoiceProcessingMode mode,
|
|
}) => RustLib.instance.api.crateApiSetIosVoiceProcessingMode(mode: mode);
|
|
|
|
/// Set the preferred audio output route (Android/iOS).
|
|
void setAudioOutputRoute({required BridgeAudioRoute route}) =>
|
|
RustLib.instance.api.crateApiSetAudioOutputRoute(route: route);
|
|
|
|
/// Called from Flutter when the app enters background/foreground.
|
|
void recordLifecycleEvent({required String state}) =>
|
|
RustLib.instance.api.crateApiRecordLifecycleEvent(state: state);
|
|
|
|
/// Bridge processing backend.
|
|
enum BridgeAudioBackend {
|
|
/// Platform voice processing.
|
|
platformVoiceProcessing,
|
|
|
|
/// Sonora backend.
|
|
sonora,
|
|
|
|
/// WebRTC APM backend.
|
|
webrtcApm,
|
|
|
|
/// No-op backend.
|
|
noop,
|
|
}
|
|
|
|
/// Audio device info from the platform.
|
|
class BridgeAudioDevice {
|
|
/// Stable platform-reported device identifier.
|
|
final String id;
|
|
|
|
/// Human-readable device name.
|
|
final String name;
|
|
|
|
/// Additional device details useful for disambiguation.
|
|
final String details;
|
|
|
|
/// True if the OS reports this as the default device.
|
|
final bool isDefault;
|
|
|
|
/// True if Chanora currently has this device pinned.
|
|
final bool isSelected;
|
|
|
|
const BridgeAudioDevice({
|
|
required this.id,
|
|
required this.name,
|
|
required this.details,
|
|
required this.isDefault,
|
|
required this.isSelected,
|
|
});
|
|
|
|
@override
|
|
int get hashCode =>
|
|
id.hashCode ^
|
|
name.hashCode ^
|
|
details.hashCode ^
|
|
isDefault.hashCode ^
|
|
isSelected.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgeAudioDevice &&
|
|
runtimeType == other.runtimeType &&
|
|
id == other.id &&
|
|
name == other.name &&
|
|
details == other.details &&
|
|
isDefault == other.isDefault &&
|
|
isSelected == other.isSelected;
|
|
}
|
|
|
|
/// List of available audio devices.
|
|
class BridgeAudioDeviceList {
|
|
/// Available input devices.
|
|
final List<BridgeAudioDevice> inputDevices;
|
|
|
|
/// Available output devices.
|
|
final List<BridgeAudioDevice> outputDevices;
|
|
|
|
const BridgeAudioDeviceList({
|
|
required this.inputDevices,
|
|
required this.outputDevices,
|
|
});
|
|
|
|
@override
|
|
int get hashCode => inputDevices.hashCode ^ outputDevices.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgeAudioDeviceList &&
|
|
runtimeType == other.runtimeType &&
|
|
inputDevices == other.inputDevices &&
|
|
outputDevices == other.outputDevices;
|
|
}
|
|
|
|
/// P1 audio-processing configuration DTO.
|
|
class BridgeAudioProcessingConfig {
|
|
/// Route class.
|
|
final BridgeAudioRoute route;
|
|
|
|
/// iOS voice-processing mode.
|
|
final BridgeIosVoiceProcessingMode iosMode;
|
|
|
|
/// Processing backend.
|
|
final BridgeAudioBackend processingBackend;
|
|
|
|
/// VAD backend.
|
|
final BridgeVadBackend vadBackend;
|
|
|
|
/// AEC owner.
|
|
final BridgeEffectOwner aec;
|
|
|
|
/// Noise suppression owner.
|
|
final BridgeEffectOwner ns;
|
|
|
|
/// AGC owner.
|
|
final BridgeEffectOwner agc;
|
|
|
|
/// High-pass filter enabled.
|
|
final bool hpfEnabled;
|
|
|
|
/// Limiter enabled.
|
|
final bool limiterEnabled;
|
|
|
|
/// VAD hangover in ms.
|
|
final int vadHangoverMs;
|
|
|
|
/// VAD pre-roll in ms.
|
|
final int vadPreRollMs;
|
|
|
|
/// Minimum transmit duration in ms.
|
|
final int vadMinTxMs;
|
|
|
|
/// Debug WAV dump enabled.
|
|
final bool debugWavDumpEnabled;
|
|
|
|
const BridgeAudioProcessingConfig({
|
|
required this.route,
|
|
required this.iosMode,
|
|
required this.processingBackend,
|
|
required this.vadBackend,
|
|
required this.aec,
|
|
required this.ns,
|
|
required this.agc,
|
|
required this.hpfEnabled,
|
|
required this.limiterEnabled,
|
|
required this.vadHangoverMs,
|
|
required this.vadPreRollMs,
|
|
required this.vadMinTxMs,
|
|
required this.debugWavDumpEnabled,
|
|
});
|
|
|
|
@override
|
|
int get hashCode =>
|
|
route.hashCode ^
|
|
iosMode.hashCode ^
|
|
processingBackend.hashCode ^
|
|
vadBackend.hashCode ^
|
|
aec.hashCode ^
|
|
ns.hashCode ^
|
|
agc.hashCode ^
|
|
hpfEnabled.hashCode ^
|
|
limiterEnabled.hashCode ^
|
|
vadHangoverMs.hashCode ^
|
|
vadPreRollMs.hashCode ^
|
|
vadMinTxMs.hashCode ^
|
|
debugWavDumpEnabled.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgeAudioProcessingConfig &&
|
|
runtimeType == other.runtimeType &&
|
|
route == other.route &&
|
|
iosMode == other.iosMode &&
|
|
processingBackend == other.processingBackend &&
|
|
vadBackend == other.vadBackend &&
|
|
aec == other.aec &&
|
|
ns == other.ns &&
|
|
agc == other.agc &&
|
|
hpfEnabled == other.hpfEnabled &&
|
|
limiterEnabled == other.limiterEnabled &&
|
|
vadHangoverMs == other.vadHangoverMs &&
|
|
vadPreRollMs == other.vadPreRollMs &&
|
|
vadMinTxMs == other.vadMinTxMs &&
|
|
debugWavDumpEnabled == other.debugWavDumpEnabled;
|
|
}
|
|
|
|
/// P1 audio-processing stats DTO.
|
|
class BridgeAudioProcessingStats {
|
|
/// Input dBFS.
|
|
final double inputDbfs;
|
|
|
|
/// Render dBFS.
|
|
final double renderDbfs;
|
|
|
|
/// Processed capture dBFS.
|
|
final double processedDbfs;
|
|
|
|
/// Latest VAD probability.
|
|
final double vadProbability;
|
|
|
|
/// VAD active.
|
|
final bool vadActive;
|
|
|
|
/// Currently transmitting.
|
|
final bool transmitting;
|
|
|
|
/// VAD backend.
|
|
final BridgeVadBackend vadBackend;
|
|
|
|
/// Fallback VAD active.
|
|
final bool vadFallbackActive;
|
|
|
|
/// Processing backend.
|
|
final BridgeAudioBackend processingBackend;
|
|
|
|
/// iOS voice-processing mode.
|
|
final BridgeIosVoiceProcessingMode iosVoiceProcessingMode;
|
|
|
|
/// Audio route.
|
|
final BridgeAudioRoute audioRoute;
|
|
|
|
/// Actual sample rate.
|
|
final int actualSampleRateHz;
|
|
|
|
/// Actual IO buffer frames.
|
|
final int actualIoBufferFrames;
|
|
|
|
/// Input overruns.
|
|
final BigInt inputOverruns;
|
|
|
|
/// Output underruns.
|
|
final BigInt outputUnderruns;
|
|
|
|
/// Callback xruns.
|
|
final BigInt callbackXruns;
|
|
|
|
/// Clipped samples.
|
|
final BigInt clippedSamples;
|
|
|
|
/// Number of effectively silent processed capture frames.
|
|
final BigInt zeroFrames;
|
|
|
|
/// Number of processed capture frames.
|
|
final BigInt captureFrames;
|
|
|
|
/// Input callbacks carrying 10 ms of audio.
|
|
final BigInt callbacks10Ms;
|
|
|
|
/// Input callbacks carrying 20 ms of audio.
|
|
final BigInt callbacks20Ms;
|
|
|
|
/// Input callbacks carrying other sizes.
|
|
final BigInt callbacksOther;
|
|
|
|
/// Sonora enabled.
|
|
final bool sonoraEnabled;
|
|
|
|
/// Platform voice processing enabled.
|
|
final bool platformVoiceProcessingEnabled;
|
|
|
|
const BridgeAudioProcessingStats({
|
|
required this.inputDbfs,
|
|
required this.renderDbfs,
|
|
required this.processedDbfs,
|
|
required this.vadProbability,
|
|
required this.vadActive,
|
|
required this.transmitting,
|
|
required this.vadBackend,
|
|
required this.vadFallbackActive,
|
|
required this.processingBackend,
|
|
required this.iosVoiceProcessingMode,
|
|
required this.audioRoute,
|
|
required this.actualSampleRateHz,
|
|
required this.actualIoBufferFrames,
|
|
required this.inputOverruns,
|
|
required this.outputUnderruns,
|
|
required this.callbackXruns,
|
|
required this.clippedSamples,
|
|
required this.zeroFrames,
|
|
required this.captureFrames,
|
|
required this.callbacks10Ms,
|
|
required this.callbacks20Ms,
|
|
required this.callbacksOther,
|
|
required this.sonoraEnabled,
|
|
required this.platformVoiceProcessingEnabled,
|
|
});
|
|
|
|
@override
|
|
int get hashCode =>
|
|
inputDbfs.hashCode ^
|
|
renderDbfs.hashCode ^
|
|
processedDbfs.hashCode ^
|
|
vadProbability.hashCode ^
|
|
vadActive.hashCode ^
|
|
transmitting.hashCode ^
|
|
vadBackend.hashCode ^
|
|
vadFallbackActive.hashCode ^
|
|
processingBackend.hashCode ^
|
|
iosVoiceProcessingMode.hashCode ^
|
|
audioRoute.hashCode ^
|
|
actualSampleRateHz.hashCode ^
|
|
actualIoBufferFrames.hashCode ^
|
|
inputOverruns.hashCode ^
|
|
outputUnderruns.hashCode ^
|
|
callbackXruns.hashCode ^
|
|
clippedSamples.hashCode ^
|
|
zeroFrames.hashCode ^
|
|
captureFrames.hashCode ^
|
|
callbacks10Ms.hashCode ^
|
|
callbacks20Ms.hashCode ^
|
|
callbacksOther.hashCode ^
|
|
sonoraEnabled.hashCode ^
|
|
platformVoiceProcessingEnabled.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgeAudioProcessingStats &&
|
|
runtimeType == other.runtimeType &&
|
|
inputDbfs == other.inputDbfs &&
|
|
renderDbfs == other.renderDbfs &&
|
|
processedDbfs == other.processedDbfs &&
|
|
vadProbability == other.vadProbability &&
|
|
vadActive == other.vadActive &&
|
|
transmitting == other.transmitting &&
|
|
vadBackend == other.vadBackend &&
|
|
vadFallbackActive == other.vadFallbackActive &&
|
|
processingBackend == other.processingBackend &&
|
|
iosVoiceProcessingMode == other.iosVoiceProcessingMode &&
|
|
audioRoute == other.audioRoute &&
|
|
actualSampleRateHz == other.actualSampleRateHz &&
|
|
actualIoBufferFrames == other.actualIoBufferFrames &&
|
|
inputOverruns == other.inputOverruns &&
|
|
outputUnderruns == other.outputUnderruns &&
|
|
callbackXruns == other.callbackXruns &&
|
|
clippedSamples == other.clippedSamples &&
|
|
zeroFrames == other.zeroFrames &&
|
|
captureFrames == other.captureFrames &&
|
|
callbacks10Ms == other.callbacks10Ms &&
|
|
callbacks20Ms == other.callbacks20Ms &&
|
|
callbacksOther == other.callbacksOther &&
|
|
sonoraEnabled == other.sonoraEnabled &&
|
|
platformVoiceProcessingEnabled ==
|
|
other.platformVoiceProcessingEnabled;
|
|
}
|
|
|
|
/// Bridge route class for P1 audio-processing policy.
|
|
enum BridgeAudioRoute {
|
|
/// Built-in speakerphone.
|
|
speaker,
|
|
|
|
/// Built-in receiver/earpiece.
|
|
earpiece,
|
|
|
|
/// Wired or USB headset.
|
|
wiredHeadset,
|
|
|
|
/// Bluetooth HFP duplex route.
|
|
bluetoothHfp,
|
|
|
|
/// Bluetooth A2DP output-only route.
|
|
bluetoothA2Dp,
|
|
|
|
/// Unknown route.
|
|
unknown,
|
|
}
|
|
|
|
/// Statistics from the audio engine.
|
|
class BridgeAudioStats {
|
|
/// Number of Opus frames sent since audio started.
|
|
final int framesSent;
|
|
|
|
/// Number of inbound voice packets decoded.
|
|
final int framesReceived;
|
|
|
|
/// Current push-to-talk state.
|
|
final bool pttActive;
|
|
|
|
const BridgeAudioStats({
|
|
required this.framesSent,
|
|
required this.framesReceived,
|
|
required this.pttActive,
|
|
});
|
|
|
|
@override
|
|
int get hashCode =>
|
|
framesSent.hashCode ^ framesReceived.hashCode ^ pttActive.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgeAudioStats &&
|
|
runtimeType == other.runtimeType &&
|
|
framesSent == other.framesSent &&
|
|
framesReceived == other.framesReceived &&
|
|
pttActive == other.pttActive;
|
|
}
|
|
|
|
/// Bookmark DTO mirroring [`chanora_core::Bookmark`].
|
|
class BridgeBookmark {
|
|
/// Row id assigned by SQLite. Use `0` when adding new rows;
|
|
/// the returned id is then meaningful.
|
|
final PlatformInt64 id;
|
|
|
|
/// User-facing label.
|
|
final String displayName;
|
|
|
|
/// `hostname[:port]` or TSDNS name.
|
|
final String host;
|
|
|
|
/// Nickname to use for this bookmark.
|
|
final String nickname;
|
|
|
|
/// Optional remembered password. Empty string = none.
|
|
final String password;
|
|
|
|
const BridgeBookmark({
|
|
required this.id,
|
|
required this.displayName,
|
|
required this.host,
|
|
required this.nickname,
|
|
required this.password,
|
|
});
|
|
|
|
@override
|
|
int get hashCode =>
|
|
id.hashCode ^
|
|
displayName.hashCode ^
|
|
host.hashCode ^
|
|
nickname.hashCode ^
|
|
password.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgeBookmark &&
|
|
runtimeType == other.runtimeType &&
|
|
id == other.id &&
|
|
displayName == other.displayName &&
|
|
host == other.host &&
|
|
nickname == other.nickname &&
|
|
password == other.password;
|
|
}
|
|
|
|
/// Channel as seen by Dart. Matches `chanora_protocol::ChannelInfo`
|
|
/// but with primitive `u64` ids so the Dart side gets `BigInt`s
|
|
/// without any wrapper-type ceremony.
|
|
class BridgeChannel {
|
|
/// Stable channel id.
|
|
final BigInt id;
|
|
|
|
/// Parent channel id; 0 means top-level.
|
|
final BigInt parent;
|
|
|
|
/// Display name.
|
|
final String name;
|
|
|
|
/// Server-side ordering hint.
|
|
final PlatformInt64 order;
|
|
|
|
/// True when the server marks the channel as password-protected.
|
|
final bool hasPassword;
|
|
|
|
/// Talk power threshold required to speak in this channel.
|
|
/// None means no talk-power restriction.
|
|
final int? neededTalkPower;
|
|
|
|
const BridgeChannel({
|
|
required this.id,
|
|
required this.parent,
|
|
required this.name,
|
|
required this.order,
|
|
required this.hasPassword,
|
|
this.neededTalkPower,
|
|
});
|
|
|
|
@override
|
|
int get hashCode =>
|
|
id.hashCode ^
|
|
parent.hashCode ^
|
|
name.hashCode ^
|
|
order.hashCode ^
|
|
hasPassword.hashCode ^
|
|
neededTalkPower.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgeChannel &&
|
|
runtimeType == other.runtimeType &&
|
|
id == other.id &&
|
|
parent == other.parent &&
|
|
name == other.name &&
|
|
order == other.order &&
|
|
hasPassword == other.hasPassword &&
|
|
neededTalkPower == other.neededTalkPower;
|
|
}
|
|
|
|
/// Client as seen by Dart.
|
|
class BridgeClient {
|
|
/// Stable client id.
|
|
final BigInt id;
|
|
|
|
/// Channel id the client is currently in.
|
|
final BigInt channel;
|
|
|
|
/// Nickname.
|
|
final String name;
|
|
|
|
/// True when this client has muted microphone/input capture.
|
|
final bool inputMuted;
|
|
|
|
/// True when this client has muted speaker/output audio.
|
|
final bool outputMuted;
|
|
|
|
/// True when recent inbound voice activity was observed for this client.
|
|
final bool isSpeaking;
|
|
|
|
/// True for TeamSpeak ServerQuery clients.
|
|
final bool isServerQuery;
|
|
|
|
/// Current talk power value assigned by the server.
|
|
final int talkPower;
|
|
|
|
/// True when the server has granted talk power regardless of numeric value.
|
|
final bool talkPowerGranted;
|
|
|
|
const BridgeClient({
|
|
required this.id,
|
|
required this.channel,
|
|
required this.name,
|
|
required this.inputMuted,
|
|
required this.outputMuted,
|
|
required this.isSpeaking,
|
|
required this.isServerQuery,
|
|
required this.talkPower,
|
|
required this.talkPowerGranted,
|
|
});
|
|
|
|
@override
|
|
int get hashCode =>
|
|
id.hashCode ^
|
|
channel.hashCode ^
|
|
name.hashCode ^
|
|
inputMuted.hashCode ^
|
|
outputMuted.hashCode ^
|
|
isSpeaking.hashCode ^
|
|
isServerQuery.hashCode ^
|
|
talkPower.hashCode ^
|
|
talkPowerGranted.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgeClient &&
|
|
runtimeType == other.runtimeType &&
|
|
id == other.id &&
|
|
channel == other.channel &&
|
|
name == other.name &&
|
|
inputMuted == other.inputMuted &&
|
|
outputMuted == other.outputMuted &&
|
|
isSpeaking == other.isSpeaking &&
|
|
isServerQuery == other.isServerQuery &&
|
|
talkPower == other.talkPower &&
|
|
talkPowerGranted == other.talkPowerGranted;
|
|
}
|
|
|
|
/// Bridge effect owner for AEC/NS/AGC.
|
|
enum BridgeEffectOwner {
|
|
/// Platform-owned effect.
|
|
platform,
|
|
|
|
/// Sonora-owned effect.
|
|
sonora,
|
|
|
|
/// WebRTC APM-owned effect.
|
|
webrtcApm,
|
|
|
|
/// Conservative route-managed setting.
|
|
conservative,
|
|
|
|
/// Disabled.
|
|
off,
|
|
}
|
|
|
|
@freezed
|
|
sealed class BridgeEvent with _$BridgeEvent {
|
|
const BridgeEvent._();
|
|
|
|
/// Initial connect succeeded, or a reconnect attempt succeeded.
|
|
const factory BridgeEvent.connected({
|
|
/// Server name reported by the server snapshot.
|
|
required String serverName,
|
|
}) = BridgeEvent_Connected;
|
|
|
|
/// Connection lost; supervisor will retry.
|
|
const factory BridgeEvent.lost({
|
|
/// Reason classification from the protocol layer.
|
|
required String reason,
|
|
}) = BridgeEvent_Lost;
|
|
|
|
/// Supervisor is sleeping before its next reconnect attempt.
|
|
const factory BridgeEvent.reconnecting({
|
|
/// 1-based attempt counter for the current outage.
|
|
required int attempt,
|
|
|
|
/// Seconds the supervisor will sleep before this attempt.
|
|
required int delaySecs,
|
|
}) = BridgeEvent_Reconnecting;
|
|
|
|
/// Session ended (user-requested disconnect or unrecoverable).
|
|
const factory BridgeEvent.disconnected({
|
|
/// Reason classification.
|
|
required String reason,
|
|
}) = BridgeEvent_Disconnected;
|
|
|
|
/// Audio engine started.
|
|
const factory BridgeEvent.audioStarted() = BridgeEvent_AudioStarted;
|
|
|
|
/// Audio engine stopped.
|
|
const factory BridgeEvent.audioStopped() = BridgeEvent_AudioStopped;
|
|
|
|
/// Snapshot probe observed a change in channel/client counts.
|
|
/// UI uses this to drive an auto-refresh without active
|
|
/// polling.
|
|
const factory BridgeEvent.snapshotChanged({
|
|
/// Latest channel count.
|
|
required int channels,
|
|
|
|
/// Latest client count.
|
|
required int clients,
|
|
}) = BridgeEvent_SnapshotChanged;
|
|
|
|
/// Detected desktop Push-to-Talk capability (gen2 v0.9.3,
|
|
/// DEC-023..028). The fields carry only privacy-safe values per
|
|
/// DEC-027: the capability level, a stable backend identifier,
|
|
/// and the bound input class. No key codes, scan codes, or
|
|
/// virtual-key values cross this boundary.
|
|
const factory BridgeEvent.pttCapability({
|
|
/// Stable capability identifier (`"L0Focused"`,
|
|
/// `"L1GlobalShortcut"`, `"L2GlobalHoldToTalk"`,
|
|
/// `"L3GlobalWithMouseButtons"`, or `"L4DeviceAware"`).
|
|
required String level,
|
|
|
|
/// Stable backend identifier (e.g. `"focused"`).
|
|
required String backendId,
|
|
|
|
/// Coarse bound input class (e.g. `"keyboard"`,
|
|
/// `"mouse-side-button"`); empty when no binding is active.
|
|
required String boundInputClass,
|
|
}) = BridgeEvent_PttCapability;
|
|
|
|
/// Voice subsystem state snapshot (SDD-094). The Flutter
|
|
/// VoiceBar listens to this stream.
|
|
const factory BridgeEvent.voiceState({
|
|
/// True when the session is currently joined to a voice
|
|
/// channel and the audio engine is running.
|
|
required bool inChannel,
|
|
|
|
/// Active transmit mode.
|
|
required BridgeTransmitMode transmitMode,
|
|
|
|
/// True when the hard-mute clamp is engaged.
|
|
required bool mute,
|
|
|
|
/// Current release-tail in milliseconds (0..=500).
|
|
required int releaseTailMs,
|
|
|
|
/// Last confirmed authoritative channel id.
|
|
BigInt? currentChannelId,
|
|
|
|
/// Non-authoritative pending join target channel id.
|
|
BigInt? pendingTargetChannelId,
|
|
|
|
/// Whether a new join intent is currently allowed.
|
|
required bool canJoin,
|
|
|
|
/// Whether leave intent is currently allowed.
|
|
required bool canLeave,
|
|
|
|
/// Join projection sync state.
|
|
required BridgeVoiceJoinSyncState joinSyncState,
|
|
|
|
/// Last stable join error code, if any.
|
|
BridgeVoiceJoinErrorCode? joinErrorCode,
|
|
}) = BridgeEvent_VoiceState;
|
|
|
|
/// iOS audio interruption state (SDD-101).
|
|
const factory BridgeEvent.interruptionState({
|
|
/// True when interruption began, false when it ended.
|
|
required bool began,
|
|
|
|
/// Resume recommendation from the platform. False on begin.
|
|
required bool shouldResume,
|
|
}) = BridgeEvent_InterruptionState;
|
|
|
|
/// SDD-106 §5: resolved platform-level permission state. On
|
|
/// Android this is published by the JNI hook in
|
|
/// [`crate::permission_jni`] whenever
|
|
/// `AndroidPermissionRequester` reports a state transition
|
|
/// (initial grant, denial, permanent denial, or mid-session
|
|
/// revocation). The audio engine's `TransmitModeSelector`
|
|
/// observes the `RECORD_AUDIO` variant of this event as an
|
|
/// authoritative clamp on the transmit gate per SDD-106 §6
|
|
/// (and SRS-209's listen-only fail-safe).
|
|
///
|
|
/// Carries the canonical Android permission string in
|
|
/// `permission` (e.g. `"android.permission.RECORD_AUDIO"`)
|
|
/// and the resolved state in `state`. No raw user input,
|
|
/// timestamps, or other PII cross this boundary.
|
|
const factory BridgeEvent.permissionState({
|
|
/// Canonical Android permission identifier.
|
|
required String permission,
|
|
|
|
/// Resolved permission state.
|
|
required PermissionStateKind state,
|
|
}) = BridgeEvent_PermissionState;
|
|
|
|
/// A text message received from the server.
|
|
const factory BridgeEvent.chatMessage({
|
|
/// Client id of the sender.
|
|
required BigInt senderId,
|
|
|
|
/// Nickname of the sender.
|
|
required String senderName,
|
|
|
|
/// Message content.
|
|
required String message,
|
|
|
|
/// Target scope (server/channel/private/poke).
|
|
required BridgeMessageTarget target,
|
|
}) = BridgeEvent_ChatMessage;
|
|
|
|
/// Human-readable server activity surfaced from protocol bookkeeping events.
|
|
const factory BridgeEvent.serverActivity({
|
|
/// TeamSpeak-style activity line.
|
|
required String message,
|
|
}) = BridgeEvent_ServerActivity;
|
|
|
|
/// Audio route changed (speaker/earpiece/BT/wired).
|
|
const factory BridgeEvent.audioRouteChanged({
|
|
/// The new audio route.
|
|
required BridgeAudioRoute route,
|
|
}) = BridgeEvent_AudioRouteChanged;
|
|
}
|
|
|
|
/// Bridge iOS voice-processing mode.
|
|
enum BridgeIosVoiceProcessingMode {
|
|
/// Shipping VPIO path.
|
|
platformVoiceProcessing,
|
|
|
|
/// Experimental Sonora path.
|
|
sonoraExperimental,
|
|
}
|
|
|
|
@freezed
|
|
sealed class BridgeMessageTarget with _$BridgeMessageTarget {
|
|
const BridgeMessageTarget._();
|
|
|
|
/// Broadcast to entire server.
|
|
const factory BridgeMessageTarget.server() = BridgeMessageTarget_Server;
|
|
|
|
/// Broadcast to current channel.
|
|
const factory BridgeMessageTarget.channel() = BridgeMessageTarget_Channel;
|
|
|
|
/// Private message to a specific client.
|
|
const factory BridgeMessageTarget.client(BigInt field0) =
|
|
BridgeMessageTarget_Client;
|
|
|
|
/// Poke a specific client.
|
|
const factory BridgeMessageTarget.poke(BigInt field0) =
|
|
BridgeMessageTarget_Poke;
|
|
}
|
|
|
|
/// Coarse OS-reported network state. Mirrors
|
|
/// [`chanora_core::NetworkState`] across the bridge.
|
|
enum BridgeNetworkState {
|
|
/// No signal seen yet.
|
|
unknown,
|
|
|
|
/// OS reports a usable network.
|
|
online,
|
|
|
|
/// OS reports no network.
|
|
offline,
|
|
}
|
|
|
|
/// Persisted PTT binding display state for the UI.
|
|
class BridgePttBinding {
|
|
/// Stable input category string (`""`, `"keyboard"`, or
|
|
/// `"mouse-side-button"`).
|
|
final String inputClass;
|
|
|
|
/// Display-only key label; empty when no binding is active.
|
|
final String keyLabel;
|
|
|
|
const BridgePttBinding({required this.inputClass, required this.keyLabel});
|
|
|
|
@override
|
|
int get hashCode => inputClass.hashCode ^ keyLabel.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgePttBinding &&
|
|
runtimeType == other.runtimeType &&
|
|
inputClass == other.inputClass &&
|
|
keyLabel == other.keyLabel;
|
|
}
|
|
|
|
/// Privacy-safe PTT capability descriptor for the UI.
|
|
class BridgePttDescriptor {
|
|
/// Stable capability level name.
|
|
final String level;
|
|
|
|
/// Stable backend identifier.
|
|
final String backendId;
|
|
|
|
/// Coarse bound input class; empty when no binding is active.
|
|
final String boundInputClass;
|
|
|
|
const BridgePttDescriptor({
|
|
required this.level,
|
|
required this.backendId,
|
|
required this.boundInputClass,
|
|
});
|
|
|
|
@override
|
|
int get hashCode =>
|
|
level.hashCode ^ backendId.hashCode ^ boundInputClass.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgePttDescriptor &&
|
|
runtimeType == other.runtimeType &&
|
|
level == other.level &&
|
|
backendId == other.backendId &&
|
|
boundInputClass == other.boundInputClass;
|
|
}
|
|
|
|
/// Coarse PTT input class (gen2 v0.9.3 / DEC-026). Stable strings;
|
|
/// the bridge never carries raw key codes.
|
|
enum BridgePttInputClass {
|
|
/// No binding is active.
|
|
none,
|
|
|
|
/// A keyboard key.
|
|
keyboard,
|
|
|
|
/// A mouse side button (Mouse4 / Mouse5).
|
|
mouseSideButton,
|
|
}
|
|
|
|
/// Server snapshot as seen by Dart.
|
|
class BridgeSnapshot {
|
|
/// Server name.
|
|
final String serverName;
|
|
|
|
/// Welcome banner text.
|
|
final String welcomeMessage;
|
|
|
|
/// Server platform (e.g. "Linux").
|
|
final String platform;
|
|
|
|
/// Server version string.
|
|
final String version;
|
|
|
|
/// Channels currently known.
|
|
final List<BridgeChannel> channels;
|
|
|
|
/// Clients currently known.
|
|
final List<BridgeClient> clients;
|
|
|
|
/// Our own client id. Useful for the UI to highlight our row
|
|
/// in the client list and to know which channel we are in
|
|
/// without trusting the optimistic local state.
|
|
final BigInt ownClientId;
|
|
|
|
const BridgeSnapshot({
|
|
required this.serverName,
|
|
required this.welcomeMessage,
|
|
required this.platform,
|
|
required this.version,
|
|
required this.channels,
|
|
required this.clients,
|
|
required this.ownClientId,
|
|
});
|
|
|
|
@override
|
|
int get hashCode =>
|
|
serverName.hashCode ^
|
|
welcomeMessage.hashCode ^
|
|
platform.hashCode ^
|
|
version.hashCode ^
|
|
channels.hashCode ^
|
|
clients.hashCode ^
|
|
ownClientId.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is BridgeSnapshot &&
|
|
runtimeType == other.runtimeType &&
|
|
serverName == other.serverName &&
|
|
welcomeMessage == other.welcomeMessage &&
|
|
platform == other.platform &&
|
|
version == other.version &&
|
|
channels == other.channels &&
|
|
clients == other.clients &&
|
|
ownClientId == other.ownClientId;
|
|
}
|
|
|
|
/// Voice transmit mode mirror (SDD-095). Schema-controlled enum;
|
|
/// the wire encoding matches [`chanora_core::TransmitMode::as_u8`].
|
|
enum BridgeTransmitMode {
|
|
/// Push-to-talk (default).
|
|
ptt,
|
|
|
|
/// Continuous transmission while in channel and not muted.
|
|
continuous,
|
|
|
|
/// Voice-activity detection — reserved per DEC-030; v1 behaves
|
|
/// as `Continuous`.
|
|
voiceActivity,
|
|
}
|
|
|
|
/// Bridge VAD backend.
|
|
enum BridgeVadBackend {
|
|
/// Silero ONNX VAD.
|
|
sileroOnnx,
|
|
|
|
/// WebRTC fallback VAD.
|
|
webrtcVad,
|
|
|
|
/// Debug energy VAD.
|
|
energyDebug,
|
|
|
|
/// VAD disabled.
|
|
disabled,
|
|
}
|
|
|
|
/// Bridge mirror of stable join error/status codes.
|
|
enum BridgeVoiceJoinErrorCode {
|
|
/// Duplicate same-target join intent was coalesced.
|
|
duplicateSameTargetCoalesced,
|
|
|
|
/// A different target was requested while one is already pending.
|
|
joinAlreadyPendingDifferentTarget,
|
|
|
|
/// Join denied by server policy/permission.
|
|
joinDenied,
|
|
|
|
/// Join failed due to protocol-level error.
|
|
joinProtocolFailure,
|
|
|
|
/// Join failed due to transport/network error.
|
|
joinNetworkFailure,
|
|
|
|
/// Join timed out awaiting confirmation.
|
|
joinTimeout,
|
|
|
|
/// Pending join was superseded by user leave.
|
|
joinSupersededByLeave,
|
|
|
|
/// Stale join outcome was ignored.
|
|
joinStaleOutcomeIgnored,
|
|
|
|
/// Authoritative membership reconciled to different channel.
|
|
joinReconciledDifferentChannel,
|
|
|
|
/// Join command was rejected before send acceptance.
|
|
joinCommandRejectedBeforeSend,
|
|
|
|
/// Join intent rejected while reducer synchronizing.
|
|
joinCannotStartWhileSynchronizing,
|
|
}
|
|
|
|
/// Bridge mirror of core join projection sync state.
|
|
enum BridgeVoiceJoinSyncState {
|
|
/// Reducer is ready to accept channel actions.
|
|
ready,
|
|
|
|
/// Reducer is waiting on initial snapshot reconciliation.
|
|
synchronizingInitialSnapshot,
|
|
|
|
/// Reducer is waiting on reconnect snapshot reconciliation.
|
|
synchronizingReconnect,
|
|
}
|
|
|
|
/// Schema-controlled mirror of the Kotlin
|
|
/// `AndroidPermissionRequester.PermissionState` sealed class
|
|
/// (SDD-106 §5). Crosses the bridge as an enum so the Dart side can
|
|
/// `switch` on it exhaustively without parsing strings.
|
|
enum PermissionStateKind {
|
|
/// Permission granted by the user; capture may proceed.
|
|
granted,
|
|
|
|
/// Permission denied (re-promptable).
|
|
denied,
|
|
|
|
/// Permission permanently denied — the UI is expected to
|
|
/// deep-link to system settings (SDD-106 §3).
|
|
permanentlyDenied,
|
|
|
|
/// Any state string that did not match the contract above.
|
|
/// Treated identically to `Denied` by the transmit clamp
|
|
/// (fail-safe per SRS-209).
|
|
unknown,
|
|
}
|