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.
- package/README.md +654 -37
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/omikitplugin/OmiLocalCameraView.kt +94 -0
- package/android/src/main/java/com/omikitplugin/OmiRemoteCameraView.kt +117 -0
- package/android/src/main/java/com/omikitplugin/OmikitPluginModule.kt +24 -17
- package/android/src/main/java/com/omikitplugin/OmikitPluginPackage.kt +11 -8
- package/ios/CallProcess/CallManager.swift +99 -29
- package/ios/Library/OmikitPlugin.m +18 -0
- package/ios/Library/OmikitPlugin.swift +233 -1
- package/ios/OmikitPlugin-Bridging-Header.h +1 -0
- package/ios/OmikitPlugin.xcodeproj/project.pbxproj +4 -4
- package/ios/VideoCall/OmiLocalCameraViewBridge.m +14 -0
- package/ios/VideoCall/OmiLocalCameraViewManager.swift +41 -0
- package/ios/VideoCall/OmiRemoteCameraViewBridge.m +14 -0
- package/ios/VideoCall/OmiRemoteCameraViewManager.swift +40 -0
- package/lib/commonjs/NativeOmikitPlugin.js +2 -1
- package/lib/commonjs/NativeOmikitPlugin.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/omi_audio_type.js +5 -7
- package/lib/commonjs/omi_audio_type.js.map +1 -1
- package/lib/commonjs/omi_call_state.js +5 -3
- package/lib/commonjs/omi_call_state.js.map +1 -1
- package/lib/commonjs/omi_local_camera.js +19 -17
- package/lib/commonjs/omi_local_camera.js.map +1 -1
- package/lib/commonjs/omi_remote_camera.js +20 -17
- package/lib/commonjs/omi_remote_camera.js.map +1 -1
- package/lib/commonjs/omi_start_call_status.js +5 -24
- package/lib/commonjs/omi_start_call_status.js.map +1 -1
- package/lib/commonjs/omikit.js +56 -3
- package/lib/commonjs/omikit.js.map +1 -1
- package/lib/commonjs/types/index.d.js.map +1 -1
- package/lib/module/NativeOmikitPlugin.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/omi_audio_type.js +4 -7
- package/lib/module/omi_audio_type.js.map +1 -1
- package/lib/module/omi_call_state.js +4 -3
- package/lib/module/omi_call_state.js.map +1 -1
- package/lib/module/omi_local_camera.js +19 -18
- package/lib/module/omi_local_camera.js.map +1 -1
- package/lib/module/omi_remote_camera.js +20 -18
- package/lib/module/omi_remote_camera.js.map +1 -1
- package/lib/module/omi_start_call_status.js +4 -24
- package/lib/module/omi_start_call_status.js.map +1 -1
- package/lib/module/omikit.js +49 -1
- package/lib/module/omikit.js.map +1 -1
- package/lib/module/types/index.d.js.map +1 -1
- package/omikit-plugin.podspec +1 -1
- package/package.json +2 -11
- package/react-native.config.js +14 -0
- package/src/NativeOmikitPlugin.ts +1 -0
- package/src/omi_call_state.tsx +1 -0
- package/src/omi_local_camera.tsx +15 -19
- package/src/omi_remote_camera.tsx +16 -19
- package/src/omikit.tsx +63 -0
- package/src/types/index.d.ts +344 -62
- package/android/src/main/java/com/omikitplugin/FLLocalCameraModule.kt +0 -34
- package/android/src/main/java/com/omikitplugin/FLLocalCameraView.kt +0 -44
- package/android/src/main/java/com/omikitplugin/FLRemoteCameraModule.kt +0 -37
- package/android/src/main/java/com/omikitplugin/FLRemoteCameraView.kt +0 -23
- package/ios/VideoCall/FLLocalCameraView.m +0 -17
- package/ios/VideoCall/FLLocalCameraView.swift +0 -44
- package/ios/VideoCall/FLRemoteCameraView.m +0 -18
- 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.
|
|
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.
|
|
50
|
-
| iOS | OmiKit | 1.
|
|
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.
|
|
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
|
-
###
|
|
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
|
-
###
|
|
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
|
-
|
|
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 <
|
|
216
|
-
#import <
|
|
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 **
|
|
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
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
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
|
-
###
|
|
1381
|
+
### Platform Differences
|
|
965
1382
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
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
|
-
|
|
971
|
-
|
|
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
|
-
|
|
974
|
-
|
|
975
|
-
|
|
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
|
-
|
|
978
|
-
|
|
979
|
-
|
|
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
|
-
|
|
983
|
-
|
|
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)
|