omikit-plugin 3.2.70 → 3.2.72

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
@@ -591,7 +591,7 @@ startServices();
591
591
  - The `initCallWithApiKey()` function is usually used for your client, who only has a certain function, calling a fixed number. For example, you can only call your hotline number
592
592
 
593
593
  ```javascript
594
- import { initCallWithApiKey } from 'omikit-plugin';
594
+ import { initCallWithApiKey, getCurrentUser } from 'omikit-plugin';
595
595
  import messaging from '@react-native-firebase/messaging';
596
596
 
597
597
  let token: String;
@@ -617,7 +617,18 @@ const loginInfo = {
617
617
  // Initialize call functionality using the provided API key
618
618
  const result = await initCallWithApiKey(loginInfo);
619
619
 
620
- // If result is true, the user has successfully logged in.
620
+ /* NOTE: Please check the user information again, if the object is not empty then you have successfully logged in.
621
+ Otherwise, if you have not successfully logged in, you should not navigate to the call screen. When startCall with empty information, it may crash your application or not be clear when receiving the startCall error ❌❌*/
622
+
623
+ // Example:
624
+
625
+ if (result){
626
+ const infoUser = await getCurrentUser()
627
+ if (infoUser != null && Object.keys(infoUser).length > 0) {
628
+ // ✅ Login OMI Success
629
+ // Can navigate to call screen or start call 🚀 🚀
630
+ }
631
+ }
621
632
  ```
622
633
 
623
634
  📌 **initCallWithUserPassword()**
@@ -628,7 +639,7 @@ const result = await initCallWithApiKey(loginInfo);
628
639
  - The `initCallWithUserPassword()` function is for employees. They can call any telecommunications number allowed in your business on the OMI system.
629
640
 
630
641
  ```javascript
631
- import { initCallWithUserPassword } from 'omikit-plugin';
642
+ import { initCallWithUserPassword, getCurrentUser } from 'omikit-plugin';
632
643
  import messaging from '@react-native-firebase/messaging';
633
644
 
634
645
  let token: String;
@@ -652,7 +663,20 @@ const loginInfo = {
652
663
 
653
664
  // Initialize call functionality using username and password authentication
654
665
  const result = await initCallWithUserPassword(loginInfo);
655
- // If result is true, the user has successfully logged in.
666
+
667
+
668
+ /* ❌ ❌ NOTE: Please check the user information again, if the object is not empty then you have successfully logged in.
669
+ Otherwise, if you have not successfully logged in, you should not navigate to the call screen. When startCall with empty information, it may crash your application or not be clear when receiving the startCall error ❌❌*/
670
+
671
+ // Example:
672
+
673
+ if (result){
674
+ const infoUser = await getCurrentUser()
675
+ if (infoUser != null && Object.keys(infoUser).length > 0) {
676
+ // ✅ Login OMI Success
677
+ // Can navigate to call screen or start call 🚀 🚀
678
+ }
679
+ }
656
680
  ```
657
681
 
658
682
  📌 **configPushNotification()**
