omikit-plugin 3.3.4 → 3.3.6

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.
@@ -37,12 +37,70 @@ import vn.vihat.omicall.omisdk.utils.Utils
37
37
  import java.util.Timer
38
38
  import java.util.TimerTask
39
39
 
40
+ /**
41
+ * OmiSDK Registration Status Mapping
42
+ */
43
+ object OmiRegistrationStatus {
44
+ private val statusMap = mapOf(
45
+ // Success
46
+ 200 to ("ERROR_SUCCESS" to "Registration successful"),
47
+
48
+ // Parameter validation errors (4xx)
49
+ 400 to ("ERROR_MISSING_PARAMETERS" to "Missing required parameters. Please check your configuration."),
50
+ 401 to ("ERROR_INVALID_CREDENTIALS" to "Invalid credentials. Please check username/password."),
51
+
52
+ // Permission errors (45x) - Android specific
53
+ 450 to ("ERROR_MISSING_RECORD_AUDIO" to "RECORD_AUDIO permission required for Android 14+"),
54
+ 451 to ("ERROR_MISSING_FOREGROUND_SERVICE" to "FOREGROUND_SERVICE permission required"),
55
+ 452 to ("ERROR_MISSING_POST_NOTIFICATIONS" to "POST_NOTIFICATIONS permission required for Android 13+"),
56
+
57
+ // Service errors (5xx)
58
+ 500 to ("ERROR_SERVICE_START_FAILED" to "Failed to start SIP service"),
59
+ 501 to ("ERROR_SERVICE_NOT_AVAILABLE" to "SIP service not available"),
60
+ 502 to ("ERROR_SERVICE_DEGRADED" to "Service degraded - may miss calls when app killed"),
61
+
62
+ // Network errors (6xx)
63
+ 600 to ("ERROR_NETWORK_UNAVAILABLE" to "Network unavailable"),
64
+ 601 to ("ERROR_CONNECTION_TIMEOUT" to "Connection timeout"),
65
+
66
+ // Legacy compatibility
67
+ 403 to ("ERROR_FORBIDDEN" to "Access denied. Check realm/domain permissions."),
68
+ 404 to ("ERROR_REALM_NOT_FOUND" to "Realm not found. Check configuration."),
69
+ 408 to ("ERROR_TIMEOUT" to "Connection timeout"),
70
+ 503 to ("ERROR_SERVICE_UNAVAILABLE" to "Service temporarily unavailable"),
71
+
72
+ // Unknown
73
+ 999 to ("ERROR_UNKNOWN" to "Unknown error occurred")
74
+ )
75
+
76
+ fun getError(code: Int): Pair<String, String> {
77
+ return statusMap[code] ?: ("ERROR_REGISTRATION_FAILED" to "Registration failed (Status: $code)")
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Helper functions for parameter validation
83
+ */
84
+ object ValidationHelper {
85
+ fun validateRequired(params: Map<String, String?>, promise: Promise): Boolean {
86
+ params.forEach { (key, value) ->
87
+ if (value.isNullOrEmpty()) {
88
+ val errorCode = "ERROR_MISSING_${key.uppercase()}"
89
+ val message = "$key is required for OMICALL initialization. Please provide a valid $key."
90
+ promise.reject(errorCode, message)
91
+ return false
92
+ }
93
+ }
94
+ return true
95
+ }
96
+ }
40
97
 
41
98
  class OmikitPluginModule(reactContext: ReactApplicationContext?) :
42
99
  ReactContextBaseJavaModule(reactContext), ActivityEventListener, OmiListener {
43
100
  private val mainScope = CoroutineScope(Dispatchers.Main)
44
101
  private var isIncoming: Boolean = false
45
102
  private var isAnswerCall: Boolean = false
103
+ private var permissionPromise: Promise? = null
46
104
 
47
105
  override fun getName(): String {
48
106
  return NAME
@@ -522,58 +580,57 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
522
580
 
523
581
  currentActivity?.runOnUiThread {
524
582
  try {
525
- // Lấy các giá trị từ data với null safety
526
- val notificationIcon = data.getString("notificationIcon") ?: ""
527
- val prefix = data?.getString("prefix") ?: ""
528
- val incomingBackgroundColor = data?.getString("incomingBackgroundColor") ?: ""
529
- val incomingAcceptButtonImage = data?.getString("incomingAcceptButtonImage") ?: ""
530
- val incomingDeclineButtonImage = data?.getString("incomingDeclineButtonImage") ?: ""
531
- val prefixMissedCallMessage = data?.getString("prefixMissedCallMessage") ?: ""
532
- val backImage = data?.getString("backImage") ?: ""
533
- val userImage = data?.getString("userImage") ?: ""
534
- val userNameKey = data?.getString("userNameKey") ?: ""
535
- val channelId = data?.getString("channelId") ?: ""
536
- val missedCallTitle = data?.getString("missedCallTitle") ?: ""
537
- val audioNotificationDescription = data?.getString("audioNotificationDescription") ?: ""
538
- val videoNotificationDescription = data?.getString("videoNotificationDescription") ?: ""
539
- val displayNameType = data?.getString("displayNameType") ?: ""
540
- val appRepresentName = data?.getString("representName") ?: ""
541
- // Chuyển đổi isUserBusy thành Boolean
542
- val isUserBusy = data.getBoolean("isUserBusy") ?: true
543
-
544
- // Cấu hình push notification
545
- OmiClient.getInstance(context).configPushNotification(
583
+ // Extract parameters from data with proper defaults
584
+ val notificationIcon = data.getString("notificationIcon") ?: "ic_notification"
585
+ val incomingBackgroundColor = data.getString("incomingBackgroundColor") ?: "#FFFFFF"
586
+ val incomingAcceptButtonImage = data.getString("incomingAcceptButtonImage") ?: "ic_accept"
587
+ val incomingDeclineButtonImage = data.getString("incomingDeclineButtonImage") ?: "ic_decline"
588
+ val prefixMissedCallMessage = data.getString("prefixMissedCallMessage") ?: "Cuộc gọi nhỡ từ"
589
+ val backImage = data.getString("backImage") ?: "ic_back"
590
+ val userImage = data.getString("userImage") ?: "ic_user"
591
+ val userNameKey = data.getString("userNameKey") ?: "full_name"
592
+ val channelId = data.getString("channelId") ?: "omicall_channel"
593
+ val missedCallTitle = data.getString("missedCallTitle") ?: "Cuộc gọi nhỡ"
594
+ val audioNotificationDescription = data.getString("audioNotificationDescription") ?: "Cuộc gọi audio"
595
+ val videoNotificationDescription = data.getString("videoNotificationDescription") ?: "Cuộc gọi video"
596
+ val representName = data.getString("representName") ?: ""
597
+ val isUserBusy = data.getBoolean("isUserBusy")
598
+
599
+ // Configure push notification with extracted parameters
600
+ OmiClient.getInstance(context).configPushNotification(
546
601
  showMissedCall = true,
547
- notificationIcon = notificationIcon ?: "ic_notification",
548
- notificationAvatar = userImage ?: "ic_inbound_avatar_notification",
549
- fullScreenAvatar = userImage ?: "ic_inbound_avatar_fullscreen",
602
+ notificationIcon = notificationIcon,
603
+ notificationAvatar = userImage,
604
+ fullScreenAvatar = userImage,
550
605
  internalCallText = "Gọi nội bộ",
551
- videoCallText = "Gọi Video",
552
- inboundCallText = prefix,
606
+ videoCallText = videoNotificationDescription,
607
+ inboundCallText = audioNotificationDescription,
553
608
  unknownContactText = "Cuộc gọi không xác định",
554
609
  showUUID = false,
555
610
  inboundChannelId = "${channelId}-inbound",
556
611
  inboundChannelName = "Cuộc gọi đến",
557
612
  missedChannelId = "${channelId}-missed",
558
- missedChannelName = "Cuộc gọi nhỡ",
559
- displayNameType = userNameKey ?: "full_name",
560
- notificationMissedCallPrefix = prefixMissedCallMessage ?: "Cuộc gọi nhỡ từ",
561
- representName = appRepresentName ?: ""
562
- )
613
+ missedChannelName = missedCallTitle,
614
+ displayNameType = userNameKey,
615
+ notificationMissedCallPrefix = prefixMissedCallMessage,
616
+ representName = representName
617
+ )
563
618
 
564
- // Cấu hình decline call behavior
619
+ // Configure decline call behavior
565
620
  OmiClient.getInstance(context).configureDeclineCallBehavior(isUserBusy)
566
621
 
622
+ Log.d("OmikitPlugin", "✅ Push notification configured successfully")
567
623
  promise.resolve(true)
568
624
  } catch (e: Exception) {
569
- Log.e("OmikitPlugin", "Error configuring push notification: ${e.message}", e)
625
+ Log.e("OmikitPlugin", "Error configuring push notification: ${e.message}", e)
570
626
  promise.reject("E_CONFIG_FAILED", "Failed to configure push notification", e)
571
627
  }
572
628
  } ?: run {
629
+ Log.e("OmikitPlugin", "❌ Current activity is null")
573
630
  promise.reject("E_NULL_ACTIVITY", "Current activity is null")
574
631
  }
575
632
  } catch (e: Exception) {
576
- Log.e("OmikitPlugin", "Error in configPushNotification: ${e.message}", e)
633
+ Log.e("OmikitPlugin", "Error in configPushNotification: ${e.message}", e)
577
634
  promise.reject("E_UNKNOWN", "Unknown error occurred", e)
578
635
  }
579
636
  }
@@ -590,11 +647,12 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
590
647
  val projectId = data.getString("projectId") ?: ""
591
648
 
592
649
  // Validate required parameters
593
- if (userName.isNullOrEmpty() || password.isNullOrEmpty() || realm.isNullOrEmpty() || firebaseToken.isNullOrEmpty()) {
594
- Log.e("OmikitPlugin", "❌ Missing required parameters for SIP registration")
595
- promise.resolve(false)
596
- return@launch
597
- }
650
+ if (!ValidationHelper.validateRequired(mapOf(
651
+ "userName" to userName,
652
+ "password" to password,
653
+ "realm" to realm,
654
+ "fcmToken" to firebaseToken
655
+ ), promise)) return@launch
598
656
 
599
657
  withContext(Dispatchers.Default) {
600
658
  try {
@@ -610,29 +668,33 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
610
668
  Log.d("OmikitPlugin", "🔇 Using Silent Registration API for user: $userName")
611
669
 
612
670
  OmiClient.getInstance(reactApplicationContext!!).silentRegister(
613
- userName = userName,
614
- password = password,
615
- realm = realm,
671
+ userName = userName ?: "",
672
+ password = password ?: "",
673
+ realm = realm ?: "",
616
674
  isVideo = isVideo ?: true,
617
- firebaseToken = firebaseToken,
675
+ firebaseToken = firebaseToken ?: "",
618
676
  host = host,
619
677
  projectId = projectId
620
678
  ) { success, statusCode, message ->
621
679
  Log.d("OmikitPlugin", "🔇 Silent registration callback - success: $success, status: $statusCode, message: $message")
622
-
623
680
  if (success) {
624
681
  Log.d("OmikitPlugin", "✅ Silent registration successful - no notification, no auto-unregister")
682
+ // ✅ Resolve promise với kết quả từ callback
683
+ promise.resolve(success)
625
684
  } else {
626
685
  Log.e("OmikitPlugin", "❌ Silent registration failed: $message")
686
+ if (statusCode == 200) {
687
+ promise.resolve(true)
688
+ } else {
689
+ val (errorCode, errorMessage) = OmiRegistrationStatus.getError(statusCode)
690
+ promise.reject(errorCode, "$errorMessage (Status: $statusCode)")
691
+ }
627
692
  }
628
-
629
- // ✅ Resolve promise với kết quả từ callback
630
- promise.resolve(success)
631
693
  }
632
694
 
633
695
  } catch (e: Exception) {
634
696
  Log.e("OmikitPlugin", "❌ Error during silent registration: ${e.message}", e)
635
- promise.resolve(false)
697
+ promise.reject("ERROR_INITIALIZATION_EXCEPTION", "OMICALL initialization failed due to an unexpected error: ${e.message}. Please check your network connection and configuration.", e)
636
698
  }
637
699
  }
638
700
  }
@@ -653,27 +715,36 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
653
715
  requestPermission(isVideo)
654
716
  withContext(Dispatchers.Default) {
655
717
  try {
656
- if (usrName != null && usrUuid != null && apiKey != null && firebaseToken != null) {
657
- loginResult = OmiClient.registerWithApiKey(
658
- apiKey = apiKey,
659
- userName = usrName,
660
- uuid = usrUuid,
661
- phone = phone ?: "",
662
- isVideo = isVideo,
663
- firebaseToken,
664
- projectId
665
- )
666
-
667
- // Sử dụng API mới để ngăn chặn AUTO-UNREGISTER sau khi register thành công
668
- if (loginResult) {
669
- Log.d("OmikitPlugin", "🛡️ Preventing AUTO-UNREGISTER after successful API key registration")
670
- preventAutoUnregisterCrash("Successful API key registration - userName: $usrName")
671
- }
672
-
718
+ // Validate required parameters
719
+ if (!ValidationHelper.validateRequired(mapOf(
720
+ "fullName" to usrName,
721
+ "usrUuid" to usrUuid,
722
+ "apiKey" to apiKey,
723
+ "fcmToken" to firebaseToken
724
+ ), promise)) return@withContext
725
+
726
+ loginResult = OmiClient.registerWithApiKey(
727
+ apiKey = apiKey ?: "",
728
+ userName = usrName ?: "",
729
+ uuid = usrUuid ?: "",
730
+ phone = phone ?: "",
731
+ isVideo = isVideo,
732
+ firebaseToken,
733
+ projectId
734
+ )
735
+
736
+ // ✅ Sử dụng API mới để ngăn chặn AUTO-UNREGISTER sau khi register thành công
737
+ if (loginResult) {
738
+ Log.d("OmikitPlugin", "🛡️ Preventing AUTO-UNREGISTER after successful API key registration")
739
+ preventAutoUnregisterCrash("Successful API key registration - userName: $usrName")
673
740
  promise.resolve(true)
741
+ } else {
742
+ Log.e("OmikitPlugin", "❌ API key registration failed")
743
+ promise.reject("ERROR_API_KEY_REGISTRATION_FAILED", "OMICALL API key initialization failed. Please check your API key, UUID, and network connection.")
674
744
  }
675
- } catch (_: Throwable) {
676
- promise.resolve(loginResult)
745
+ } catch (e: Exception) {
746
+ Log.e("OmikitPlugin", "❌ Error during API key registration: ${e.message}", e)
747
+ promise.reject("ERROR_API_KEY_INITIALIZATION_EXCEPTION", "OMICALL API key initialization failed due to an unexpected error: ${e.message}. Please check your configuration and network connection.", e)
677
748
  }
678
749
  }
679
750
  }
@@ -1099,6 +1170,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
1099
1170
 
1100
1171
  companion object {
1101
1172
  const val NAME = "OmikitPlugin"
1173
+ const val REQUEST_PERMISSIONS_CODE = 1001
1174
+ const val REQUEST_OVERLAY_PERMISSION_CODE = 1002
1102
1175
 
1103
1176
  fun onDestroy() {
1104
1177
  try {
@@ -1123,9 +1196,55 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
1123
1196
  act: ReactActivity,
1124
1197
  ) {
1125
1198
  act.runOnUiThread {
1199
+ // Handle our custom permission request
1200
+ if (requestCode == REQUEST_PERMISSIONS_CODE) {
1201
+ handlePermissionResults(permissions, grantResults, act)
1202
+ }
1203
+
1204
+ // Also handle SDK permission request
1126
1205
  OmiSDKUtils.handlePermissionRequest(requestCode, permissions, grantResults, act)
1127
1206
  }
1128
1207
  }
1208
+
1209
+ private fun handlePermissionResults(
1210
+ permissions: Array<out String>,
1211
+ grantResults: IntArray,
1212
+ act: ReactActivity
1213
+ ) {
1214
+ try {
1215
+ val deniedPermissions = mutableListOf<String>()
1216
+ val grantedPermissions = mutableListOf<String>()
1217
+
1218
+ for (i in permissions.indices) {
1219
+ if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
1220
+ grantedPermissions.add(permissions[i])
1221
+ } else {
1222
+ deniedPermissions.add(permissions[i])
1223
+ }
1224
+ }
1225
+
1226
+ Log.d("OmikitPlugin", "✅ Granted: ${grantedPermissions.joinToString()}")
1227
+ if (deniedPermissions.isNotEmpty()) {
1228
+ Log.w("OmikitPlugin", "❌ Denied: ${deniedPermissions.joinToString()}")
1229
+ }
1230
+
1231
+ // Check if we have essential permissions for VoIP
1232
+ val hasRecordAudio = grantedPermissions.contains(Manifest.permission.RECORD_AUDIO)
1233
+ val hasCallPhone = grantedPermissions.contains(Manifest.permission.CALL_PHONE)
1234
+ val hasModifyAudio = grantedPermissions.contains(Manifest.permission.MODIFY_AUDIO_SETTINGS)
1235
+
1236
+ val canProceed = hasRecordAudio && hasCallPhone && hasModifyAudio
1237
+
1238
+ if (canProceed) {
1239
+ Log.d("OmikitPlugin", "🎉 Essential VoIP permissions granted!")
1240
+ } else {
1241
+ Log.e("OmikitPlugin", "⚠️ Missing essential VoIP permissions - app may not work properly")
1242
+ }
1243
+
1244
+ } catch (e: Exception) {
1245
+ Log.e("OmikitPlugin", "❌ Error handling permission results: ${e.message}", e)
1246
+ }
1247
+ }
1129
1248
 
1130
1249
  fun onGetIntentFromNotification(
1131
1250
  context: ReactApplicationContext,
@@ -1217,23 +1336,224 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
1217
1336
  )
1218
1337
  }
1219
1338
 
1220
- private fun requestPermission(isVideo: Boolean) {
1221
- var permissions = arrayOf(
1339
+ @ReactMethod
1340
+ fun checkAndRequestPermissions(isVideo: Boolean, promise: Promise) {
1341
+ try {
1342
+ val missingPermissions = getMissingPermissions(isVideo)
1343
+
1344
+ if (missingPermissions.isEmpty()) {
1345
+ Log.d("OmikitPlugin", "✅ All permissions already granted")
1346
+ promise.resolve(true)
1347
+ return
1348
+ }
1349
+
1350
+ Log.d("OmikitPlugin", "📋 Missing permissions: ${missingPermissions.joinToString()}")
1351
+
1352
+ // Store promise for callback
1353
+ permissionPromise = promise
1354
+
1355
+ ActivityCompat.requestPermissions(
1356
+ reactApplicationContext.currentActivity!!,
1357
+ missingPermissions.toTypedArray(),
1358
+ REQUEST_PERMISSIONS_CODE,
1359
+ )
1360
+ } catch (e: Exception) {
1361
+ Log.e("OmikitPlugin", "❌ Error checking permissions: ${e.message}", e)
1362
+ promise.resolve(false)
1363
+ }
1364
+ }
1365
+
1366
+ private fun getMissingPermissions(isVideo: Boolean): List<String> {
1367
+ val allPermissions = mutableListOf<String>()
1368
+
1369
+ // Basic permissions for VoIP
1370
+ allPermissions.addAll(arrayOf(
1222
1371
  Manifest.permission.USE_SIP,
1223
1372
  Manifest.permission.CALL_PHONE,
1224
1373
  Manifest.permission.MODIFY_AUDIO_SETTINGS,
1225
1374
  Manifest.permission.RECORD_AUDIO,
1226
- )
1375
+ ))
1376
+
1377
+ // Video call permissions
1227
1378
  if (isVideo) {
1228
- permissions = permissions.plus(Manifest.permission.CAMERA)
1379
+ allPermissions.add(Manifest.permission.CAMERA)
1229
1380
  }
1381
+
1382
+ // Android 13+ notifications
1230
1383
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
1231
- permissions = permissions.plus(Manifest.permission.POST_NOTIFICATIONS)
1384
+ allPermissions.add(Manifest.permission.POST_NOTIFICATIONS)
1232
1385
  }
1386
+
1387
+ // Android 14+ foreground service permissions
1388
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
1389
+ allPermissions.addAll(arrayOf(
1390
+ Manifest.permission.FOREGROUND_SERVICE_MICROPHONE,
1391
+ Manifest.permission.FOREGROUND_SERVICE_PHONE_CALL
1392
+ ))
1393
+ }
1394
+
1395
+ return allPermissions.filter { permission ->
1396
+ ContextCompat.checkSelfPermission(reactApplicationContext, permission) != PackageManager.PERMISSION_GRANTED
1397
+ }
1398
+ }
1399
+
1400
+ @ReactMethod
1401
+ fun checkPermissionStatus(promise: Promise) {
1402
+ try {
1403
+ val permissionStatus = mutableMapOf<String, Any>()
1404
+
1405
+ // Essential permissions
1406
+ val essentialPermissions = arrayOf(
1407
+ Manifest.permission.RECORD_AUDIO,
1408
+ Manifest.permission.CALL_PHONE,
1409
+ Manifest.permission.MODIFY_AUDIO_SETTINGS,
1410
+ Manifest.permission.USE_SIP
1411
+ )
1412
+
1413
+ // Check essential permissions
1414
+ val missingEssential = mutableListOf<String>()
1415
+ val grantedEssential = mutableListOf<String>()
1416
+
1417
+ essentialPermissions.forEach { permission ->
1418
+ if (ContextCompat.checkSelfPermission(reactApplicationContext, permission) == PackageManager.PERMISSION_GRANTED) {
1419
+ grantedEssential.add(permission)
1420
+ } else {
1421
+ missingEssential.add(permission)
1422
+ }
1423
+ }
1424
+
1425
+ // Check Android 14+ foreground service permissions
1426
+ val foregroundServiceStatus = mutableMapOf<String, Boolean>()
1427
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
1428
+ foregroundServiceStatus["FOREGROUND_SERVICE_MICROPHONE"] = ContextCompat.checkSelfPermission(
1429
+ reactApplicationContext,
1430
+ Manifest.permission.FOREGROUND_SERVICE_MICROPHONE
1431
+ ) == PackageManager.PERMISSION_GRANTED
1432
+
1433
+ foregroundServiceStatus["FOREGROUND_SERVICE_PHONE_CALL"] = ContextCompat.checkSelfPermission(
1434
+ reactApplicationContext,
1435
+ Manifest.permission.FOREGROUND_SERVICE_PHONE_CALL
1436
+ ) == PackageManager.PERMISSION_GRANTED
1437
+ }
1438
+
1439
+ // Check system alert window permission
1440
+ val canDrawOverlays = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
1441
+ Settings.canDrawOverlays(reactApplicationContext)
1442
+ } else {
1443
+ true
1444
+ }
1445
+
1446
+ permissionStatus["essentialGranted"] = grantedEssential
1447
+ permissionStatus["essentialMissing"] = missingEssential
1448
+ permissionStatus["canMakeVoipCalls"] = missingEssential.isEmpty()
1449
+ permissionStatus["foregroundServicePermissions"] = foregroundServiceStatus
1450
+ permissionStatus["canDrawOverlays"] = canDrawOverlays
1451
+ permissionStatus["androidVersion"] = Build.VERSION.SDK_INT
1452
+ permissionStatus["targetSdk"] = reactApplicationContext.applicationInfo.targetSdkVersion
1453
+
1454
+ val map = Arguments.makeNativeMap(permissionStatus)
1455
+ promise.resolve(map)
1456
+
1457
+ } catch (e: Exception) {
1458
+ Log.e("OmikitPlugin", "❌ Error checking permission status: ${e.message}", e)
1459
+ promise.resolve(null)
1460
+ }
1461
+ }
1462
+
1463
+ @ReactMethod
1464
+ fun requestSystemAlertWindowPermission(promise: Promise) {
1465
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
1466
+ if (!Settings.canDrawOverlays(reactApplicationContext)) {
1467
+ permissionPromise = promise
1468
+ val intent = Intent(
1469
+ Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
1470
+ Uri.parse("package:${reactApplicationContext.packageName}")
1471
+ )
1472
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
1473
+ reactApplicationContext.currentActivity?.startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION_CODE)
1474
+ } else {
1475
+ promise.resolve(true)
1476
+ }
1477
+ } else {
1478
+ promise.resolve(true)
1479
+ }
1480
+ }
1481
+
1482
+ /**
1483
+ * Request specific permissions for error codes 450, 451, 452
1484
+ * Shows permission request popup for customers
1485
+ * @param codes - Array of permission codes to request (450, 451, 452)
1486
+ * @param promise - Promise to resolve with request result
1487
+ */
1488
+ @ReactMethod
1489
+ fun requestPermissionsByCodes(codes: ReadableArray, promise: Promise) {
1490
+ try {
1491
+ val permissionCodes = codes.toArrayList().map { it.toString().toInt() }
1492
+ val permissionsToRequest = mutableListOf<String>()
1493
+
1494
+ for (code in permissionCodes) {
1495
+ when (code) {
1496
+ 450 -> { // RECORD_AUDIO permission
1497
+ if (ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
1498
+ permissionsToRequest.add(Manifest.permission.RECORD_AUDIO)
1499
+ }
1500
+ }
1501
+ 451 -> { // FOREGROUND_SERVICE permissions
1502
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
1503
+ if (ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.FOREGROUND_SERVICE_MICROPHONE) != PackageManager.PERMISSION_GRANTED) {
1504
+ permissionsToRequest.add(Manifest.permission.FOREGROUND_SERVICE_MICROPHONE)
1505
+ }
1506
+ if (ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.FOREGROUND_SERVICE_PHONE_CALL) != PackageManager.PERMISSION_GRANTED) {
1507
+ permissionsToRequest.add(Manifest.permission.FOREGROUND_SERVICE_PHONE_CALL)
1508
+ }
1509
+ }
1510
+ }
1511
+ 452 -> { // POST_NOTIFICATIONS permission
1512
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
1513
+ if (ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
1514
+ permissionsToRequest.add(Manifest.permission.POST_NOTIFICATIONS)
1515
+ }
1516
+ }
1517
+ }
1518
+ }
1519
+ }
1520
+
1521
+ if (permissionsToRequest.isEmpty()) {
1522
+ promise.resolve(true)
1523
+ return
1524
+ }
1525
+
1526
+ // Store promise for callback
1527
+ permissionPromise = promise
1528
+
1529
+ Log.d("OmikitPlugin", "🔐 Requesting permissions for codes ${permissionCodes.joinToString()}: ${permissionsToRequest.joinToString()}")
1530
+
1531
+ ActivityCompat.requestPermissions(
1532
+ reactApplicationContext.currentActivity!!,
1533
+ permissionsToRequest.toTypedArray(),
1534
+ REQUEST_PERMISSIONS_CODE
1535
+ )
1536
+
1537
+ } catch (e: Exception) {
1538
+ Log.e("OmikitPlugin", "❌ Error requesting permissions by codes: ${e.message}", e)
1539
+ promise.reject("ERROR_PERMISSION_REQUEST", "Failed to request permissions: ${e.message}")
1540
+ }
1541
+ }
1542
+
1543
+ private fun requestPermission(isVideo: Boolean) {
1544
+ val missingPermissions = getMissingPermissions(isVideo)
1545
+
1546
+ if (missingPermissions.isEmpty()) {
1547
+ Log.d("OmikitPlugin", "✅ All permissions already granted")
1548
+ return
1549
+ }
1550
+
1551
+ Log.d("OmikitPlugin", "📋 Requesting missing permissions for Android ${Build.VERSION.SDK_INT}: ${missingPermissions.joinToString()}")
1552
+
1233
1553
  ActivityCompat.requestPermissions(
1234
1554
  reactApplicationContext.currentActivity!!,
1235
- permissions,
1236
- 0,
1555
+ missingPermissions.toTypedArray(),
1556
+ REQUEST_PERMISSIONS_CODE,
1237
1557
  )
1238
1558
  }
