omikit-plugin 4.0.2 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +654 -37
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/omikitplugin/OmiLocalCameraView.kt +94 -0
  4. package/android/src/main/java/com/omikitplugin/OmiRemoteCameraView.kt +117 -0
  5. package/android/src/main/java/com/omikitplugin/OmikitPluginModule.kt +24 -17
  6. package/android/src/main/java/com/omikitplugin/OmikitPluginPackage.kt +11 -8
  7. package/ios/CallProcess/CallManager.swift +99 -29
  8. package/ios/Library/OmikitPlugin.m +18 -0
  9. package/ios/Library/OmikitPlugin.swift +233 -1
  10. package/ios/OmikitPlugin-Bridging-Header.h +1 -0
  11. package/ios/OmikitPlugin.xcodeproj/project.pbxproj +4 -4
  12. package/ios/VideoCall/OmiLocalCameraViewBridge.m +14 -0
  13. package/ios/VideoCall/OmiLocalCameraViewManager.swift +41 -0
  14. package/ios/VideoCall/OmiRemoteCameraViewBridge.m +14 -0
  15. package/ios/VideoCall/OmiRemoteCameraViewManager.swift +40 -0
  16. package/lib/commonjs/NativeOmikitPlugin.js +2 -1
  17. package/lib/commonjs/NativeOmikitPlugin.js.map +1 -1
  18. package/lib/commonjs/index.js.map +1 -1
  19. package/lib/commonjs/omi_audio_type.js +5 -7
  20. package/lib/commonjs/omi_audio_type.js.map +1 -1
  21. package/lib/commonjs/omi_call_state.js +5 -3
  22. package/lib/commonjs/omi_call_state.js.map +1 -1
  23. package/lib/commonjs/omi_local_camera.js +19 -17
  24. package/lib/commonjs/omi_local_camera.js.map +1 -1
  25. package/lib/commonjs/omi_remote_camera.js +20 -17
  26. package/lib/commonjs/omi_remote_camera.js.map +1 -1
  27. package/lib/commonjs/omi_start_call_status.js +5 -24
  28. package/lib/commonjs/omi_start_call_status.js.map +1 -1
  29. package/lib/commonjs/omikit.js +56 -3
  30. package/lib/commonjs/omikit.js.map +1 -1
  31. package/lib/commonjs/types/index.d.js.map +1 -1
  32. package/lib/module/NativeOmikitPlugin.js.map +1 -1
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/omi_audio_type.js +4 -7
  35. package/lib/module/omi_audio_type.js.map +1 -1
  36. package/lib/module/omi_call_state.js +4 -3
  37. package/lib/module/omi_call_state.js.map +1 -1
  38. package/lib/module/omi_local_camera.js +19 -18
  39. package/lib/module/omi_local_camera.js.map +1 -1
  40. package/lib/module/omi_remote_camera.js +20 -18
  41. package/lib/module/omi_remote_camera.js.map +1 -1
  42. package/lib/module/omi_start_call_status.js +4 -24
  43. package/lib/module/omi_start_call_status.js.map +1 -1
  44. package/lib/module/omikit.js +49 -1
  45. package/lib/module/omikit.js.map +1 -1
  46. package/lib/module/types/index.d.js.map +1 -1
  47. package/omikit-plugin.podspec +1 -1
  48. package/package.json +2 -11
  49. package/react-native.config.js +14 -0
  50. package/src/NativeOmikitPlugin.ts +1 -0
  51. package/src/omi_call_state.tsx +1 -0
  52. package/src/omi_local_camera.tsx +15 -19
  53. package/src/omi_remote_camera.tsx +16 -19
  54. package/src/omikit.tsx +63 -0
  55. package/src/types/index.d.ts +344 -62
  56. package/android/src/main/java/com/omikitplugin/FLLocalCameraModule.kt +0 -34
  57. package/android/src/main/java/com/omikitplugin/FLLocalCameraView.kt +0 -44
  58. package/android/src/main/java/com/omikitplugin/FLRemoteCameraModule.kt +0 -37
  59. package/android/src/main/java/com/omikitplugin/FLRemoteCameraView.kt +0 -23
  60. package/ios/VideoCall/FLLocalCameraView.m +0 -17
  61. package/ios/VideoCall/FLLocalCameraView.swift +0 -44
  62. package/ios/VideoCall/FLRemoteCameraView.m +0 -18
  63. package/ios/VideoCall/FLRemoteCameraView.swift +0 -124
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  The [omikit-plugin](https://www.npmjs.com/package/omikit-plugin) enables VoIP/SIP calling via the OMICALL platform with support for both Old and **New Architecture** (TurboModules + Fabric).
4
4
 
5
- **Status:** Active maintenance | **Version:** 4.0.1
5
+ **Status:** Active maintenance | **Version:** 4.1.1
6
6
 
7
7
  ---
8
8
 
@@ -46,8 +46,37 @@ The [omikit-plugin](https://www.npmjs.com/package/omikit-plugin) enables VoIP/SI
46
46
 
47
47
  | Platform | SDK | Version |
48
48
  |----------|-----|---------|
49
- | Android | OMIKIT | 2.6.4 |
50
- | iOS | OmiKit | 1.10.34 |
49
+ | Android | OMIKIT | 2.6.6 |
50
+ | iOS | OmiKit | 1.11.4 |
51
+
52
+ ### Platform Requirements
53
+
54
+ | | Android | iOS |
55
+ |--|---------|-----|
56
+ | **Min SDK** | API 24 (Android 7.0) | iOS 13.0 |
57
+ | **Target SDK** | API 36 (Android 16) | — |
58
+ | **Compile SDK** | API 36 | — |
59
+
60
+ ### Device Requirements
61
+
62
+ | Requirement | Platform | Notes |
63
+ |-------------|----------|-------|
64
+ | **Physical device** | iOS (required) | iOS Simulator is **not supported** — OmiKit binary is arm64 device-only |
65
+ | **Physical device** | Android (recommended) | Emulator works for basic UI testing but VoIP/audio routing is unreliable |
66
+ | **Google Play Services** | Android (required) | Required for FCM push notifications |
67
+ | **Microphone** | Both (required) | Required for all calls |
68
+ | **Camera** | Both (optional) | Only required for video calls |
69
+ | **Internet** | Both (required) | SIP registration + RTP media streaming |
70
+
71
+ ### Package Size
72
+
73
+ | Component | Size |
74
+ |-----------|------|
75
+ | npm package (total) | ~353 KB |
76
+ | Android native code | ~4.7 MB |
77
+ | iOS native code | ~176 KB |
78
+
79
+ > **Note:** These sizes are for the plugin only. The native SDKs (OmiKit/OMIKIT) are installed separately via CocoaPods/Maven and will add to the final app size.
51
80
 
52
81
  ---
53
82
 
@@ -102,7 +131,32 @@ Add to `android/app/src/main/AndroidManifest.xml`:
102
131
  >
103
132
  > Make sure to add the `tools` namespace to your manifest tag: `xmlns:tools="http://schemas.android.com/tools"`
104
133
 
105
- ### 2. Firebase Cloud Messaging (FCM)
134
+ ### 2. Incoming Call Activity (Required)
135
+
136
+ Your main Activity must handle incoming call intents from the SDK. Add the following `intent-filter` to your `MainActivity` in `AndroidManifest.xml`:
137
+
138
+ ```xml
139
+ <activity
140
+ android:name=".MainActivity"
141
+ android:showWhenLocked="true"
142
+ android:turnScreenOn="true"
143
+ android:launchMode="singleTask"
144
+ ...>
145
+
146
+ <!-- Incoming call intent-filter (required for lock screen) -->
147
+ <intent-filter>
148
+ <action android:name="${applicationId}.ACTION_INCOMING_CALL" />
149
+ <action android:name="android.intent.action.CALL" />
150
+ <category android:name="android.intent.category.DEFAULT" />
151
+ <data android:host="incoming_call" android:scheme="omisdk" />
152
+ </intent-filter>
153
+ </activity>
154
+ ```
155
+
156
+ > **Important:** The `${applicationId}.ACTION_INCOMING_CALL` action ensures incoming calls show correctly on **lock screen** for Android 9-14. Without this, the default dialer may intercept the intent instead of your app.
157
+
158
+ ### 3. Firebase Cloud Messaging (FCM)
159
+
106
160
 
107
161
  Add your `google-services.json` to `android/app/`.
108
162
 
@@ -112,7 +166,7 @@ In `android/app/build.gradle`:
112
166
  apply plugin: 'com.google.gms.google-services'
113
167
  ```
114
168
 
115
- ### 3. Maven Repository
169
+ ### 4. Maven Repository
116
170
 
117
171
  **Option A — `settings.gradle.kts` (recommended for new projects)**
118
172
 
@@ -172,7 +226,7 @@ OMI_TOKEN=omi_github_access_token
172
226
 
173
227
  > **Note:** Contact the OMICall development team to get `OMI_USER` and `OMI_TOKEN` credentials.
174
228
 
175
- ### 4. New Architecture (Optional)
229
+ ### 5. New Architecture (Optional)
176
230
 
177
231
  To enable New Architecture on Android, in `android/gradle.properties`:
178
232
 
@@ -209,13 +263,352 @@ Enable **Push Notifications** capability in Xcode for VoIP push (PushKit).
209
263
 
210
264
  ### 4. AppDelegate Setup
211
265
 
212
- In your `AppDelegate.mm` (or `.m`):
266
+ The AppDelegate template differs depending on your React Native version. Choose the one that matches your project:
267
+
268
+ #### RN 0.74 – 0.78 (RCTAppDelegate pattern)
269
+
270
+ <details>
271
+ <summary>AppDelegate.h</summary>
213
272
 
214
273
  ```objc
215
- #import <OmiKit/OmiKit-umbrella.h>
216
- #import <OmiKit/Constants.h>
274
+ #import <RCTAppDelegate.h>
275
+ #import <UIKit/UIKit.h>
276
+ #import <UserNotifications/UserNotifications.h>
277
+ #import <OmiKit/OmiKit.h>
278
+
279
+ @interface AppDelegate : RCTAppDelegate <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate>
280
+
281
+ @property (nonatomic, strong) UIWindow *window;
282
+ @property (nonatomic, strong) PushKitManager *pushkitManager;
283
+ @property (nonatomic, strong) CallKitProviderDelegate *provider;
284
+ @property (nonatomic, strong) PKPushRegistry *voipRegistry;
285
+
286
+ @end
287
+ ```
288
+
289
+ </details>
290
+
291
+ <details>
292
+ <summary>AppDelegate.mm</summary>
293
+
294
+ ```objc
295
+ #import "AppDelegate.h"
296
+ #import <Firebase.h>
297
+ #import <React/RCTBundleURLProvider.h>
298
+ #import <OmiKit/OmiKit.h>
299
+
300
+ #if __has_include("OmikitNotification.h")
301
+ #import "OmikitNotification.h"
302
+ #else
303
+ #import <omikit_plugin/OmikitNotification.h>
304
+ #endif
305
+
306
+ @implementation AppDelegate
307
+
308
+ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
309
+ {
310
+ self.moduleName = @"YourAppName"; // Replace with your app name
311
+
312
+ // ----- OmiKit Config ------
313
+ [OmiClient setEnviroment:KEY_OMI_APP_ENVIROMENT_SANDBOX
314
+ userNameKey:@"full_name"
315
+ maxCall:2
316
+ callKitImage:@"call_image"
317
+ typePushVoip:TYPE_PUSH_CALLKIT_DEFAULT];
318
+
319
+ self.provider = [[CallKitProviderDelegate alloc]
320
+ initWithCallManager:[OMISIPLib sharedInstance].callManager];
321
+ self.voipRegistry = [[PKPushRegistry alloc]
322
+ initWithQueue:dispatch_get_main_queue()];
323
+ self.pushkitManager = [[PushKitManager alloc]
324
+ initWithVoipRegistry:self.voipRegistry];
325
+
326
+ if (@available(iOS 10.0, *)) {
327
+ [UNUserNotificationCenter currentNotificationCenter].delegate =
328
+ (id<UNUserNotificationCenterDelegate>)self;
329
+ }
330
+
331
+ if ([FIRApp defaultApp] == nil) {
332
+ [FIRApp configure];
333
+ }
334
+ // ----- End OmiKit Config ------
335
+
336
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
337
+ }
338
+
339
+ // Handle foreground notifications
340
+ - (void)userNotificationCenter:(UNUserNotificationCenter *)center
341
+ willPresentNotification:(UNNotification *)notification
342
+ withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
343
+ {
344
+ completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
345
+ }
346
+
347
+ // Handle missed call notification tap
348
+ - (void)userNotificationCenter:(UNUserNotificationCenter *)center
349
+ didReceiveNotificationResponse:(UNNotificationResponse *)response
350
+ withCompletionHandler:(void (^)())completionHandler
351
+ {
352
+ NSDictionary *userInfo = response.notification.request.content.userInfo;
353
+ if (userInfo && [userInfo valueForKey:@"omisdkCallerNumber"]) {
354
+ [OmikitNotification didRecieve:userInfo];
355
+ }
356
+ completionHandler();
357
+ }
358
+
359
+ // Register push notification token
360
+ - (void)application:(UIApplication *)app
361
+ didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken
362
+ {
363
+ const unsigned char *data = (const unsigned char *)[devToken bytes];
364
+ NSMutableString *token = [NSMutableString string];
365
+ for (NSUInteger i = 0; i < [devToken length]; i++) {
366
+ [token appendFormat:@"%02.2hhX", data[i]];
367
+ }
368
+ [OmiClient setUserPushNotificationToken:[token copy]];
369
+ }
370
+
371
+ // Terminate all calls when app is killed
372
+ - (void)applicationWillTerminate:(UIApplication *)application {
373
+ @try {
374
+ [OmiClient OMICloseCall];
375
+ } @catch (NSException *exception) {}
376
+ }
377
+
378
+ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
379
+ {
380
+ return [self bundleURL];
381
+ }
382
+
383
+ - (NSURL *)bundleURL
384
+ {
385
+ #if DEBUG
386
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
387
+ #else
388
+ return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
389
+ #endif
390
+ }
391
+
392
+ @end
217
393
  ```
218
394
 
395
+ </details>
396
+
397
+ #### RN 0.79+ (RCTReactNativeFactory pattern)
398
+
399
+ RN 0.79+ uses `RCTReactNativeFactory` instead of `RCTAppDelegate`. Add OmiKit setup in your existing AppDelegate:
400
+
401
+ <details>
402
+ <summary>AppDelegate.swift (Swift template)</summary>
403
+
404
+ ```swift
405
+ import UIKit
406
+ import React
407
+ import ReactAppDependencyProvider
408
+ import OmiKit
409
+
410
+ @main
411
+ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
412
+ var window: UIWindow?
413
+ var provider: CallKitProviderDelegate?
414
+ var pushkitManager: PushKitManager?
415
+ var voipRegistry: PKPushRegistry?
416
+
417
+ func application(_ application: UIApplication,
418
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
419
+ // React Native setup
420
+ let delegate = ReactNativeDelegate()
421
+ let factory = RCTReactNativeFactory(delegate: delegate)
422
+ delegate.dependencyProvider = RCTAppDependencyProvider()
423
+
424
+ window = UIWindow(frame: UIScreen.main.bounds)
425
+ factory.startReactNative(
426
+ withModuleName: "YourAppName",
427
+ in: window,
428
+ launchOptions: launchOptions
429
+ )
430
+
431
+ // ----- OmiKit Config ------
432
+ #ifdef DEBUG
433
+ OmiClient.setEnviroment(KEY_OMI_APP_ENVIROMENT_SANDBOX,
434
+ userNameKey: "full_name",
435
+ maxCall: 1,
436
+ callKitImage: "call_image",
437
+ typePushVoip: TYPE_PUSH_CALLKIT_DEFAULT)
438
+ #else
439
+ OmiClient.setEnviroment(KEY_OMI_APP_ENVIROMENT_PRODUCTION,
440
+ userNameKey: "full_name",
441
+ maxCall: 1,
442
+ callKitImage: "call_image",
443
+ typePushVoip: TYPE_PUSH_CALLKIT_DEFAULT)
444
+ #endif
445
+
446
+ provider = CallKitProviderDelegate(callManager: OMISIPLib.sharedInstance().callManager)
447
+ voipRegistry = PKPushRegistry(queue: .main)
448
+ pushkitManager = PushKitManager(voipRegistry: voipRegistry!)
449
+
450
+ UNUserNotificationCenter.current().delegate = self
451
+ FirebaseApp.configure()
452
+ // ----- End OmiKit Config ------
453
+
454
+ return true
455
+ }
456
+
457
+ // Handle missed call notification tap
458
+ func userNotificationCenter(_ center: UNUserNotificationCenter,
459
+ didReceive response: UNNotificationResponse,
460
+ withCompletionHandler completionHandler: @escaping () -> Void) {
461
+ let userInfo = response.notification.request.content.userInfo
462
+ if userInfo["omisdkCallerNumber"] != nil {
463
+ OmikitNotification.didRecieve(userInfo)
464
+ }
465
+ completionHandler()
466
+ }
467
+
468
+ // Register push notification token
469
+ func application(_ application: UIApplication,
470
+ didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
471
+ let token = deviceToken.map { String(format: "%02.2hhX", $0) }.joined()
472
+ OmiClient.setUserPushNotificationToken(token)
473
+ }
474
+
475
+ // Terminate all calls when app is killed
476
+ func applicationWillTerminate(_ application: UIApplication) {
477
+ try? OmiClient.omiCloseCall()
478
+ }
479
+ }
480
+
481
+ class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
482
+ override func sourceURL(for bridge: RCTBridge) -> URL? {
483
+ return bundleURL()
484
+ }
485
+
486
+ override func bundleURL() -> URL? {
487
+ #if DEBUG
488
+ return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
489
+ #else
490
+ return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
491
+ #endif
492
+ }
493
+ }
494
+ ```
495
+
496
+ </details>
497
+
498
+ <details>
499
+ <summary>AppDelegate.mm (Objective-C template)</summary>
500
+
501
+ ```objc
502
+ #import "AppDelegate.h"
503
+ #import <Firebase.h>
504
+ #import <React/RCTBundleURLProvider.h>
505
+ #import <React/RCTReactNativeFactory.h>
506
+ #import <ReactAppDependencyProvider/RCTAppDependencyProvider.h>
507
+ #import <OmiKit/OmiKit.h>
508
+
509
+ #if __has_include("OmikitNotification.h")
510
+ #import "OmikitNotification.h"
511
+ #else
512
+ #import <omikit_plugin/OmikitNotification.h>
513
+ #endif
514
+
515
+ @interface ReactNativeDelegate : RCTDefaultReactNativeFactoryDelegate
516
+ @end
517
+
518
+ @implementation AppDelegate
519
+
520
+ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
521
+ {
522
+ ReactNativeDelegate *delegate = [ReactNativeDelegate new];
523
+ RCTReactNativeFactory *factory = [[RCTReactNativeFactory alloc] initWithDelegate:delegate];
524
+ delegate.dependencyProvider = [RCTAppDependencyProvider new];
525
+
526
+ self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
527
+ [factory startReactNativeWithModuleName:@"YourAppName" in:self.window launchOptions:launchOptions];
528
+
529
+ // ----- OmiKit Config ------
530
+ #ifdef DEBUG
531
+ [OmiClient setEnviroment:KEY_OMI_APP_ENVIROMENT_SANDBOX userNameKey:@"full_name" maxCall:1 callKitImage:@"icYourApp" typePushVoip:@"background"];
532
+ #else
533
+ [OmiClient setEnviroment:KEY_OMI_APP_ENVIROMENT_PRODUCTION userNameKey:@"full_name" maxCall:1 callKitImage:@"icYourApp" typePushVoip:@"background"];
534
+ #endif
535
+
536
+ self.provider = [[CallKitProviderDelegate alloc]
537
+ initWithCallManager:[OMISIPLib sharedInstance].callManager];
538
+ self.voipRegistry = [[PKPushRegistry alloc]
539
+ initWithQueue:dispatch_get_main_queue()];
540
+ self.pushkitManager = [[PushKitManager alloc]
541
+ initWithVoipRegistry:self.voipRegistry];
542
+
543
+ if (@available(iOS 10.0, *)) {
544
+ [UNUserNotificationCenter currentNotificationCenter].delegate =
545
+ (id<UNUserNotificationCenterDelegate>)self;
546
+ }
547
+
548
+ if ([FIRApp defaultApp] == nil) {
549
+ [FIRApp configure];
550
+ }
551
+ // ----- End OmiKit Config ------
552
+
553
+ return YES;
554
+ }
555
+
556
+ // Handle missed call notification tap
557
+ - (void)userNotificationCenter:(UNUserNotificationCenter *)center
558
+ didReceiveNotificationResponse:(UNNotificationResponse *)response
559
+ withCompletionHandler:(void (^)())completionHandler
560
+ {
561
+ NSDictionary *userInfo = response.notification.request.content.userInfo;
562
+ if (userInfo && [userInfo valueForKey:@"omisdkCallerNumber"]) {
563
+ [OmikitNotification didRecieve:userInfo];
564
+ }
565
+ completionHandler();
566
+ }
567
+
568
+ // Register push notification token
569
+ - (void)application:(UIApplication *)app
570
+ didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken
571
+ {
572
+ const unsigned char *data = (const unsigned char *)[devToken bytes];
573
+ NSMutableString *token = [NSMutableString string];
574
+ for (NSUInteger i = 0; i < [devToken length]; i++) {
575
+ [token appendFormat:@"%02.2hhX", data[i]];
576
+ }
577
+ [OmiClient setUserPushNotificationToken:[token copy]];
578
+ }
579
+
580
+ // Terminate all calls when app is killed
581
+ - (void)applicationWillTerminate:(UIApplication *)application {
582
+ @try {
583
+ [OmiClient OMICloseCall];
584
+ } @catch (NSException *exception) {}
585
+ }
586
+
587
+ @end
588
+
589
+ @implementation ReactNativeDelegate
590
+
591
+ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
592
+ {
593
+ return [self bundleURL];
594
+ }
595
+
596
+ - (NSURL *)bundleURL
597
+ {
598
+ #if DEBUG
599
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
600
+ #else
601
+ return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
602
+ #endif
603
+ }
604
+
605
+ @end
606
+ ```
607
+
608
+ </details>
609
+
610
+ > **Note:** Replace `YourAppName` with your app's module name. For production, change `KEY_OMI_APP_ENVIROMENT_SANDBOX` to `KEY_OMI_APP_ENVIROMENT_PRODUCTION`.
611
+
219
612
  ### 5. New Architecture (Optional)
220
613
 
221
614
  In your `Podfile`:
@@ -224,15 +617,20 @@ In your `Podfile`:
224
617
  ENV['RN_NEW_ARCH_ENABLED'] = '1'
225
618
  ```
