forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpigeon_interfaces.dart
More file actions
387 lines (311 loc) · 13.5 KB
/
Copy pathpigeon_interfaces.dart
File metadata and controls
387 lines (311 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(
PigeonOptions(
dartOut: 'lib/gen/pigeon_communicator.g.dart',
dartOptions: DartOptions(),
swiftOut: 'ios/Runner/PigeonCommunicator.g.swift',
swiftOptions: SwiftOptions(),
kotlinOut: 'android/app/src/main/kotlin/com/friend/ios/PigeonCommunicator.g.kt',
kotlinOptions: KotlinOptions(package: 'com.friend.ios'),
dartPackageName: 'omi_pigeon',
),
)
// =============================================================================
// Watch Recorder APIs
// =============================================================================
@HostApi()
abstract class WatchRecorderHostAPI {
@SwiftFunction('startRecording()')
void startRecording();
@SwiftFunction('stopRecording()')
void stopRecording();
@SwiftFunction('sendAudioData(audioData:)')
void sendAudioData(Uint8List audioData);
@SwiftFunction('sendAudioChunk(audioChunk:chunkIndex:isLast:sampleRate:)')
void sendAudioChunk(Uint8List audioChunk, int chunkIndex, bool isLast, double sampleRate);
@SwiftFunction('isWatchPaired()')
bool isWatchPaired();
@SwiftFunction('isWatchReachable()')
bool isWatchReachable();
@SwiftFunction('isWatchSessionSupported()')
bool isWatchSessionSupported();
@SwiftFunction('isWatchAppInstalled()')
bool isWatchAppInstalled();
@SwiftFunction('requestWatchMicrophonePermission()')
void requestWatchMicrophonePermission();
@SwiftFunction('requestMainAppMicrophonePermission()')
void requestMainAppMicrophonePermission();
@SwiftFunction('checkMainAppMicrophonePermission()')
bool checkMainAppMicrophonePermission();
@SwiftFunction('getWatchBatteryLevel()')
double getWatchBatteryLevel();
@SwiftFunction('getWatchBatteryState()')
int getWatchBatteryState();
@SwiftFunction('requestWatchBatteryUpdate()')
void requestWatchBatteryUpdate();
@SwiftFunction('getWatchInfo()')
Map<String, String> getWatchInfo();
}
@FlutterApi()
abstract class WatchRecorderFlutterAPI {
void onRecordingStarted();
void onRecordingStopped();
void onAudioData(Uint8List audioData);
void onAudioChunk(Uint8List audioChunk, int chunkIndex, bool isLast, double sampleRate);
void onRecordingError(String error);
void onMicrophonePermissionResult(bool granted);
void onMainAppMicrophonePermissionResult(bool granted);
void onWatchBatteryUpdate(double batteryLevel, int batteryState);
}
// =============================================================================
// BLE APIs
// =============================================================================
/// Discovered BLE peripheral info passed from native to Dart.
class BlePeripheral {
final String uuid;
final String name;
final int rssi;
final List<String> serviceUuids;
BlePeripheral({required this.uuid, required this.name, required this.rssi, required this.serviceUuids});
}
/// Discovered BLE service with its characteristic UUIDs.
class BleService {
final String uuid;
final List<String> characteristicUuids;
BleService({required this.uuid, required this.characteristicUuids});
}
/// A single disconnect event stored in native preferences.
class BleDisconnectEvent {
final int timestamp;
final String reason;
final int reasonCode;
final bool isManual;
/// Kind of event: "disconnect" (link lost after connect) or "fail_to_connect"
/// (connect attempt never established). Defaults to "disconnect" for legacy records.
final String eventType;
/// Last RSSI sample captured before this event (dBm). 0 if unknown.
final int lastRssi;
/// How long the link was established before this event (ms). 0 if unknown
/// or for fail_to_connect events.
final int connectionDurationMs;
/// App lifecycle state at the moment of the event: "foreground", "background",
/// or "inactive" (iOS transitioning). Empty string if unknown.
final String appState;
/// ms between this disconnect and the subsequent successful reconnect.
/// 0 while the device has not yet reconnected.
final int timeToReconnectMs;
/// RSSI trajectory over the ~15s before this event. One of:
/// "fading" — signal declined ≥10 dB before the drop (walk-away)
/// "sudden" — signal stable then link died (interference/stall/device off)
/// "gap" — no recent RSSI samples (keep-alive wasn't running)
/// "unknown" — insufficient samples to classify
/// Empty string on legacy records written before this field existed.
final String rssiTrend;
BleDisconnectEvent({
required this.timestamp,
required this.reason,
required this.reasonCode,
required this.isManual,
required this.eventType,
required this.lastRssi,
required this.connectionDurationMs,
required this.appState,
required this.timeToReconnectMs,
required this.rssiTrend,
});
}
/// A single battery level reading persisted by the native BLE layer.
class BleBatteryPoint {
final int timestamp;
final int level;
BleBatteryPoint({required this.timestamp, required this.level});
}
/// Diagnostics data read from native preferences on demand.
class BleDeviceDiagnostics {
final List<BleDisconnectEvent> disconnectHistory;
final int reconnectionCount;
final int connectedAt;
/// Count of connect attempts that never reached didConnect. Surfaces the
/// silent-failure path separately from established-then-dropped disconnects.
final int failToConnectCount;
/// BLE bytes consumed by native offline writers since the app most recently
/// entered the background. These packets intentionally never reach Dart.
final int nativeBackgroundBytesConsumed;
/// BLE notification packets represented by [nativeBackgroundBytesConsumed].
final int nativeBackgroundPacketsConsumed;
BleDeviceDiagnostics({
required this.disconnectHistory,
required this.reconnectionCount,
required this.connectedAt,
required this.failToConnectCount,
required this.nativeBackgroundBytesConsumed,
required this.nativeBackgroundPacketsConsumed,
});
}
/// Dart → Native: commands sent from Flutter to the native BLE module.
@HostApi()
abstract class BleHostApi {
@SwiftFunction('startScan(timeout:serviceUuids:)')
void startScan(int timeoutSeconds, List<String> serviceUuids);
@SwiftFunction('stopScan()')
void stopScan();
@SwiftFunction('manageDevice(uuid:requiresBond:)')
void manageDevice(String uuid, bool requiresBond);
@SwiftFunction('unmanageDevice(uuid:)')
void unmanageDevice(String uuid);
@async
@SwiftFunction('requestBond(uuid:)')
bool requestBond(String uuid);
// Characteristic operations
@async
@SwiftFunction('readCharacteristic(peripheralUuid:serviceUuid:characteristicUuid:)')
Uint8List readCharacteristic(String peripheralUuid, String serviceUuid, String characteristicUuid);
@async
@SwiftFunction('writeCharacteristic(peripheralUuid:serviceUuid:characteristicUuid:data:)')
void writeCharacteristic(String peripheralUuid, String serviceUuid, String characteristicUuid, Uint8List data);
@SwiftFunction('subscribeCharacteristic(peripheralUuid:serviceUuid:characteristicUuid:)')
void subscribeCharacteristic(String peripheralUuid, String serviceUuid, String characteristicUuid);
@SwiftFunction('unsubscribeCharacteristic(peripheralUuid:serviceUuid:characteristicUuid:)')
void unsubscribeCharacteristic(String peripheralUuid, String serviceUuid, String characteristicUuid);
// State
@SwiftFunction('getBluetoothState()')
String getBluetoothState();
/// (Android only) Show the system "enable Bluetooth" prompt. Resolves to true
/// once Bluetooth is on. No-op on iOS — returns whether the adapter is powered on.
@async
@SwiftFunction('enableBluetooth()')
bool enableBluetooth();
@SwiftFunction('isPeripheralConnected(uuid:)')
bool isPeripheralConnected(String uuid);
// Diagnostics
@SwiftFunction('startRssiStreaming(uuid:)')
void startRssiStreaming(String uuid);
@SwiftFunction('stopRssiStreaming(uuid:)')
void stopRssiStreaming(String uuid);
@async
@SwiftFunction('getDeviceDiagnostics(uuid:)')
BleDeviceDiagnostics getDeviceDiagnostics(String uuid);
@async
@SwiftFunction('getBatteryHistory(uuid:)')
List<BleBatteryPoint> getBatteryHistory(String uuid);
/// (Android only) Check if any CompanionDeviceManager association exists.
@SwiftFunction('hasCompanionDeviceAssociation()')
bool hasCompanionDeviceAssociation();
/// (Android only) Initiate CompanionDeviceManager association for a device.
@async
@SwiftFunction('requestCompanionDeviceAssociation(deviceAddress:)')
String requestCompanionDeviceAssociation(String deviceAddress);
}
@FlutterApi()
abstract class BleFlutterApi {
void onBluetoothStateChanged(String state);
void onPeripheralDiscovered(BlePeripheral peripheral);
void onDeviceReady(String peripheralUuid, List<BleService> services);
void onPeripheralDisconnected(String peripheralUuid, String? error);
void onCharacteristicValueUpdated(
String peripheralUuid,
String serviceUuid,
String characteristicUuid,
Uint8List value,
);
void onRssiUpdate(String peripheralUuid, int rssi);
void onStateRestored(List<String> peripheralUuids);
/// Native batch writer finalized a recording file (rotation / gap / stop) so
/// Dart can rescan the recordings dir without waiting for a disconnect.
void onBatchRecordingFinalized(String fileName);
}
// =============================================================================
// Ray-Ban Meta (Meta Wearables Device Access Toolkit) APIs
// =============================================================================
/// A pair of Ray-Ban Meta glasses reported by the Meta Wearables toolkit.
class RayBanMetaGlasses {
final String id;
final String name;
RayBanMetaGlasses({required this.id, required this.name});
}
/// A Bluetooth Hands-Free Profile input exposed by iOS.
class BluetoothHfpInput {
final String uid;
final String name;
BluetoothHfpInput({required this.uid, required this.name});
}
/// Dart → native. Camera/photo capture goes through the Meta Wearables Device
/// Access Toolkit (DAT); the toolkit has no microphone API, so audio capture
/// uses the platform Bluetooth HFP route as Meta's docs prescribe. All methods
/// are safe to call on builds without the DAT SDK — getAvailabilityMode()
/// reports which mode this build supports.
@HostApi()
abstract class RayBanMetaHostAPI {
/// 'full' (DAT SDK linked + Meta app credentials configured),
/// 'audio_only' (no DAT — platform Bluetooth audio route only), or 'none'.
@SwiftFunction('getAvailabilityMode()')
String getAvailabilityMode();
@SwiftFunction('initialize()')
void initialize();
/// 'unregistered' | 'registering' | 'registered' ('unavailable' without DAT).
@SwiftFunction('getRegistrationState()')
String getRegistrationState();
/// Launches the Meta AI companion app to authorize this app for the glasses.
@SwiftFunction('startRegistration()')
void startRegistration();
@SwiftFunction('unregister()')
void unregister();
@async
@SwiftFunction('getAvailableGlasses()')
List<RayBanMetaGlasses> getAvailableGlasses();
@SwiftFunction('connect(deviceId:)')
void connect(String deviceId);
@SwiftFunction('disconnect()')
void disconnect();
/// 'disconnected' | 'connecting' | 'connected'.
@SwiftFunction('getConnectionState()')
String getConnectionState();
/// DAT camera permission for the glasses: resolves 'granted' | 'denied'.
@async
@SwiftFunction('requestCameraPermission()')
String requestCameraPermission();
/// 'granted' | 'denied' | 'not_determined' | 'unavailable'.
@async
@SwiftFunction('getCameraPermissionStatus()')
String getCameraPermissionStatus();
/// Starts capturing the glasses microphone over the Bluetooth HFP route and
/// streaming PCM16 mono frames to RayBanMetaFlutterAPI.onAudioFrame.
@SwiftFunction('startAudioCapture(inputUid:)')
void startAudioCapture(String? inputUid);
@SwiftFunction('stopAudioCapture()')
void stopAudioCapture();
/// True when the active audio input route is the glasses' Bluetooth HFP mic.
@SwiftFunction('isGlassesAudioRouteActive()')
bool isGlassesAudioRouteActive();
/// Bluetooth HFP input ports currently available, for the audio-only
/// fallback when the DAT SDK is not part of this build. The UID is the
/// stable identity; the user-visible name may change.
@SwiftFunction('getBluetoothHfpInputs()')
List<BluetoothHfpInput> getBluetoothHfpInputs();
/// Starts the DAT camera stream session so photo capture is ready. While
/// active the glasses' capture LED is on (hardware-enforced by Meta).
@SwiftFunction('startCamera()')
void startCamera();
@SwiftFunction('stopCamera()')
void stopCamera();
/// Captures one photo; result arrives via RayBanMetaFlutterAPI.onPhotoCaptured.
@SwiftFunction('capturePhoto()')
void capturePhoto();
}
/// Native → Dart events for Ray-Ban Meta.
@FlutterApi()
abstract class RayBanMetaFlutterAPI {
void onRegistrationStateChanged(String state);
void onGlassesDiscovered(RayBanMetaGlasses glasses);
void onConnectionStateChanged(String deviceId, String state);
/// PCM16 little-endian mono audio at [sampleRate] Hz from the glasses mic.
void onAudioFrame(Uint8List pcm16Frame, double sampleRate);
/// Whether the glasses' HFP mic is the active input route right now.
void onAudioRouteChanged(bool glassesRouteActive);
/// JPEG bytes plus clockwise orientation in degrees (0/90/180/270).
void onPhotoCaptured(Uint8List jpegBytes, int orientationDegrees);
/// 'stopped' | 'starting' | 'streaming' | 'paused'.
void onCameraStateChanged(String state);
void onCameraPermissionChanged(String status);
void onError(String code, String message);
}