@@ -909,7 +933,9 @@ const result = await startCallWithUuid({
909
933
  {
910
934
  "extension": "111",
911
935
  "full_name": "chau1",
936
+ "fullName": "chau1",
912
937
  "avatar_url": "",
938
+ "avatarUrl": "",
913
939
  "uuid": "122aaa"
914
940
  }
915
941
  ```
@@ -1060,6 +1086,20 @@ const result = await startCallWithUuid({
1060
1086
  ### 🚀🚀 Events listener 🚀🚀
1061
1087
 
1062
1088
  ```javascript
1089
+ import { omiEmitter } from 'omikit-plugin';
1090
+
1091
+ /*
1092
+ ❌ ❌ With TypeScript, in android, it seems our omiEmitter is not working properly. Please use the following manual declaration, to ensure performance
1093
+ */
1094
+
1095
+ // 📌 For TypeScript, Android
1096
+ import { NativeEventEmitter, NativeModules } from "react-native";
1097
+ const { OmikitPlugin } = NativeModules;
1098
+ const omiEmitter = new NativeEventEmitter(OmikitPlugin);
1099
+
1100
+
1101
+
1102
+
1063
1103
  useEffect(() => {
1064
1104
  omiEmitter.addListener(OmiCallEvent.onCallStateChanged, onCallStateChanged);
1065
1105
  omiEmitter.addListener(OmiCallEvent.onMuted, onMuted);
@@ -1129,24 +1169,27 @@ useEffect(() => {
1129
1169
  ```javascript
1130
1170
  // The event is updated every time the call status changes
1131
1171
  const onCallStateChanged = (data: any) => {
1172
+ // ⚠️ ⚠️ Currently, we support two data formats: camelCase and snake_case. Snake_case is used for data v1, while camelCase is for v2. We encourage customers to use camelCase instead of snake_case, as we plan to completely remove the snake_case format in the future ❌ ❌
1173
+
1132
1174
  /*
1133
1175
  Call state change event data (Object) includes:
1134
1176
 
1135
1177
  - _id: string (UUID of the call)
1136
1178
  - callInfo: object (Detailed call information)
1137
1179
  - callerNumber: string (Phone number of the caller)
1138
- - code_end_call: number (Status code when the call ends)
1139
- - destination_number?: string (Destination phone number, optional)
1180
+ - code_end_call, codeEndCall: number (Status code when the call ends)
1181
+ - destination_number, destinationNumber?: string (Destination phone number, optional)
1140
1182
  - direction: string ("inbound" or "outbound", call direction)
1141
1183
  - disposition: string (Call answer status)
1142
1184
  - incoming: boolean (true if it is an incoming call)
1143
1185
  - isVideo: boolean (true if it is a video call)
1144
- - sip_user: string (Current SIP user)
1145
- - source_number: string (SIP number of the user)
1186
+ - sip_user, sipUser: string (Current SIP user)
1187
+ - source_number, sourceNumber: string (SIP number of the user)
1146
1188
  - status: string (value matching with List status call)
1147
- - time_end: number (Timestamp when the call ended)
1148
- - time_start_to_answer: number (Time taken to answer the call)
1149
- - transaction_id: string (OMI Call unique ID)
1189
+ - time_end, timeEnd: number (Timestamp when the call ended)
1190
+ - time_start_to_answer, timeStartToAnswer: number (Time taken to answer the call)
1191
+ - transaction_id, transactionId: string (OMI Call unique ID)
1192
+ - typeNumber: string ("", "internal", "phone", "zalo")
1150
1193
  */
1151
1194
  };
1152
1195
 
@@ -1180,7 +1223,7 @@ const onSwitchboardAnswer = (data: any) => {
1180
1223
  }
1181
1224
  ```
1182
1225
 
1183
- ✨ Table describing `code_end_call` status
1226
+ ✨ Table describing `code_end_call, codeEndCall` status
1184
1227
 
1185
1228
  | Code | Description |
1186
1229
  | --------------- | --------------------------------------------------------------------------------------------------------------------- |
@@ -120,7 +120,7 @@ dependencies {
120
120
  // use for OMISDK
121
121
  implementation("androidx.work:work-runtime:2.8.1")
122
122
  implementation "androidx.security:security-crypto:1.1.0-alpha06"
123
- api 'vn.vihat.omicall:omi-sdk:2.3.19'
123
+ api 'vn.vihat.omicall:omi-sdk:2.3.22'
124
124
 
125
125
  implementation "com.facebook.react:react-native:+" // From node_modules
126
126
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
@@ -4,4 +4,4 @@ OmikitPlugin_targetSdkVersion=33
4
4
  OmikitPlugin_compileSdkVersion=33
5
5
  OmikitPlugin_ndkversion=21.4.7075529
6
6
  OMI_USER=omicall
7
- OMI_TOKEN=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
7
+ OMI_TOKEN=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
@@ -24,6 +24,7 @@ import kotlinx.coroutines.CoroutineScope
24
24
  import kotlinx.coroutines.Dispatchers
25
25
  import kotlinx.coroutines.launch
26
26
  import kotlinx.coroutines.withContext
27
+ import kotlinx.coroutines.delay
27
28
  import vn.vihat.omicall.omisdk.OmiAccountListener
28
29
  import vn.vihat.omicall.omisdk.OmiClient
29
30
  import vn.vihat.omicall.omisdk.OmiListener
@@ -38,76 +39,105 @@ import vn.vihat.omicall.omisdk.utils.Utils
38
39
  class OmikitPluginModule(reactContext: ReactApplicationContext?) :
39
40
  ReactContextBaseJavaModule(reactContext), ActivityEventListener, OmiListener {
40
41
  private val mainScope = CoroutineScope(Dispatchers.Main)
41
- private var isIncomming: Boolean = false
42
+ private var isIncoming: Boolean = false
42
43
  private var isAnserCall: Boolean = false
43
44
 
44
45
  override fun getName(): String {
45
46
  return NAME
46
47
  }
47
48
 
49
+
50
+ private val handler = Handler(Looper.getMainLooper())
51
+
48
52
  override fun incomingReceived(callerId: Int?, phoneNumber: String?, isVideo: Boolean?) {
49
- isIncomming = true;
53
+ isIncoming = true;
50
54
  Log.d("OMISDK", "=>> START INCOMING CALL REVICED => ")
51
- val map: WritableMap = WritableNativeMap()
52
- map.putBoolean("isVideo", isVideo ?: true)
53
- map.putBoolean("incoming", isIncomming)
54
- map.putString("callerNumber", phoneNumber)
55
- map.putString("_id", "")
56
- map.putInt("status", CallState.incoming.value)
55
+
56
+ val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber ?: "")
57
+
58
+ val map: WritableMap = WritableNativeMap().apply {
59
+ putBoolean("isVideo", isVideo ?: true)
60
+ putBoolean("incoming", isIncoming)
61
+ putString("callerNumber", phoneNumber ?: "")
62
+ putString("_id", "")
63
+ putInt("status", CallState.incoming.value)
64
+ putString("typeNumber", typeNumber)
65
+ }
66
+
57
67
  sendEvent(CALL_STATE_CHANGED, map)
58
68
  }
59
69
 
60
70
  override fun onCallEstablished(
61
- callerId: Int,
62
- phoneNumber: String?,
63
- isVideo: Boolean?,
64
- startTime: Long,
65
- transactionId: String?,
71
+ callerId: Int,
72
+ phoneNumber: String?,
73
+ isVideo: Boolean?,
74
+ startTime: Long,
75
+ transactionId: String?,
66
76
  ) {
67
- isAnserCall = true
68
- Log.d("OMISDK", "=>> ON CALL ESTABLISHED => ")
69
- Handler(Looper.getMainLooper()).postDelayed({
70
- Log.d("OmikitReactNative", "onCallEstablished")
71
- val map: WritableMap = WritableNativeMap()
72
- map.putString("callerNumber", phoneNumber)
73
- map.putBoolean("isVideo", isVideo ?: true)
74
- map.putBoolean("incoming", isIncomming)
75
- map.putString("transactionId", transactionId)
76
- map.putInt("status", CallState.confirmed.value)
77
- sendEvent(CALL_STATE_CHANGED, map)
78
- }, 200)
77
+ isAnserCall = true
78
+ Log.d("OMISDK", "=>> ON CALL ESTABLISHED => ")
79
+
80
+ Handler(Looper.getMainLooper()).postDelayed({
81
+ Log.d("OmikitReactNative", "onCallEstablished")
82
+
83
+ val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber ?: "")
84
+
85
+ val map: WritableMap = WritableNativeMap().apply {
86
+ putString("callerNumber", phoneNumber ?: "")
87
+ putBoolean("isVideo", isVideo ?: true)
88
+ putBoolean("incoming", isIncoming) // 🔹 Kiểm tra lỗi chính tả biến này
89
+ putString("transactionId", transactionId ?: "")
90
+ putInt("status", CallState.confirmed.value)
91
+ putString("typeNumber", typeNumber)
92
+ }
93
+
94
+ sendEvent(CALL_STATE_CHANGED, map)
95
+ }, 200)
79
96
  }
80
97
 
81
98
  override fun onCallEnd(callInfo: MutableMap<String, Any?>, statusCode: Int) {
82
- Log.d("OMISDK RN", "=>> onCallEnd 0000 => $callInfo")
83
- val call = callInfo as Map<*, *>
84
- val map: WritableMap = WritableNativeMap()
85
- val timeStartToAnswer = call["time_start_to_answer"] as Long?
86
- val timeEnd = call["time_end"] as Long
87
- map.putString("transaction_id", call["transaction_id"] as String?)
88
- map.putString("direction", call["direction"] as String)
89
- map.putString("source_number", call["source_number"] as String)
90
- map.putString("destination_number", call["destination_number"] as String)
91
- map.putDouble("time_start_to_answer", (timeStartToAnswer ?: 0).toDouble())
92
- map.putDouble("time_end", timeEnd.toDouble())
93
- map.putString("sip_user", call["sip_user"] as String)
94
- map.putString("disposition", call["disposition"] as String)
95
- map.putInt("status", CallState.disconnected.value)
96
- map.putInt("code_end_call", statusCode as Int)
97
- Log.d("OMISDK RN", "=>> onCallEnd => $map")
98
- sendEvent(CALL_STATE_CHANGED, map)
99
+ Log.d("OMISDK RN", "=>> onCallEnd 0000 => $callInfo")
100
+
101
+ // Kiểm tra kiểu dữ liệu trước khi ép kiểu để tránh lỗi
102
+ val call = callInfo ?: mutableMapOf()
103
+
104
+ val timeStartToAnswer = (call["time_start_to_answer"] as? Long) ?: 0L
105
+ val timeEnd = (call["time_end"] as? Long) ?: 0L
106
+ val phoneNumber = (call["destination_number"] as? String) ?: (call["source_number"] as? String) ?: ""
107
+ val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber)
108
+
109
+ val map: WritableMap = WritableNativeMap().apply {
110
+ putString("direction", call["direction"] as? String ?: "")
111
+ putString("transactionId", call["transaction_id"] as? String ?: "")
112
+ putString("sourceNumber", call["source_number"] as? String ?: "")
113
+ putString("destinationNumber", call["destination_number"] as? String ?: "")
114
+ putDouble("timeStartToAnswer", timeStartToAnswer.toDouble())
115
+ putDouble("timeEnd", timeEnd.toDouble())
116
+ putString("sipUser", call["sip_user"] as? String ?: "")
117
+ putInt("codeEndCall", statusCode)
118
+ putString("disposition", call["disposition"] as? String ?: "")
119
+ putInt("status", CallState.disconnected.value)
120
+ putString("typeNumber", typeNumber)
121
+ }
122
+
123
+ Log.d("OMISDK RN", "=>> onCallEnd => $map")
124
+ sendEvent(CALL_STATE_CHANGED, map)
99
125
  }
100
126
 
101
127
  override fun onConnecting() {
102
- Log.d("OMISDK", "=>> ON CONNECTING CALL => ")
103
- val map: WritableMap = WritableNativeMap()
104
- map.putString("callerNumber", "")
105
- map.putBoolean("isVideo", NotificationService.isVideo)
106
- map.putBoolean("incoming", isIncomming)
107
- map.putString("transactionId", "")
108
- map.putString("_id", "")
109
- map.putInt("status", CallState.connecting.value)
110
- sendEvent(CALL_STATE_CHANGED, map)
128
+ Log.d("OMISDK", "=>> ON CONNECTING CALL => ")
129
+
130
+ val map: WritableMap = WritableNativeMap().apply {
131
+ putString("callerNumber", "")
132
+ putBoolean("isVideo", NotificationService.isVideo)
133
+ putBoolean("incoming", isIncoming ?: false)
134
+ putString("transactionId", "")
135
+ putString("_id", "")
136
+ putInt("status", CallState.connecting.value)
137
+ putString("typeNumber", "")
138
+ }
139
+
140
+ sendEvent(CALL_STATE_CHANGED, map)
111
141
  }
112
142
 
113
143
  override fun onDescriptionError() {
@@ -117,25 +147,21 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
117
147
  }
118
148
 
119
149
  override fun onRinging(callerId: Int, transactionId: String?) {
120
- var callDirection = OmiClient.callDirection
121
- val map: WritableMap = WritableNativeMap()
150
+ val callDirection = OmiClient.callDirection
151
+ val prePhoneNumber = OmiClient.prePhoneNumber ?: ""
152
+ val typeNumber = OmiKitUtils().checkTypeNumber(prePhoneNumber)
153
+
154
+ val map: WritableMap = WritableNativeMap().apply {
155
+ putString("callerNumber", if (callDirection == "inbound") prePhoneNumber else "")
156
+ putBoolean("isVideo", NotificationService.isVideo)
157
+ putString("transactionId", transactionId ?: "")
158
+ putInt("status", if (callDirection == "inbound") CallState.incoming.value else CallState.early.value)
159
+ putBoolean("incoming", callDirection == "inbound")
160
+ putString("typeNumber", typeNumber)
161
+ }
122
162
 
123
- if (callDirection == "inbound") {
124
- Log.d("OMISDK", "=>> ON IN COMMING CALL => ")
125
- map.putString("callerNumber", OmiClient.prePhoneNumber)
126
- map.putBoolean("isVideo", NotificationService.isVideo)
127
- map.putBoolean("incoming", true)
128
- map.putString("transactionId", transactionId ?: "")
129
- map.putInt("status", CallState.incoming.value)
130
- } else {
131
- map.putString("callerNumber", "")
132
- map.putBoolean("isVideo", NotificationService.isVideo)
133
- map.putString("transactionId", transactionId ?: "")
134
- map.putInt("status", CallState.early.value)
135
- map.putBoolean("incoming", false)
136
- Log.d("OMISDK", "=>> ON RINGING CALL => ")
137
- }
138
- sendEvent(CALL_STATE_CHANGED, map)
163
+ Log.d("OMISDK", if (callDirection == "inbound") "=>> ON INCOMING CALL => " else "=>> ON RINGING CALL => ")
164
+ sendEvent(CALL_STATE_CHANGED, map)
139
165
  }
140
166
 
141
167
 
@@ -166,15 +192,21 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
166
192
  }
167
193
 
168
194
  override fun onOutgoingStarted(callerId: Int, phoneNumber: String?, isVideo: Boolean?) {
169
- Log.d("OMISDK", "=>> ON OUT GOING STARTED CALL => ")
170
- val map: WritableMap = WritableNativeMap()
171
- map.putString("callerNumber", "")
172
- map.putBoolean("isVideo", NotificationService.isVideo)
173
- map.putString("transactionId", "")
174
- map.putInt("status", CallState.calling.value)
175
- map.putString("_id", "")
176
- map.putBoolean("incoming", isIncomming)
177
- sendEvent(CALL_STATE_CHANGED, map)
195
+ Log.d("OMISDK", "=>> ON OUTGOING STARTED CALL => ")
196
+
197
+ val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber ?: "")
198
+
199
+ val map: WritableMap = WritableNativeMap().apply {
200
+ putString("callerNumber", phoneNumber ?: "")
201
+ putBoolean("isVideo", NotificationService.isVideo)
202
+ putString("transactionId", "")
203
+ putInt("status", CallState.calling.value)
204
+ putString("_id", "")
205
+ putBoolean("incoming", isIncoming) // 🔹 Kiểm tra lỗi chính tả của biến này
206
+ putString("typeNumber", typeNumber)
207
+ }
208
+
209
+ sendEvent(CALL_STATE_CHANGED, map)
178
210
  }
179
211
 
180
212
  override fun onSwitchBoardAnswer(sip: String) {
@@ -187,13 +219,18 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
187
219
  Log.d("OMISDK", "=> ON REGISTER COMPLETED => status code: $statusCode")
188
220
 
189
221
  if (statusCode != 200) {
222
+ val normalizedStatusCode = if (statusCode == 403) 853 else statusCode
223
+ val typeNumber = ""
224
+
190
225
  val mapObject = WritableNativeMap().apply {
191
- putBoolean("isVideo", false)
192
- putBoolean("incoming", true)
193
- putString("callerNumber", "")
194
- putString("_id", "")
195
- putInt("status", 6)
196
- putInt("code_end_call", if (statusCode == 403) 853 else statusCode)
226
+ putBoolean("isVideo", false)
227
+ putBoolean("incoming", true)
228
+ putString("callerNumber", "")
229
+ putString("_id", "")
230
+ putInt("status", 6)
231
+ putInt("code_end_call", normalizedStatusCode)
232
+ putInt("codeEndCall", normalizedStatusCode)
233
+ putString("typeNumber", typeNumber)
197
234
  }
198
235
  sendEvent(CALL_STATE_CHANGED, mapObject)
199
236
  }
@@ -395,51 +432,68 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
395
432
  }
396
433
 
397
434
  @ReactMethod
398
- fun getInitialCall(counter: Int = 4, promise: Promise) {
399
- currentActivity?.runOnUiThread {
400
- if (reactApplicationContext != null) {
401
- val call = Utils.getActiveCall(reactApplicationContext!!)
402
- Log.d("getInitialCall RN", "getInitialCall abc $call")
403
- if (call == null) {
435
+ fun getInitialCall(counter: Int = 1, promise: Promise) {
436
+ val context = reactApplicationContext ?: run {
437
+ Log.e("getInitialCall", "❌ React context is null")
438
+ promise.resolve(false)
439
+ return
440
+ }
441
+
442
+ val call = Utils.getActiveCall(context)
443
+ Log.d("getInitialCall RN", "📞 Active call: $call")
444
+
445
+ if (call == null) {
404
446
  if (counter <= 0) {
405
- promise.resolve(false);
447
+ promise.resolve(false)
406
448
  } else {
407
- thread {
408
- Thread.sleep(5000) // Chờ 5 giây
409
- getInitialCall(counter - 1, promise); // Gọi lại hàm đệ quy
410
- }
411
- }
412
- } else {
413
- val phoneNumberTemp: String = call.remoteNumber as String
414
- if (phoneNumberTemp.isNotEmpty()) {
415
- val map: WritableMap = WritableNativeMap()
416
- map.putString("callerNumber", phoneNumberTemp)
417
- val statusPendingCall = OmiKitUtils().getStatusPendingCall(reactApplicationContext)
418
- if (call.state == 3) {
419
- if (statusPendingCall != 0) {
420
- call.state = statusPendingCall
449
+ mainScope.launch {
450
+ Log.d("getInitialCall RN", "🔄 Retrying in 2s... (Attempts left: $counter)")
451
+ delay(1000) // Chờ 2 giây
452
+ getInitialCall(counter - 1, promise) // Gọi lại hàm đệ quy
421
453
  }
422
- }
423
- map.putBoolean("incoming", call.direction == "inbound")
424
- map.putInt("_id", call.id)
425
- map.putInt("status", call.state)
426
- map.putBoolean("muted", false)
427
- map.putBoolean("isVideo", call.isVideo ?: false)
428
- promise.resolve(map)
429
- if (statusPendingCall == 2 && call.state != 5) {
430
- Log.d("getInitialCall RN", "incomingReceive $statusPendingCall")
431
- val map2: WritableMap = WritableNativeMap()
432
- map2.putBoolean("isVideo", call.isVideo ?: false)
433
- map2.putBoolean("incoming", true)
434
- map2.putString("callerNumber", phoneNumberTemp)
435
- map2.putString("_id", "")
436
- map2.putInt("status", 2)
437
- sendEvent(CALL_STATE_CHANGED, map2)
438
- }
439
454
  }
440
- }
455
+ return
456
+ }
457
+
458
+ val phoneNumber = call.remoteNumber as? String ?: ""
459
+ if (phoneNumber.isEmpty()) {
460
+ promise.resolve(false)
461
+ return
462
+ }
463
+
464
+ val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber ?: "")
465
+
466
+ val map: WritableMap = WritableNativeMap().apply {
467
+ putString("callerNumber", phoneNumber)
468
+ putBoolean("incoming", call.direction == "inbound")
469
+ putInt("_id", call.id)
470
+ putInt("status", call.state)
471
+ putBoolean("muted", false)
472
+ putBoolean("isVideo", call.isVideo ?: false)
473
+ putString("typeNumber", typeNumber)
474
+ }
475
+
476
+ val statusPendingCall = OmiKitUtils().getStatusPendingCall(context)
477
+ if (call.state == 3 && statusPendingCall != 0) {
478
+ call.state = statusPendingCall
479
+ }
480
+
481
+ promise.resolve(map)
482
+
483
+ if (statusPendingCall == 2 && call.state != 5) {
484
+ Log.d("getInitialCall RN", "🚀 Incoming Receive Triggered ($statusPendingCall)")
485
+
486
+
487
+ val eventMap: WritableMap = WritableNativeMap().apply {
488
+ putBoolean("isVideo", call.isVideo ?: false)
489
+ putBoolean("incoming", true)
490
+ putString("callerNumber", phoneNumber)
491
+ putString("_id", "")
492
+ putInt("status", 2)
493
+ putString("typeNumber", typeNumber)
494
+ }
495
+ sendEvent(CALL_STATE_CHANGED, eventMap)
441
496
  }
442
- }
443
497
  }
444
498
 
445
499
 
@@ -505,17 +559,32 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
505
559
 
506
560
  @ReactMethod
507
561
  fun joinCall(promise: Promise) {
508
- currentActivity?.runOnUiThread {
509
- if (reactApplicationContext != null) {
510
- OmiClient.getInstance(reactApplicationContext!!).pickUp()
511
- promise.resolve(true)
562
+ val appContext = reactApplicationContext.applicationContext
563
+ val activity = currentActivity
564
+
565
+ if (appContext == null) {
566
+ promise.reject("E_NULL_CONTEXT", "Application context is null")
567
+ return
568
+ }
569
+
570
+ if (activity == null) {
571
+ promise.reject("E_NULL_ACTIVITY", "Current activity is null")
572
+ return
573
+ }
574
+
575
+ activity.runOnUiThread {
576
+ try {
577
+ OmiClient.getInstance(appContext).pickUp()
578
+ promise.resolve(true)
579
+ } catch (e: Exception) {
580
+ promise.reject("E_JOIN_CALL_FAILED", "Failed to join call", e)
581
+ }
512
582
  }
513
- }
514
583
  }
515
584
 
516
585
  @ReactMethod
517
586
  fun endCall(promise: Promise) {
518
- if (isIncomming && !isAnserCall) {
587
+ if (isIncoming && !isAnserCall) {
519
588
  OmiClient.getInstance(reactApplicationContext!!).decline()
520
589
  } else {
521
590
  OmiClient.getInstance(reactApplicationContext!!).hangUp()
@@ -526,7 +595,7 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
526
595
 
527
596
  @ReactMethod
528
597
  fun rejectCall(promise: Promise) {
529
- if (isIncomming && !isAnserCall) {
598
+ if (isIncoming && !isAnserCall) {
530
599
  OmiClient.getInstance(reactApplicationContext!!).decline()
531
600
  }
532
601
  promise.resolve(true)
@@ -646,6 +715,10 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
646
715
  map.putString("uuid", call["uuid"] as String?)
647
716
  map.putString("full_name", call["full_name"] as String?)
648
717
  map.putString("avatar_url", call["avatar_url"] as String?)
718
+
719
+ map.putString("fullName", call["full_name"] as String?)
720
+ map.putString("avatarUrl", call["avatar_url"] as String?)
721
+
649
722
  promise.resolve(map)
650
723
  } else {
651
724
  promise.resolve(null);
@@ -671,6 +744,10 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
671
744
  map.putString("uuid", call["uuid"] as String?)
672
745
  map.putString("full_name", call["full_name"] as String?)
673
746
  map.putString("avatar_url", call["avatar_url"] as String?)
747
+
748
+ map.putString("fullName", call["full_name"] as String?)
749
+ map.putString("avatarUrl", call["avatar_url"] as String?)
750
+
674
751
  promise.resolve(map)
675
752
  } else {
676
753
  promise.resolve(null);
@@ -695,6 +772,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
695
772
  map.putString("uuid", call["uuid"] as String?)
696
773
  map.putString("full_name", call["full_name"] as String?)
697
774
  map.putString("avatar_url", call["avatar_url"] as String?)
775
+
776
+ map.putString("fullName", call["full_name"] as String?)
777
+ map.putString("avatarUrl", call["avatar_url"] as String?)
698
778
  promise.resolve(map)
699
779
  } else {
700
780
  promise.resolve(null)
@@ -806,18 +886,19 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
806
886
  }
807
887
  }
808
888
 
809
- fun sendEvent(eventName: String?, params: Any?) {
810
- if (eventName == null) {
811
- Log.e("OmikitPlugin", "eventName is null. Event cannot be emitted.")
812
- return
813
- }
814
- if (currentActivity != null) {
815
- currentActivity!!.runOnUiThread {
816
- reactApplicationContext.getJSModule(RCTNativeAppEventEmitter::class.java)
817
- .emit(eventName, params)
889
+ fun sendEvent(eventName: String?, params: Any?) {
890
+ if (eventName == null) {
891
+ Log.e("OmikitPlugin", "eventName is null or empty. Không thể gửi event.")
892
+ return
893
+ }
894
+ if (currentActivity != null) {
895
+ currentActivity!!.runOnUiThread {
896
+ reactApplicationContext.getJSModule(RCTNativeAppEventEmitter::class.java)
897
+ .emit(eventName, params)
898
+ Log.d("OmikitPlugin", "✅ Event $eventName đã được gửi thành công!")
899
+ }
818
900
  }
819
901
  }
820
- }
821
902
 
822
903
  private fun requestPermission(isVideo: Boolean) {
823
904
  var permissions = arrayOf(
@@ -38,4 +38,15 @@ class OmiKitUtils {
38
38
  clearStatusPendingCall(context)
39
39
  return status
40
40
  }
41
+
42
+ fun checkTypeNumber(phoneNumber: String?): String {
43
+ if (phoneNumber.isNullOrBlank()) return "" //
44
+
45
+ return when {
46
+ phoneNumber.length < 8 -> "internal"
47
+ phoneNumber.any { it.isLetter() } && phoneNumber.any { it.isDigit() } -> "zalo"
48
+ else -> "phone"
49
+ }
50
+ }
51
+
41
52
  }