226
619
 
227
- For **full bridgeless mode**, in `AppDelegate.mm`:
620
+ For **New Architecture with video call support**, add Fabric interop registration in `AppDelegate.mm` inside `didFinishLaunchingWithOptions`, **before** `return [super ...]`:
228
621
 
229
622
  ```objc
230
- - (BOOL)bridgelessEnabled
231
- {
232
- return YES;
233
- }
623
+ // Required imports at the top of AppDelegate.mm
624
+ #import <React-RCTFabric/React/RCTComponentViewFactory.h>
625
+ #import <React-RCTFabric/React/RCTLegacyViewManagerInteropComponentView.h>
626
+
627
+ // Inside didFinishLaunchingWithOptions, before return:
628
+ [RCTLegacyViewManagerInteropComponentView supportLegacyViewManagerWithName:@"OmiLocalCameraView"];
629
+ [RCTLegacyViewManagerInteropComponentView supportLegacyViewManagerWithName:@"OmiRemoteCameraView"];
234
630
  ```
235
631
 
632
+ > **Important:** Bridgeless mode is **not yet supported** for video call views. If you use New Architecture, keep bridge mode enabled (do **not** add `bridgelessEnabled` returning `YES`).
633
+
236
634
  Then run `cd ios && pod install`.
237
635
 
