omikit-plugin 4.1.0 → 4.1.2

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 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.1.0
5
+ **Status:** Active maintenance | **Version:** 4.1.1
6
6
 
7
7
  ---
8
8
 
@@ -46,7 +46,7 @@ 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 |
49
+ | Android | OMIKIT | 2.6.6 |
50
50
  | iOS | OmiKit | 1.11.4 |
51
51
 
52
52
  ### Platform Requirements
@@ -54,8 +54,8 @@ The [omikit-plugin](https://www.npmjs.com/package/omikit-plugin) enables VoIP/SI
54
54
  | | Android | iOS |
55
55
  |--|---------|-----|
56
56
  | **Min SDK** | API 24 (Android 7.0) | iOS 13.0 |
57
- | **Target SDK** | API 35 (Android 15) | — |
58
- | **Compile SDK** | API 35 | — |
57
+ | **Target SDK** | API 36 (Android 16) | — |
58
+ | **Compile SDK** | API 36 | — |
59
59
 
60
60
  ### Device Requirements
61
61
 
@@ -131,7 +131,32 @@ Add to `android/app/src/main/AndroidManifest.xml`:
131
131
  >
132
132
  > Make sure to add the `tools` namespace to your manifest tag: `xmlns:tools="http://schemas.android.com/tools"`
133
133
 
134
- ### 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
+
135
160
 
136
161
  Add your `google-services.json` to `android/app/`.
137
162
 
@@ -141,7 +166,7 @@ In `android/app/build.gradle`:
141
166
  apply plugin: 'com.google.gms.google-services'
142
167
  ```
143
168
 
144
- ### 3. Maven Repository
169
+ ### 4. Maven Repository
145
170
 
146
171
  **Option A — `settings.gradle.kts` (recommended for new projects)**
147
172
 
@@ -201,7 +226,7 @@ OMI_TOKEN=omi_github_access_token
201
226
 
202
227
  > **Note:** Contact the OMICall development team to get `OMI_USER` and `OMI_TOKEN` credentials.
203
228
 
204
- ### 4. New Architecture (Optional)
229
+ ### 5. New Architecture (Optional)
205
230
 
206
231
  To enable New Architecture on Android, in `android/gradle.properties`:
207
232
 
@@ -238,13 +263,352 @@ Enable **Push Notifications** capability in Xcode for VoIP push (PushKit).
238
263
 
239
264
  ### 4. AppDelegate Setup
240
265
 
241
- 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>
242
272
 
243
273
  ```objc
244
- #import <OmiKit/OmiKit-umbrella.h>
245
- #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
246
287
  ```
247
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
393
+ ```
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
+
248
612
  ### 5. New Architecture (Optional)
249
613
 
250
614
  In your `Podfile`:
@@ -253,15 +617,20 @@ In your `Podfile`:
253
617
  ENV['RN_NEW_ARCH_ENABLED'] = '1'
254
618
  ```
255
619
 
256
- 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 ...]`:
257
621
 
258
622
  ```objc
259
- - (BOOL)bridgelessEnabled
260
- {
261
- return YES;
262
- }
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"];
263
630
  ```
264
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
+
265
634
  Then run `cd ios && pod install`.
266
635
 
267
636
  ---
@@ -293,7 +662,7 @@ Then run `cd ios && pod install`.
293
662
  │ │ │ │ │ │
294
663
  │ ▼ │ │ ▼ │
295
664
  │ OMIKIT SDK │ │ OmiKit SDK │
296
- │ (v2.6.4) │ │ (v1.10.34) │
665
+ │ (v2.6.5) │ │ (v1.11.4) │
297
666
  │ │ │ │ │ │
298
667
  │ ▼ │ │ ▼ │
299
668
  │ SIP Stack │ │ SIP Stack │
@@ -1270,73 +1639,90 @@ PBX ──► FCM ──► App receives data message
1270
1639
  - Foreground service keeps the app alive during the call
1271
1640
  - Full-screen intent for lock screen call UI
1272
1641
 
1273
- ### Notification Management
1642
+ ---
1274
1643
 
1275
- ```typescript
1276
- // Hide the system notification without affecting SIP registration
1277
- await hideSystemNotificationSafely();
1644
+ ## Permissions
1278
1645
 
1279
- // Hide notification only
1280
- await hideSystemNotificationOnly();
1646
+ Runtime permissions must be granted before making or receiving calls. We recommend using [`react-native-permissions`](https://github.com/zoontek/react-native-permissions) for a consistent cross-platform experience.
1281
1647
 
1282
- // Hide notification and unregister SIP (with reason for analytics)
1283
- await hideSystemNotificationAndUnregister('user_dismissed');
1648
+ ### Install
1649
+
1650
+ ```bash
1651
+ npm install react-native-permissions
1652
+ # iOS
1653
+ cd ios && pod install
1284
1654
  ```
1285
1655
 
1286
- ---
1656
+ ### Android Runtime Permissions
1657
+
1658
+ ```typescript
1659
+ import { check, request, PERMISSIONS, RESULTS, Platform } from 'react-native-permissions';
1287
1660
 
1288
- ## Permissions (Android)
1661
+ async function requestCallPermissions(isVideo: boolean) {
1662
+ if (Platform.OS !== 'android') return true;
1289
1663
 
1290
- Android 15+ requires explicit runtime permissions for VoIP functionality.
1664
+ // Voice call permissions
1665
+ const permissions = [
1666
+ PERMISSIONS.ANDROID.RECORD_AUDIO,
1667
+ PERMISSIONS.ANDROID.CALL_PHONE,
1668
+ PERMISSIONS.ANDROID.POST_NOTIFICATIONS, // Android 13+
1669
+ ];
1670
+
1671
+ // Video call — add camera
1672
+ if (isVideo) {
1673
+ permissions.push(PERMISSIONS.ANDROID.CAMERA);
1674
+ }
1675
+
1676
+ const results = await Promise.all(permissions.map((p) => request(p)));
1677
+ return results.every((r) => r === RESULTS.GRANTED);
1678
+ }
1679
+ ```
1291
1680
 
1292
- ### Quick Setup
1681
+ ### iOS Runtime Permissions
1293
1682
 
1294
1683
  ```typescript
1295
- import {
1296
- checkAndRequestPermissions,
1297
- checkPermissionStatus,
1298
- requestPermissionsByCodes,
1299
- requestSystemAlertWindowPermission,
1300
- openSystemAlertSetting,
1301
- } from 'omikit-plugin';
1684
+ import { request, PERMISSIONS, RESULTS, Platform } from 'react-native-permissions';
1302
1685
 
1303
- // Check and request all permissions at once
1304
- const granted = await checkAndRequestPermissions(isVideo);
1686
+ async function requestCallPermissions(isVideo: boolean) {
1687
+ if (Platform.OS !== 'ios') return true;
1305
1688
 
1306
- // Check current permission status
1307
- const status = await checkPermissionStatus();
1308
- // Returns: { microphone: boolean, camera: boolean, overlay: boolean, ... }
1689
+ const micResult = await request(PERMISSIONS.IOS.MICROPHONE);
1690
+ if (micResult !== RESULTS.GRANTED) return false;
1691
+
1692
+ if (isVideo) {
1693
+ const camResult = await request(PERMISSIONS.IOS.CAMERA);
1694
+ if (camResult !== RESULTS.GRANTED) return false;
1695
+ }
1696
+
1697
+ return true;
1698
+ }
1309
1699
  ```
1310
1700
 
1311
- ### Handle Permission Errors from startCall
1701
+ ### Overlay Permission (Android only)
1702
+
1703
+ To show incoming call UI on lock screen, overlay permission is required:
1312
1704
 
1313
1705
  ```typescript
1314
- const result = await startCall({ phoneNumber, isVideo: false });
1315
-
1316
- switch (result.status) {
1317
- case 450: // OmiStartCallStatus.permissionMicrophone
1318
- await requestPermissionsByCodes([450]);
1319
- break;
1320
- case 451: // OmiStartCallStatus.permissionCamera
1321
- await requestPermissionsByCodes([451]);
1322
- break;
1323
- case 452: // OmiStartCallStatus.permissionOverlay
1324
- await requestSystemAlertWindowPermission();
1325
- // or open system settings directly:
1326
- await openSystemAlertSetting();
1327
- break;
1328
- }
1706
+ import { requestSystemAlertWindowPermission, openSystemAlertSetting } from 'omikit-plugin';
1707
+
1708
+ // Request overlay permission
1709
+ await requestSystemAlertWindowPermission();
1710
+
1711
+ // Or open system settings directly
1712
+ await openSystemAlertSetting();
1329
1713
  ```
1330
1714
 
1331
- ### Permission Codes
1715
+ ### Permission Summary
1332
1716
 
1333
- | Code | Permission | Required for |
1334
- |------|-----------|--------------|
1335
- | 450 | Microphone | All calls |
1336
- | 451 | Camera | Video calls |
1337
- | 452 | Overlay (SYSTEM_ALERT_WINDOW) | Incoming call popup on lock screen |
1717
+ | Permission | Platform | Required for |
1718
+ |-----------|----------|--------------|
1719
+ | Microphone (`RECORD_AUDIO`) | Android & iOS | All calls |
1720
+ | Camera (`CAMERA`) | Android & iOS | Video calls only |
1721
+ | Phone (`CALL_PHONE`) | Android | VoIP calls |
1722
+ | Notification (`POST_NOTIFICATIONS`) | Android 13+ | Incoming call notifications |
1723
+ | Overlay (`SYSTEM_ALERT_WINDOW`) | Android | Incoming call popup on lock screen |
1338
1724
 
1339
- > **Note:** On iOS, permissions are handled via `Info.plist` and system prompts. The above functions return `true` on iOS.
1725
+ > **Note:** On iOS, microphone and camera permissions are handled via `Info.plist` descriptions and system prompts. Overlay permission is not applicable on iOS.
1340
1726
 
1341
1727
  ---
1342
1728
 
@@ -65,7 +65,7 @@ dependencies {
65
65
  // OMISDK
66
66
  implementation("androidx.work:work-runtime:2.8.1")
67
67
  implementation "androidx.security:security-crypto:1.1.0-alpha06"
68
- api "io.omicrm.vihat:omi-sdk:2.6.4"
68
+ api "io.omicrm.vihat:omi-sdk:2.6.8"
69
69
 
70
70
  // React Native — resolved from consumer's node_modules
71
71
  implementation "com.facebook.react:react-native:+"