1239
1559
 
@@ -1422,10 +1742,10 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
1422
1742
  Log.d("OmikitPlugin", "🔍 Checking credentials for user: $userName")
1423
1743
 
1424
1744
  OmiClient.getInstance(reactApplicationContext!!).checkCredentials(
1425
- userName = userName,
1426
- password = password,
1427
- realm = realm,
1428
- firebaseToken = firebaseToken,
1745
+ userName = userName ?: "",
1746
+ password = password ?: "",
1747
+ realm = realm ?: "",
1748
+ firebaseToken = firebaseToken ?: "",
1429
1749
  host = host,
1430
1750
  projectId = projectId
1431
1751
  ) { success, statusCode, message ->
@@ -1478,12 +1798,12 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
1478
1798
  Log.d("OmikitPlugin", "⚙️ Registering with options for user: $userName - showNotification: $showNotification, enableAutoUnregister: $enableAutoUnregister")
1479
1799
 
1480
1800
  OmiClient.getInstance(reactApplicationContext!!).registerWithOptions(
1481
- userName = userName,
1482
- password = password,
1483
- realm = realm,
1801
+ userName = userName ?: "",
1802
+ password = password ?: "",
1803
+ realm = realm ?: "",
1484
1804
  isVideo = isVideo ?: true,
1485
- firebaseToken = firebaseToken,
1486
- host = host,
1805
+ firebaseToken = firebaseToken ?: "",
1806
+ host = host ,
1487
1807
  projectId = projectId,
1488
1808
  showNotification = showNotification,
1489
1809
  enableAutoUnregister = enableAutoUnregister
@@ -1 +1 @@
1
- {"version":3,"names":["_omikit","require","Object","keys","forEach","key","exports","defineProperty","enumerable","get","_omi_local_camera","_omi_remote_camera","_omi_call_state","_omi_start_call_status"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,OAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,OAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAT,OAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AACA,IAAAK,iBAAA,GAAAT,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAO,iBAAA,EAAAN,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAK,iBAAA,CAAAL,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,iBAAA,CAAAL,GAAA;IAAA;EAAA;AAAA;AACA,IAAAM,kBAAA,GAAAV,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAQ,kBAAA,EAAAP,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAM,kBAAA,CAAAN,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAE,kBAAA,CAAAN,GAAA;IAAA;EAAA;AAAA;AACA,IAAAO,eAAA,GAAAX,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAS,eAAA,EAAAR,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAO,eAAA,CAAAP,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAG,eAAA,CAAAP,GAAA;IAAA;EAAA;AAAA;AACA,IAAAQ,sBAAA,GAAAZ,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAU,sBAAA,EAAAT,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAQ,sBAAA,CAAAR,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAI,sBAAA,CAAAR,GAAA;IAAA;EAAA;AAAA"}
1
+ {"version":3,"names":["_omikit","require","Object","keys","forEach","key","exports","defineProperty","enumerable","get","_omi_local_camera","_omi_remote_camera","_omi_call_state","_omi_start_call_status"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,OAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,OAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAT,OAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AACA,IAAAK,iBAAA,GAAAT,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAO,iBAAA,EAAAN,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAK,iBAAA,CAAAL,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,iBAAA,CAAAL,GAAA;IAAA;EAAA;AAAA;AACA,IAAAM,kBAAA,GAAAV,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAQ,kBAAA,EAAAP,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAM,kBAAA,CAAAN,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAE,kBAAA,CAAAN,GAAA;IAAA;EAAA;AAAA;AACA,IAAAO,eAAA,GAAAX,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAS,eAAA,EAAAR,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAO,eAAA,CAAAP,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAG,eAAA,CAAAP,GAAA;IAAA;EAAA;AAAA;AACA,IAAAQ,sBAAA,GAAAZ,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAU,sBAAA,EAAAT,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAQ,sBAAA,CAAAR,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAI,sBAAA,CAAAR,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
@@ -4,9 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.OmiCallState = void 0;
7
- let OmiCallState;
8
- exports.OmiCallState = OmiCallState;
9
- (function (OmiCallState) {
7
+ let OmiCallState = exports.OmiCallState = /*#__PURE__*/function (OmiCallState) {
10
8
  OmiCallState[OmiCallState["unknown"] = 0] = "unknown";
11
9
  OmiCallState[OmiCallState["calling"] = 1] = "calling";
12
10
  OmiCallState[OmiCallState["incoming"] = 2] = "incoming";
@@ -15,5 +13,6 @@ exports.OmiCallState = OmiCallState;
15
13
  OmiCallState[OmiCallState["confirmed"] = 5] = "confirmed";
16
14
  OmiCallState[OmiCallState["disconnected"] = 6] = "disconnected";
17
15
  OmiCallState[OmiCallState["hold"] = 7] = "hold";
18
- })(OmiCallState || (exports.OmiCallState = OmiCallState = {}));
16
+ return OmiCallState;
17
+ }({});
19
18
  //# sourceMappingURL=omi_call_state.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["OmiCallState","exports"],"sourceRoot":"../../src","sources":["omi_call_state.tsx"],"mappings":";;;;;;IAAYA,YAAY;AAAAC,OAAA,CAAAD,YAAA,GAAAA,YAAA;AAAA,WAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;AAAA,GAAZA,YAAY,KAAAC,OAAA,CAAAD,YAAA,GAAZA,YAAY"}
1
+ {"version":3,"names":["OmiCallState","exports"],"sourceRoot":"../../src","sources":["omi_call_state.tsx"],"mappings":";;;;;;IAAYA,YAAY,GAAAC,OAAA,CAAAD,YAAA,0BAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAZA,YAAY,CAAZA,YAAY;EAAA,OAAZA,YAAY;AAAA","ignoreList":[]}
@@ -7,8 +7,7 @@ exports.OmiLocalCameraView = void 0;
7
7
  exports.refreshLocalCamera = refreshLocalCamera;
8
8
  var _reactNative = require("react-native");
9
9
  const FLLocalCamera = _reactNative.NativeModules.FLLocalCameraView;
10
- const OmiLocalCameraView = (0, _reactNative.requireNativeComponent)('FLLocalCameraView');
11
- exports.OmiLocalCameraView = OmiLocalCameraView;
10
+ const OmiLocalCameraView = exports.OmiLocalCameraView = (0, _reactNative.requireNativeComponent)('FLLocalCameraView');
12
11
  function refreshLocalCamera() {
13
12
  return FLLocalCamera.refresh();
14
13
  }
@@ -1 +1 @@
1
- {"version":3,"names":["_reactNative","require","FLLocalCamera","NativeModules","FLLocalCameraView","OmiLocalCameraView","requireNativeComponent","exports","refreshLocalCamera","refresh"],"sourceRoot":"../../src","sources":["omi_local_camera.tsx"],"mappings":";;;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAMC,aAAa,GAAGC,0BAAa,CAACC,iBAAiB;AAE9C,MAAMC,kBAA4C,GACvD,IAAAC,mCAAsB,EAAC,mBAAmB,CAAC;AAACC,OAAA,CAAAF,kBAAA,GAAAA,kBAAA;AAEvC,SAASG,kBAAkBA,CAAA,EAAqB;EACrD,OAAON,aAAa,CAACO,OAAO,EAAE;AAChC"}
1
+ {"version":3,"names":["_reactNative","require","FLLocalCamera","NativeModules","FLLocalCameraView","OmiLocalCameraView","exports","requireNativeComponent","refreshLocalCamera","refresh"],"sourceRoot":"../../src","sources":["omi_local_camera.tsx"],"mappings":";;;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAMC,aAAa,GAAGC,0BAAa,CAACC,iBAAiB;AAE9C,MAAMC,kBAA4C,GAAAC,OAAA,CAAAD,kBAAA,GACvD,IAAAE,mCAAsB,EAAC,mBAAmB,CAAC;AAEtC,SAASC,kBAAkBA,CAAA,EAAqB;EACrD,OAAON,aAAa,CAACO,OAAO,CAAC,CAAC;AAChC","ignoreList":[]}