238
636
  ---
@@ -264,7 +662,7 @@ Then run `cd ios && pod install`.
264
662
  │ │ │ │ │ │
265
663
  │ ▼ │ │ ▼ │
266
664
  │ OMIKIT SDK │ │ OmiKit SDK │
267
- │ (v2.6.4) │ │ (v1.10.34) │
665
+ │ (v2.6.5) │ │ (v1.11.4) │
268
666
  │ │ │ │ │ │
269
667
  │ ▼ │ │ ▼ │
270
668
  │ SIP Stack │ │ SIP Stack │
@@ -945,44 +1343,259 @@ omiEmitter.addListener(OmiCallEvent.onCallStateChanged, (data) => {
945
1343
 
946
1344
  ## Video Calls
947
1345
 
948
- ### Setup
1346
+ > **Important:** To use video calls, you must login with `isVideo: true` in `initCallWithUserPassword()`. This enables camera support during SIP registration.
1347
+
1348
+ ### Prerequisites
949
1349
 
950
1350
  ```typescript
951
- import { OmiLocalCamera, OmiRemoteCamera } from 'omikit-plugin';
1351
+ // Login with video support enabled
1352
+ await initCallWithUserPassword({
1353
+ userName: 'sip_user',
1354
+ password: 'sip_password',
1355
+ realm: 'your_realm',
1356
+ isVideo: true, // Required for video call support
1357
+ fcmToken: fcmToken,
1358
+ });
952
1359
  ```
953
1360
 
954
1361
  ### Video Components
955
1362
 
956
1363
  ```tsx
957
- // Local camera preview (your camera)
958
- <OmiLocalCamera style={{ width: 120, height: 160 }} />
959
-
960
- // Remote camera view (other party's video)
961
- <OmiRemoteCamera style={{ width: '100%', height: '100%' }} />
1364
+ import {
1365
+ OmiLocalCameraView, // Your camera preview
1366
+ OmiRemoteCameraView, // Remote party's video
1367
+ registerVideoEvent,
1368
+ removeVideoEvent,
1369
+ refreshRemoteCamera,
1370
+ refreshLocalCamera,
1371
+ switchOmiCamera,
1372
+ toggleOmiVideo,
1373
+ setupVideoContainers, // iOS Fabric only
1374
+ setCameraConfig, // iOS Fabric only
1375
+ omiEmitter,
1376
+ OmiCallEvent,
1377
+ OmiCallState,
1378
+ } from 'omikit-plugin';
962
1379
  ```
963
1380
 
964
- ### Video Call Flow
1381
+ ### Platform Differences
965
1382
 
966
- ```typescript
967
- // 1. Register for video events BEFORE starting call
968
- await registerVideoEvent();
1383
+ | Feature | Android | iOS (Old Arch) | iOS (New Arch / Fabric) |
1384
+ |---------|---------|----------------|------------------------|
1385
+ | Video rendering | JSX components | JSX components | Native window containers |
1386
+ | Camera views | `<OmiRemoteCameraView>` in JSX | `<OmiRemoteCameraView>` in JSX | `setupVideoContainers()` from JS |
1387
+ | Style control | React `style` props | React `style` props | `setCameraConfig()` from JS |
1388
+ | Controls overlay | Overlay on video | Overlay on video | Below video (split layout) |
1389
+
1390
+ ### Cross-Platform Video Call Example
969
1391
 
970
- // 2. Start video call
971
- await startCall({ phoneNumber: '0901234567', isVideo: true });
1392
+ Complete example supporting both Android and iOS (Old + New Architecture):
1393
+
1394
+ ```tsx
1395
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
1396
+ import { View, Text, TouchableOpacity, StyleSheet, Platform, Dimensions } from 'react-native';
1397
+ import {
1398
+ OmiRemoteCameraView, OmiLocalCameraView,
1399
+ omiEmitter, OmiCallEvent, OmiCallState,
1400
+ registerVideoEvent, removeVideoEvent,
1401
+ refreshRemoteCamera, refreshLocalCamera,
1402
+ setupVideoContainers, setCameraConfig,
1403
+ switchOmiCamera, toggleOmiVideo, toggleMute, endCall,
1404
+ } from 'omikit-plugin';
972
1405
 
973
- // 3. Toggle video during call
974
- await toggleOmiVideo(); // on/off video stream
975
- await switchOmiCamera(); // front/back camera
1406
+ const { width: SW, height: SH } = Dimensions.get('window');
1407
+
1408
+ export const VideoCallScreen = ({ navigation, route }) => {
1409
+ const [isCallActive, setIsCallActive] = useState(false);
1410
+ const [isMuted, setIsMuted] = useState(false);
1411
+ const [cameraOn, setCameraOn] = useState(true);
1412
+ const hasNavigated = useRef(false);
1413
+
1414
+ // iOS Fabric: configure native video container position/size
1415
+ const configureIOSVideo = useCallback(() => {
1416
+ setCameraConfig({
1417
+ target: 'remote',
1418
+ x: 0, y: 0, width: SW, height: SH * 0.65,
1419
+ scaleMode: 'fill', backgroundColor: '#000',
1420
+ });
1421
+ setCameraConfig({
1422
+ target: 'local',
1423
+ x: SW - 136, y: 60, width: 120, height: 180,
1424
+ borderRadius: 12, scaleMode: 'fill',
1425
+ });
1426
+ }, []);
1427
+
1428
+ useEffect(() => {
1429
+ // iOS: register video events before call
1430
+ if (Platform.OS === 'ios') registerVideoEvent();
1431
+
1432
+ const sub = omiEmitter.addListener(OmiCallEvent.onCallStateChanged, (data) => {
1433
+ const { status } = data;
1434
+
1435
+ if (status === OmiCallState.confirmed) {
1436
+ setIsCallActive(true);
1437
+
1438
+ if (Platform.OS === 'android') {
1439
+ // Android: connect video feeds to TextureView surfaces
1440
+ refreshRemoteCamera();
1441
+ refreshLocalCamera();
1442
+ } else {
1443
+ // iOS Fabric: create native window containers + connect SDK
1444
+ setupVideoContainers().then(() => configureIOSVideo());
1445
+ }
1446
+ }
976
1447
 
977
- // 4. Listen for remote video ready
978
- omiEmitter.addListener(OmiCallEvent.onRemoteVideoReady, () => {
979
- // Remote video is now available - show OmiRemoteCamera
1448
+ if ((status === OmiCallState.disconnected || status === 6) && !hasNavigated.current) {
1449
+ hasNavigated.current = true;
1450
+ // iOS: hide native containers before navigating
1451
+ if (Platform.OS === 'ios') {
1452
+ setCameraConfig({ target: 'remote', hidden: true });
1453
+ setCameraConfig({ target: 'local', hidden: true });
1454
+ }
1455
+ setTimeout(() => {
1456
+ navigation.reset({ index: 0, routes: [{ name: 'Home' }] });
1457
+ }, 100);
1458
+ }
1459
+ });
1460
+
1461
+ return () => {
1462
+ sub.remove();
1463
+ if (Platform.OS === 'ios') removeVideoEvent();
1464
+ };
1465
+ }, [navigation, configureIOSVideo]);
1466
+
1467
+ const handleEndCall = useCallback(() => {
1468
+ if (hasNavigated.current) return;
1469
+ hasNavigated.current = true;
1470
+ if (Platform.OS === 'ios') {
1471
+ setCameraConfig({ target: 'remote', hidden: true });
1472
+ setCameraConfig({ target: 'local', hidden: true });
1473
+ }
1474
+ endCall();
1475
+ setTimeout(() => {
1476
+ navigation.reset({ index: 0, routes: [{ name: 'Home' }] });
1477
+ }, 1000);
1478
+ }, [navigation]);
1479
+
1480
+ return (
1481
+ <View style={styles.container}>
1482
+ {/* ============ VIDEO AREA ============ */}
1483
+
1484
+ {/* Android: camera views via JSX — controls can overlay on top */}
1485
+ {Platform.OS === 'android' && isCallActive && (
1486
+ <>
1487
+ <OmiRemoteCameraView style={StyleSheet.absoluteFillObject} />
1488
+ <OmiLocalCameraView style={styles.localPiP} />
1489
+ </>
1490
+ )}
1491
+
1492
+ {/* iOS Fabric: native video renders on window (top ~65%).
1493
+ React controls render below video area. */}
1494
+
1495
+ {/* ============ CONTROLS AREA ============ */}
1496
+
1497
+ {/* Spacer — pushes controls to bottom */}
1498
+ <View style={{ flex: 1 }} />
1499
+
1500
+ {/* Controls panel */}
1501
+ {isCallActive && (
1502
+ <View style={styles.controls}>
1503
+ <TouchableOpacity onPress={() => { toggleMute(); setIsMuted(m => !m); }}>
1504
+ <Text style={styles.btn}>{isMuted ? 'Unmute' : 'Mute'}</Text>
1505
+ </TouchableOpacity>
1506
+ <TouchableOpacity onPress={() => { toggleOmiVideo(); setCameraOn(c => !c); }}>
1507
+ <Text style={styles.btn}>{cameraOn ? 'Cam Off' : 'Cam On'}</Text>
1508
+ </TouchableOpacity>
1509
+ <TouchableOpacity onPress={switchOmiCamera}>
1510
+ <Text style={styles.btn}>Flip</Text>
1511
+ </TouchableOpacity>
1512
+ <TouchableOpacity onPress={handleEndCall}>
1513
+ <Text style={[styles.btn, { backgroundColor: 'red' }]}>End</Text>
1514
+ </TouchableOpacity>
1515
+ </View>
1516
+ )}
1517
+
1518
+ {/* Answer/Reject for incoming calls */}
1519
+ {!isCallActive && (
1520
+ <View style={styles.controls}>
1521
+ <TouchableOpacity onPress={handleEndCall}>
1522
+ <Text style={[styles.btn, { backgroundColor: 'red' }]}>Reject</Text>
1523
+ </TouchableOpacity>
1524
+ </View>
1525
+ )}
1526
+ </View>
1527
+ );
1528
+ };
1529
+
1530
+ const styles = StyleSheet.create({
1531
+ container: { flex: 1, backgroundColor: '#1E3050' },
1532
+ localPiP: {
1533
+ position: 'absolute', top: 60, right: 16,
1534
+ width: 120, height: 180, borderRadius: 12, overflow: 'hidden', zIndex: 10,
1535
+ },
1536
+ controls: {
1537
+ flexDirection: 'row', justifyContent: 'space-evenly',
1538
+ paddingVertical: 20, paddingHorizontal: 16,
1539
+ backgroundColor: 'rgba(0,0,0,0.5)', borderTopLeftRadius: 24, borderTopRightRadius: 24,
1540
+ paddingBottom: 40,
1541
+ },
1542
+ btn: {
1543
+ color: '#fff', fontSize: 14, paddingVertical: 12, paddingHorizontal: 16,
1544
+ backgroundColor: 'rgba(255,255,255,0.2)', borderRadius: 24, overflow: 'hidden',
1545
+ },
980
1546
  });
1547
+ ```
1548
+
1549
+ > **Key differences per platform:**
1550
+ > - **Android**: `<OmiRemoteCameraView>` + `<OmiLocalCameraView>` render in JSX. Call `refreshRemoteCamera()` + `refreshLocalCamera()` when confirmed. Controls overlay on video.
1551
+ > - **iOS (Old Arch)**: Same as Android — JSX camera views work normally.
1552
+ > - **iOS (New Arch/Fabric)**: `RCTViewManager.view()` not called. Use `setupVideoContainers()` to create native window containers, then `setCameraConfig()` to adjust position/style. Controls render below video (split layout).
1553
+ > - **Disconnect**: On iOS, call `setCameraConfig({ target: 'remote', hidden: true })` before navigating to prevent stale video overlay. Use `setTimeout` for navigation — native event callback may block immediate navigation.
1554
+ > - **End call**: Use `navigation.reset()` instead of `goBack()` to clear stacked screens. Guard with `useRef` to prevent multiple navigations.
981
1555
 
982
- // 5. Cleanup when call ends
983
- await removeVideoEvent();
1556
+ ### `setCameraConfig()` iOS Fabric Only
1557
+
1558
+ Control native video container style from JS:
1559
+
1560
+ ```typescript
1561
+ setCameraConfig({
1562
+ target: 'local' | 'remote',
1563
+ x?: number, // X position
1564
+ y?: number, // Y position
1565
+ width?: number, // View width
1566
+ height?: number, // View height
1567
+ borderRadius?: number, // Corner radius
1568
+ borderWidth?: number, // Border width
1569
+ borderColor?: string, // Hex color (#RRGGBB or #RRGGBBAA)
1570
+ backgroundColor?: string,
1571
+ opacity?: number, // 0.0 - 1.0
1572
+ hidden?: boolean, // Show/hide
1573
+ scaleMode?: 'fill' | 'fit' | 'stretch',
1574
+ });
984
1575
  ```
985
1576
 
1577
+ ### Video API Reference
1578
+
1579
+ | Function | Description | Platform |
1580
+ |----------|-------------|----------|
1581
+ | `registerVideoEvent()` | Register for video notifications | iOS only |
1582
+ | `removeVideoEvent()` | Cleanup video notifications | iOS only |
1583
+ | `refreshRemoteCamera()` | Connect remote video feed to surface | Both |
1584
+ | `refreshLocalCamera()` | Connect local camera feed to surface | Both |
1585
+ | `switchOmiCamera()` | Switch front/back camera | Both |
1586
+ | `toggleOmiVideo()` | Toggle camera on/off during call | Both |
1587
+ | `setupVideoContainers()` | Create native video containers on window | iOS Fabric only |
1588
+ | `setCameraConfig()` | Control video container style/position | iOS Fabric only |
1589
+
1590
+ ### Known Limitations
1591
+
1592
+ - **iOS Fabric**: Controls cannot overlay on top of video (native window z-order). Use split layout — video top, controls bottom.
1593
+ - **iOS Fabric**: Camera switch has ~3-6s delay (SDK Metal stabilization).
1594
+ - **Android**: Must login with `isVideo: true` for camera to initialize. Without it, colorbar test pattern shows instead of camera.
1595
+ - **Simulator**: Video calls require physical device on both platforms.
1596
+
1597
+ See [Known Issues](./docs/known-issues.md) for full details.
1598
+
986
1599
  ---
987
1600
 
988
1601
  ## Push Notifications
@@ -1214,7 +1827,10 @@ if (initialCall) {
1214
1827
  | Incoming call on Android — no UI | Missing overlay permission | Call `requestSystemAlertWindowPermission()` |
1215
1828
  | `startCall` returns 450/451/452 | Missing runtime permissions | Call `requestPermissionsByCodes([code])` |
1216
1829
  | No audio during call | Audio route issue | Check `getAudio()` and `setAudio()` |
1217
- | Video not showing | Video event not registered | Call `registerVideoEvent()` before the call |
1830
+ | Video not showing (iOS) | Video event not registered | Call `registerVideoEvent()` before the call |
1831
+ | Video shows colorbar (Android) | Login with `isVideo: false` | Login with `isVideo: true` to enable camera |
1832
+ | Video black screen (Android) | Feed not connected | Ensure `refreshRemoteCamera()` + `refreshLocalCamera()` called on confirmed |
1833
+ | iOS Fabric: controls hidden | Native video on UIWindow covers React | Use split layout — video top, controls bottom. See [Known Issues](./docs/known-issues.md) |
1218
1834
  | NativeEventEmitter warning (iOS) | Old RN bridge issue | Upgrade to v4.0.x with New Architecture |
1219
1835
  | `Invalid local URI` in logs | Empty proxy/host in login | Pass `host` parameter in `initCallWithUserPassword` |
1220
1836
  | Build error with New Arch | Codegen not configured | Ensure `codegenConfig` exists in `package.json` |
@@ -1227,6 +1843,7 @@ if (initialCall) {
1227
1843
  Full documentation in [`./docs/`](./docs/):
1228
1844
 
1229
1845
  - [API Integration Guide (App-to-App)](./docs/api-integration-guide.md)
1846
+ - [Known Issues & Limitations](./docs/known-issues.md)
1230
1847
  - [Project Overview & PDR](./docs/project-overview-pdr.md)
1231
1848
  - [Codebase Summary](./docs/codebase-summary.md)
1232
1849
  - [System Architecture](./docs/system-architecture.md)