omikit-plugin 4.0.1 → 4.1.0
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 +344 -45
- package/android/build.gradle +21 -71
- package/android/gradle.properties +4 -4
- package/android/src/main/java/com/omikitplugin/OmiLocalCameraView.kt +112 -0
- package/android/src/main/java/com/omikitplugin/OmiRemoteCameraView.kt +99 -0
- package/android/src/main/java/com/omikitplugin/OmikitPluginModule.kt +57 -38
- 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 +20 -12
- package/lib/commonjs/omi_local_camera.js.map +1 -1
- package/lib/commonjs/omi_remote_camera.js +21 -12
- 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 +91 -21
- 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 +20 -13
- package/lib/module/omi_local_camera.js.map +1 -1
- package/lib/module/omi_remote_camera.js +21 -13
- 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 +85 -20
- 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 +16 -15
- package/src/omi_remote_camera.tsx +17 -15
- package/src/omikit.tsx +104 -28
- 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.0
|
|
5
|
+
**Status:** Active maintenance | **Version:** 4.1.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -47,7 +47,36 @@ The [omikit-plugin](https://www.npmjs.com/package/omikit-plugin) enables VoIP/SI
|
|
|
47
47
|
| Platform | SDK | Version |
|
|
48
48
|
|----------|-----|---------|
|
|
49
49
|
| Android | OMIKIT | 2.6.4 |
|
|
50
|
-
| iOS | OmiKit | 1.
|
|
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 35 (Android 15) | — |
|
|
58
|
+
| **Compile SDK** | API 35 | — |
|
|
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
|
|
|
@@ -166,11 +195,11 @@ allprojects {
|
|
|
166
195
|
Then add your credentials to `~/.gradle/gradle.properties` (or project-level `gradle.properties`):
|
|
167
196
|
|
|
168
197
|
```properties
|
|
169
|
-
OMI_USER=
|
|
170
|
-
OMI_TOKEN=
|
|
198
|
+
OMI_USER=omi_github_username
|
|
199
|
+
OMI_TOKEN=omi_github_access_token
|
|
171
200
|
```
|
|
172
201
|
|
|
173
|
-
> **Note:**
|
|
202
|
+
> **Note:** Contact the OMICall development team to get `OMI_USER` and `OMI_TOKEN` credentials.
|
|
174
203
|
|
|
175
204
|
### 4. New Architecture (Optional)
|
|
176
205
|
|
|
@@ -264,11 +293,11 @@ Then run `cd ios && pod install`.
|
|
|
264
293
|
│ │ │ │ │ │
|
|
265
294
|
│ ▼ │ │ ▼ │
|
|
266
295
|
│ OMIKIT SDK │ │ OmiKit SDK │
|
|
267
|
-
│ (v2.6.4)
|
|
296
|
+
│ (v2.6.4) │ │ (v1.10.34) │
|
|
268
297
|
│ │ │ │ │ │
|
|
269
298
|
│ ▼ │ │ ▼ │
|
|
270
299
|
│ SIP Stack │ │ SIP Stack │
|
|
271
|
-
│ (OMSIP)
|
|
300
|
+
│ (OMSIP) │ │ (OMSIP) │
|
|
272
301
|
└─────────────┘ └──────────────┘
|
|
273
302
|
```
|
|
274
303
|
|
|
@@ -288,7 +317,9 @@ import {
|
|
|
288
317
|
OmiCallState,
|
|
289
318
|
} from 'omikit-plugin';
|
|
290
319
|
|
|
291
|
-
// Step 1: Start SDK services
|
|
320
|
+
// Step 1: Start SDK services
|
|
321
|
+
// ⚠️ Call ONCE on app launch (e.g., in App.tsx / index.js / useEffect in root component)
|
|
322
|
+
// Do NOT call this multiple times — it initializes native audio and event listeners.
|
|
292
323
|
await startServices();
|
|
293
324
|
|
|
294
325
|
// Step 2: Login with SIP credentials
|
|
@@ -299,7 +330,7 @@ const loginResult = await initCallWithUserPassword({
|
|
|
299
330
|
host: '', // SIP proxy, defaults to vh.omicrm.com
|
|
300
331
|
isVideo: false,
|
|
301
332
|
fcmToken: 'your_fcm_token',
|
|
302
|
-
projectId: 'your_project_id', //
|
|
333
|
+
projectId: 'your_project_id', // firebase project id
|
|
303
334
|
});
|
|
304
335
|
|
|
305
336
|
// Step 3: Listen to call events
|
|
@@ -365,7 +396,7 @@ await initCallWithUserPassword({
|
|
|
365
396
|
host?: string, // SIP proxy server (optional)
|
|
366
397
|
isVideo: boolean, // Enable video capability
|
|
367
398
|
fcmToken: string, // Firebase token for push notifications
|
|
368
|
-
projectId
|
|
399
|
+
projectId: string, // Firebase project ID
|
|
369
400
|
isSkipDevices?: boolean, // true = Customer mode, false = Agent mode (default)
|
|
370
401
|
});
|
|
371
402
|
```
|
|
@@ -416,6 +447,51 @@ await initCallWithApiKey({
|
|
|
416
447
|
});
|
|
417
448
|
```
|
|
418
449
|
|
|
450
|
+
### Option 3: App-to-App API (v4.0+)
|
|
451
|
+
|
|
452
|
+
Starting from **v4.0**, customers using the **App-to-App** service must call the OMICALL API to provision SIP extensions before initializing the SDK. The API returns SIP credentials that you pass to `initCallWithUserPassword()` with `isSkipDevices: true`.
|
|
453
|
+
|
|
454
|
+
> For full API documentation (endpoints, request/response formats), see the [API Integration Guide](./docs/api-integration-guide.md).
|
|
455
|
+
|
|
456
|
+
**Quick flow:**
|
|
457
|
+
|
|
458
|
+
```
|
|
459
|
+
Your Backend OMICALL API Mobile App (SDK)
|
|
460
|
+
│ │ │
|
|
461
|
+
│ 1. POST .../init │ │
|
|
462
|
+
├─────────────────────────────►│ │
|
|
463
|
+
│ {domain, extension, │ │
|
|
464
|
+
│ password, proxy} │ │
|
|
465
|
+
│◄─────────────────────────────┤ │
|
|
466
|
+
│ │ │
|
|
467
|
+
│ 2. Return credentials │ │
|
|
468
|
+
├──────────────────────────────────────────────────────────►│
|
|
469
|
+
│ │ 3. startServices() │
|
|
470
|
+
│ │ 4. initCallWithUserPassword
|
|
471
|
+
│ │ (isSkipDevices: true) │
|
|
472
|
+
│ │ │
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
// After getting credentials from your backend:
|
|
477
|
+
await startServices();
|
|
478
|
+
|
|
479
|
+
await initCallWithUserPassword({
|
|
480
|
+
userName: credentials.extension, // from API response
|
|
481
|
+
password: credentials.password, // from API response
|
|
482
|
+
realm: credentials.domain, // from API response
|
|
483
|
+
host: credentials.outboundProxy, // from API response
|
|
484
|
+
isVideo: false,
|
|
485
|
+
fcmToken: 'your-fcm-token',
|
|
486
|
+
isSkipDevices: true, // Required for App-to-App
|
|
487
|
+
});
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
> **Important:**
|
|
491
|
+
> - Call the OMICALL API from your **backend server** only — never expose the Bearer token in client-side code.
|
|
492
|
+
> - You **must** call the [Logout API](./docs/api-integration-guide.md#5-logout) before switching users. Otherwise, both devices using the same SIP extension will receive incoming calls simultaneously.
|
|
493
|
+
> - Use getter functions (`getProjectId()`, `getAppId()`, `getDeviceId()`, `getFcmToken()`, `getVoipToken()`) to retrieve device params for the [Add Device](./docs/api-integration-guide.md#4-add-device) and Logout APIs.
|
|
494
|
+
|
|
419
495
|
---
|
|
420
496
|
|
|
421
497
|
## Call Flows
|
|
@@ -470,8 +546,8 @@ await initCallWithApiKey({
|
|
|
470
546
|
│◄────────────────────┤ (event emitted) │
|
|
471
547
|
│ │ │
|
|
472
548
|
│ ┌──────────────┐ │ │
|
|
473
|
-
│ │ Show Call UI
|
|
474
|
-
│ │
|
|
549
|
+
│ │ Show Call UI │ │ │
|
|
550
|
+
│ │[Accept][Deny]│ │ │
|
|
475
551
|
│ └──────────────┘ │ │
|
|
476
552
|
│ │ │
|
|
477
553
|
│ joinCall() │ │
|
|
@@ -495,7 +571,7 @@ await initCallWithApiKey({
|
|
|
495
571
|
│ │ │◄──────────────┤
|
|
496
572
|
│ │ │ │
|
|
497
573
|
|
|
498
|
-
┌───────────────── iOS (VoIP Push)
|
|
574
|
+
┌───────────────── iOS (VoIP Push) ───────────────────┐
|
|
499
575
|
│ │ │ │ │ │
|
|
500
576
|
│ │ │ PushKit VoIP │ │ │
|
|
501
577
|
│ │ │◄──────────────┤ │ │
|
|
@@ -511,9 +587,9 @@ await initCallWithApiKey({
|
|
|
511
587
|
│ │◄──────────────┤ │ │ │
|
|
512
588
|
│ │ incoming (2) │ │ │ │
|
|
513
589
|
│ │◄──────────────┤ │ │ │
|
|
514
|
-
|
|
590
|
+
└─────────────────────────────────────────────────────┘
|
|
515
591
|
|
|
516
|
-
┌───────────────── Android (FCM)
|
|
592
|
+
┌───────────────── Android (FCM) ─────────────────────┐
|
|
517
593
|
│ │ │ │ │ │
|
|
518
594
|
│ │ │ FCM Message │ │ │
|
|
519
595
|
│ │ │◄──────────────┤ │ │
|
|
@@ -528,7 +604,7 @@ await initCallWithApiKey({
|
|
|
528
604
|
│ │◄──────────────┤ │ │ │
|
|
529
605
|
│ │ incoming (2) │ │ │ │
|
|
530
606
|
│ │◄──────────────┤ │ │ │
|
|
531
|
-
|
|
607
|
+
└─────────────────────────────────────────────────────┘
|
|
532
608
|
|
|
533
609
|
│ │ │ │
|
|
534
610
|
│ joinCall() │ │ │
|
|
@@ -558,8 +634,8 @@ await initCallWithApiKey({
|
|
|
558
634
|
│ disconnected (6) │ 200 OK │
|
|
559
635
|
│◄────────────────────┤────────────────────►│
|
|
560
636
|
│ │ │
|
|
561
|
-
│ │ Show Missed Call
|
|
562
|
-
│ │ Notification
|
|
637
|
+
│ │ Show Missed Call │
|
|
638
|
+
│ │ Notification │
|
|
563
639
|
│ │ │
|
|
564
640
|
│ (user taps notif) │ │
|
|
565
641
|
│ │ │
|
|
@@ -601,14 +677,14 @@ await initCallWithApiKey({
|
|
|
601
677
|
│ incoming (2) │ SIP INVITE │
|
|
602
678
|
│◄────────────────────┤◄────────────────────┤
|
|
603
679
|
│ │ │
|
|
604
|
-
┌── rejectCall()
|
|
680
|
+
┌── rejectCall() ───┐ │
|
|
605
681
|
│ Decline this │ 486 Busy Here │
|
|
606
|
-
│ device only
|
|
682
|
+
│ device only ├─────────────────────────► │
|
|
607
683
|
└───────────────────┘ (other devices ring) │
|
|
608
684
|
│ │ │
|
|
609
|
-
┌── dropCall()
|
|
685
|
+
┌── dropCall() ─────┐ │
|
|
610
686
|
│ Decline + stop │ 603 Decline │
|
|
611
|
-
│ ALL devices
|
|
687
|
+
│ ALL devices ├─────────────────────────► │
|
|
612
688
|
└───────────────────┘ (PBX stops all ringing) │
|
|
613
689
|
│ │ │
|
|
614
690
|
```
|
|
@@ -621,7 +697,7 @@ await initCallWithApiKey({
|
|
|
621
697
|
|
|
622
698
|
| Function | Returns | Description |
|
|
623
699
|
|----------|---------|-------------|
|
|
624
|
-
| `startServices()` | `Promise<boolean>` | Initialize SDK. Call once on app launch |
|
|
700
|
+
| `startServices()` | `Promise<boolean>` | Initialize SDK. **Call once** on app launch (e.g., `App.tsx` or `index.js`). Do not call multiple times |
|
|
625
701
|
| `initCallWithUserPassword(data)` | `Promise<boolean>` | Login with SIP username/password |
|
|
626
702
|
| `initCallWithApiKey(data)` | `Promise<boolean>` | Login with API key |
|
|
627
703
|
| `logout()` | `Promise<boolean>` | Logout and unregister SIP |
|
|
@@ -898,48 +974,265 @@ omiEmitter.addListener(OmiCallEvent.onCallStateChanged, (data) => {
|
|
|
898
974
|
|
|
899
975
|
## Video Calls
|
|
900
976
|
|
|
901
|
-
|
|
977
|
+
> **Important:** To use video calls, you must login with `isVideo: true` in `initCallWithUserPassword()`. This enables camera support during SIP registration.
|
|
978
|
+
|
|
979
|
+
### Prerequisites
|
|
902
980
|
|
|
903
981
|
```typescript
|
|
904
|
-
|
|
982
|
+
// Login with video support enabled
|
|
983
|
+
await initCallWithUserPassword({
|
|
984
|
+
userName: 'sip_user',
|
|
985
|
+
password: 'sip_password',
|
|
986
|
+
realm: 'your_realm',
|
|
987
|
+
isVideo: true, // Required for video call support
|
|
988
|
+
fcmToken: fcmToken,
|
|
989
|
+
});
|
|
905
990
|
```
|
|
906
991
|
|
|
907
992
|
### Video Components
|
|
908
993
|
|
|
909
994
|
```tsx
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
995
|
+
import {
|
|
996
|
+
OmiLocalCameraView, // Your camera preview
|
|
997
|
+
OmiRemoteCameraView, // Remote party's video
|
|
998
|
+
registerVideoEvent,
|
|
999
|
+
removeVideoEvent,
|
|
1000
|
+
refreshRemoteCamera,
|
|
1001
|
+
refreshLocalCamera,
|
|
1002
|
+
switchOmiCamera,
|
|
1003
|
+
toggleOmiVideo,
|
|
1004
|
+
setupVideoContainers, // iOS Fabric only
|
|
1005
|
+
setCameraConfig, // iOS Fabric only
|
|
1006
|
+
omiEmitter,
|
|
1007
|
+
OmiCallEvent,
|
|
1008
|
+
OmiCallState,
|
|
1009
|
+
} from 'omikit-plugin';
|
|
915
1010
|
```
|
|
916
1011
|
|
|
917
|
-
###
|
|
1012
|
+
### Platform Differences
|
|
918
1013
|
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1014
|
+
| Feature | Android | iOS (Old Arch) | iOS (New Arch / Fabric) |
|
|
1015
|
+
|---------|---------|----------------|------------------------|
|
|
1016
|
+
| Video rendering | JSX components | JSX components | Native window containers |
|
|
1017
|
+
| Camera views | `<OmiRemoteCameraView>` in JSX | `<OmiRemoteCameraView>` in JSX | `setupVideoContainers()` from JS |
|
|
1018
|
+
| Style control | React `style` props | React `style` props | `setCameraConfig()` from JS |
|
|
1019
|
+
| Controls overlay | Overlay on video | Overlay on video | Below video (split layout) |
|
|
922
1020
|
|
|
923
|
-
|
|
924
|
-
await startCall({ phoneNumber: '0901234567', isVideo: true });
|
|
1021
|
+
### Cross-Platform Video Call Example
|
|
925
1022
|
|
|
926
|
-
|
|
927
|
-
await toggleOmiVideo(); // on/off video stream
|
|
928
|
-
await switchOmiCamera(); // front/back camera
|
|
1023
|
+
Complete example supporting both Android and iOS (Old + New Architecture):
|
|
929
1024
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1025
|
+
```tsx
|
|
1026
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
1027
|
+
import { View, Text, TouchableOpacity, StyleSheet, Platform, Dimensions } from 'react-native';
|
|
1028
|
+
import {
|
|
1029
|
+
OmiRemoteCameraView, OmiLocalCameraView,
|
|
1030
|
+
omiEmitter, OmiCallEvent, OmiCallState,
|
|
1031
|
+
registerVideoEvent, removeVideoEvent,
|
|
1032
|
+
refreshRemoteCamera, refreshLocalCamera,
|
|
1033
|
+
setupVideoContainers, setCameraConfig,
|
|
1034
|
+
switchOmiCamera, toggleOmiVideo, toggleMute, endCall,
|
|
1035
|
+
} from 'omikit-plugin';
|
|
1036
|
+
|
|
1037
|
+
const { width: SW, height: SH } = Dimensions.get('window');
|
|
1038
|
+
|
|
1039
|
+
export const VideoCallScreen = ({ navigation, route }) => {
|
|
1040
|
+
const [isCallActive, setIsCallActive] = useState(false);
|
|
1041
|
+
const [isMuted, setIsMuted] = useState(false);
|
|
1042
|
+
const [cameraOn, setCameraOn] = useState(true);
|
|
1043
|
+
const hasNavigated = useRef(false);
|
|
1044
|
+
|
|
1045
|
+
// iOS Fabric: configure native video container position/size
|
|
1046
|
+
const configureIOSVideo = useCallback(() => {
|
|
1047
|
+
setCameraConfig({
|
|
1048
|
+
target: 'remote',
|
|
1049
|
+
x: 0, y: 0, width: SW, height: SH * 0.65,
|
|
1050
|
+
scaleMode: 'fill', backgroundColor: '#000',
|
|
1051
|
+
});
|
|
1052
|
+
setCameraConfig({
|
|
1053
|
+
target: 'local',
|
|
1054
|
+
x: SW - 136, y: 60, width: 120, height: 180,
|
|
1055
|
+
borderRadius: 12, scaleMode: 'fill',
|
|
1056
|
+
});
|
|
1057
|
+
}, []);
|
|
1058
|
+
|
|
1059
|
+
useEffect(() => {
|
|
1060
|
+
// iOS: register video events before call
|
|
1061
|
+
if (Platform.OS === 'ios') registerVideoEvent();
|
|
1062
|
+
|
|
1063
|
+
const sub = omiEmitter.addListener(OmiCallEvent.onCallStateChanged, (data) => {
|
|
1064
|
+
const { status } = data;
|
|
1065
|
+
|
|
1066
|
+
if (status === OmiCallState.confirmed) {
|
|
1067
|
+
setIsCallActive(true);
|
|
1068
|
+
|
|
1069
|
+
if (Platform.OS === 'android') {
|
|
1070
|
+
// Android: connect video feeds to TextureView surfaces
|
|
1071
|
+
refreshRemoteCamera();
|
|
1072
|
+
refreshLocalCamera();
|
|
1073
|
+
} else {
|
|
1074
|
+
// iOS Fabric: create native window containers + connect SDK
|
|
1075
|
+
setupVideoContainers().then(() => configureIOSVideo());
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
if ((status === OmiCallState.disconnected || status === 6) && !hasNavigated.current) {
|
|
1080
|
+
hasNavigated.current = true;
|
|
1081
|
+
// iOS: hide native containers before navigating
|
|
1082
|
+
if (Platform.OS === 'ios') {
|
|
1083
|
+
setCameraConfig({ target: 'remote', hidden: true });
|
|
1084
|
+
setCameraConfig({ target: 'local', hidden: true });
|
|
1085
|
+
}
|
|
1086
|
+
setTimeout(() => {
|
|
1087
|
+
navigation.reset({ index: 0, routes: [{ name: 'Home' }] });
|
|
1088
|
+
}, 100);
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
return () => {
|
|
1093
|
+
sub.remove();
|
|
1094
|
+
if (Platform.OS === 'ios') removeVideoEvent();
|
|
1095
|
+
};
|
|
1096
|
+
}, [navigation, configureIOSVideo]);
|
|
1097
|
+
|
|
1098
|
+
const handleEndCall = useCallback(() => {
|
|
1099
|
+
if (hasNavigated.current) return;
|
|
1100
|
+
hasNavigated.current = true;
|
|
1101
|
+
if (Platform.OS === 'ios') {
|
|
1102
|
+
setCameraConfig({ target: 'remote', hidden: true });
|
|
1103
|
+
setCameraConfig({ target: 'local', hidden: true });
|
|
1104
|
+
}
|
|
1105
|
+
endCall();
|
|
1106
|
+
setTimeout(() => {
|
|
1107
|
+
navigation.reset({ index: 0, routes: [{ name: 'Home' }] });
|
|
1108
|
+
}, 1000);
|
|
1109
|
+
}, [navigation]);
|
|
1110
|
+
|
|
1111
|
+
return (
|
|
1112
|
+
<View style={styles.container}>
|
|
1113
|
+
{/* ============ VIDEO AREA ============ */}
|
|
1114
|
+
|
|
1115
|
+
{/* Android: camera views via JSX — controls can overlay on top */}
|
|
1116
|
+
{Platform.OS === 'android' && isCallActive && (
|
|
1117
|
+
<>
|
|
1118
|
+
<OmiRemoteCameraView style={StyleSheet.absoluteFillObject} />
|
|
1119
|
+
<OmiLocalCameraView style={styles.localPiP} />
|
|
1120
|
+
</>
|
|
1121
|
+
)}
|
|
1122
|
+
|
|
1123
|
+
{/* iOS Fabric: native video renders on window (top ~65%).
|
|
1124
|
+
React controls render below video area. */}
|
|
1125
|
+
|
|
1126
|
+
{/* ============ CONTROLS AREA ============ */}
|
|
1127
|
+
|
|
1128
|
+
{/* Spacer — pushes controls to bottom */}
|
|
1129
|
+
<View style={{ flex: 1 }} />
|
|
1130
|
+
|
|
1131
|
+
{/* Controls panel */}
|
|
1132
|
+
{isCallActive && (
|
|
1133
|
+
<View style={styles.controls}>
|
|
1134
|
+
<TouchableOpacity onPress={() => { toggleMute(); setIsMuted(m => !m); }}>
|
|
1135
|
+
<Text style={styles.btn}>{isMuted ? 'Unmute' : 'Mute'}</Text>
|
|
1136
|
+
</TouchableOpacity>
|
|
1137
|
+
<TouchableOpacity onPress={() => { toggleOmiVideo(); setCameraOn(c => !c); }}>
|
|
1138
|
+
<Text style={styles.btn}>{cameraOn ? 'Cam Off' : 'Cam On'}</Text>
|
|
1139
|
+
</TouchableOpacity>
|
|
1140
|
+
<TouchableOpacity onPress={switchOmiCamera}>
|
|
1141
|
+
<Text style={styles.btn}>Flip</Text>
|
|
1142
|
+
</TouchableOpacity>
|
|
1143
|
+
<TouchableOpacity onPress={handleEndCall}>
|
|
1144
|
+
<Text style={[styles.btn, { backgroundColor: 'red' }]}>End</Text>
|
|
1145
|
+
</TouchableOpacity>
|
|
1146
|
+
</View>
|
|
1147
|
+
)}
|
|
1148
|
+
|
|
1149
|
+
{/* Answer/Reject for incoming calls */}
|
|
1150
|
+
{!isCallActive && (
|
|
1151
|
+
<View style={styles.controls}>
|
|
1152
|
+
<TouchableOpacity onPress={handleEndCall}>
|
|
1153
|
+
<Text style={[styles.btn, { backgroundColor: 'red' }]}>Reject</Text>
|
|
1154
|
+
</TouchableOpacity>
|
|
1155
|
+
</View>
|
|
1156
|
+
)}
|
|
1157
|
+
</View>
|
|
1158
|
+
);
|
|
1159
|
+
};
|
|
1160
|
+
|
|
1161
|
+
const styles = StyleSheet.create({
|
|
1162
|
+
container: { flex: 1, backgroundColor: '#1E3050' },
|
|
1163
|
+
localPiP: {
|
|
1164
|
+
position: 'absolute', top: 60, right: 16,
|
|
1165
|
+
width: 120, height: 180, borderRadius: 12, overflow: 'hidden', zIndex: 10,
|
|
1166
|
+
},
|
|
1167
|
+
controls: {
|
|
1168
|
+
flexDirection: 'row', justifyContent: 'space-evenly',
|
|
1169
|
+
paddingVertical: 20, paddingHorizontal: 16,
|
|
1170
|
+
backgroundColor: 'rgba(0,0,0,0.5)', borderTopLeftRadius: 24, borderTopRightRadius: 24,
|
|
1171
|
+
paddingBottom: 40,
|
|
1172
|
+
},
|
|
1173
|
+
btn: {
|
|
1174
|
+
color: '#fff', fontSize: 14, paddingVertical: 12, paddingHorizontal: 16,
|
|
1175
|
+
backgroundColor: 'rgba(255,255,255,0.2)', borderRadius: 24, overflow: 'hidden',
|
|
1176
|
+
},
|
|
933
1177
|
});
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
> **Key differences per platform:**
|
|
1181
|
+
> - **Android**: `<OmiRemoteCameraView>` + `<OmiLocalCameraView>` render in JSX. Call `refreshRemoteCamera()` + `refreshLocalCamera()` when confirmed. Controls overlay on video.
|
|
1182
|
+
> - **iOS (Old Arch)**: Same as Android — JSX camera views work normally.
|
|
1183
|
+
> - **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).
|
|
1184
|
+
> - **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.
|
|
1185
|
+
> - **End call**: Use `navigation.reset()` instead of `goBack()` to clear stacked screens. Guard with `useRef` to prevent multiple navigations.
|
|
934
1186
|
|
|
935
|
-
|
|
936
|
-
|
|
1187
|
+
### `setCameraConfig()` — iOS Fabric Only
|
|
1188
|
+
|
|
1189
|
+
Control native video container style from JS:
|
|
1190
|
+
|
|
1191
|
+
```typescript
|
|
1192
|
+
setCameraConfig({
|
|
1193
|
+
target: 'local' | 'remote',
|
|
1194
|
+
x?: number, // X position
|
|
1195
|
+
y?: number, // Y position
|
|
1196
|
+
width?: number, // View width
|
|
1197
|
+
height?: number, // View height
|
|
1198
|
+
borderRadius?: number, // Corner radius
|
|
1199
|
+
borderWidth?: number, // Border width
|
|
1200
|
+
borderColor?: string, // Hex color (#RRGGBB or #RRGGBBAA)
|
|
1201
|
+
backgroundColor?: string,
|
|
1202
|
+
opacity?: number, // 0.0 - 1.0
|
|
1203
|
+
hidden?: boolean, // Show/hide
|
|
1204
|
+
scaleMode?: 'fill' | 'fit' | 'stretch',
|
|
1205
|
+
});
|
|
937
1206
|
```
|
|
938
1207
|
|
|
1208
|
+
### Video API Reference
|
|
1209
|
+
|
|
1210
|
+
| Function | Description | Platform |
|
|
1211
|
+
|----------|-------------|----------|
|
|
1212
|
+
| `registerVideoEvent()` | Register for video notifications | iOS only |
|
|
1213
|
+
| `removeVideoEvent()` | Cleanup video notifications | iOS only |
|
|
1214
|
+
| `refreshRemoteCamera()` | Connect remote video feed to surface | Both |
|
|
1215
|
+
| `refreshLocalCamera()` | Connect local camera feed to surface | Both |
|
|
1216
|
+
| `switchOmiCamera()` | Switch front/back camera | Both |
|
|
1217
|
+
| `toggleOmiVideo()` | Toggle camera on/off during call | Both |
|
|
1218
|
+
| `setupVideoContainers()` | Create native video containers on window | iOS Fabric only |
|
|
1219
|
+
| `setCameraConfig()` | Control video container style/position | iOS Fabric only |
|
|
1220
|
+
|
|
1221
|
+
### Known Limitations
|
|
1222
|
+
|
|
1223
|
+
- **iOS Fabric**: Controls cannot overlay on top of video (native window z-order). Use split layout — video top, controls bottom.
|
|
1224
|
+
- **iOS Fabric**: Camera switch has ~3-6s delay (SDK Metal stabilization).
|
|
1225
|
+
- **Android**: Must login with `isVideo: true` for camera to initialize. Without it, colorbar test pattern shows instead of camera.
|
|
1226
|
+
- **Simulator**: Video calls require physical device on both platforms.
|
|
1227
|
+
|
|
1228
|
+
See [Known Issues](./docs/known-issues.md) for full details.
|
|
1229
|
+
|
|
939
1230
|
---
|
|
940
1231
|
|
|
941
1232
|
## Push Notifications
|
|
942
1233
|
|
|
1234
|
+
> **Setup Guide:** To configure VoIP (iOS) and FCM (Android) for receiving incoming calls, follow the detailed guide at [OMICall Mobile SDK Setup](https://omicrm.io/post/detail/mobile-sdk-post89?lng=vi&p=BrwVVWCLGM).
|
|
1235
|
+
|
|
943
1236
|
### Configuration
|
|
944
1237
|
|
|
945
1238
|
```typescript
|
|
@@ -1165,10 +1458,14 @@ if (initialCall) {
|
|
|
1165
1458
|
| Incoming call on Android — no UI | Missing overlay permission | Call `requestSystemAlertWindowPermission()` |
|
|
1166
1459
|
| `startCall` returns 450/451/452 | Missing runtime permissions | Call `requestPermissionsByCodes([code])` |
|
|
1167
1460
|
| No audio during call | Audio route issue | Check `getAudio()` and `setAudio()` |
|
|
1168
|
-
| Video not showing | Video event not registered | Call `registerVideoEvent()` before the call |
|
|
1461
|
+
| Video not showing (iOS) | Video event not registered | Call `registerVideoEvent()` before the call |
|
|
1462
|
+
| Video shows colorbar (Android) | Login with `isVideo: false` | Login with `isVideo: true` to enable camera |
|
|
1463
|
+
| Video black screen (Android) | Feed not connected | Ensure `refreshRemoteCamera()` + `refreshLocalCamera()` called on confirmed |
|
|
1464
|
+
| iOS Fabric: controls hidden | Native video on UIWindow covers React | Use split layout — video top, controls bottom. See [Known Issues](./docs/known-issues.md) |
|
|
1169
1465
|
| NativeEventEmitter warning (iOS) | Old RN bridge issue | Upgrade to v4.0.x with New Architecture |
|
|
1170
1466
|
| `Invalid local URI` in logs | Empty proxy/host in login | Pass `host` parameter in `initCallWithUserPassword` |
|
|
1171
1467
|
| Build error with New Arch | Codegen not configured | Ensure `codegenConfig` exists in `package.json` |
|
|
1468
|
+
| iOS Simulator build fails (arm64) | OmiKit binary does not include simulator slice | **iOS Simulator is not supported.** OmiKit SDK is device-only (`arm64` real device). Always build and test on a physical iOS device |
|
|
1172
1469
|
|
|
1173
1470
|
---
|
|
1174
1471
|
|
|
@@ -1176,6 +1473,8 @@ if (initialCall) {
|
|
|
1176
1473
|
|
|
1177
1474
|
Full documentation in [`./docs/`](./docs/):
|
|
1178
1475
|
|
|
1476
|
+
- [API Integration Guide (App-to-App)](./docs/api-integration-guide.md)
|
|
1477
|
+
- [Known Issues & Limitations](./docs/known-issues.md)
|
|
1179
1478
|
- [Project Overview & PDR](./docs/project-overview-pdr.md)
|
|
1180
1479
|
- [Codebase Summary](./docs/codebase-summary.md)
|
|
1181
1480
|
- [System Architecture](./docs/system-architecture.md)
|