omikit-plugin 3.2.81 → 3.2.84
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 +16 -1
- package/android/build.gradle +2 -1
- package/android/gradle.properties +0 -2
- package/android/src/main/java/com/omikitplugin/OmikitPluginModule.kt +564 -94
- package/ios/CallProcess/CallManager.swift +9 -0
- package/ios/OmikitPlugin-Bridging-Header.h +6 -0
- package/lib/commonjs/omikit.js +41 -15
- package/lib/commonjs/omikit.js.map +1 -1
- package/lib/module/omikit.js +38 -15
- package/lib/module/omikit.js.map +1 -1
- package/omikit-plugin.podspec +10 -6
- package/package.json +12 -3
- package/src/omikit.tsx +49 -25
- package/src/types/index.d.ts +54 -54
|
@@ -34,6 +34,8 @@ import vn.vihat.omicall.omisdk.utils.OmiSDKUtils
|
|
|
34
34
|
import vn.vihat.omicall.omisdk.utils.OmiStartCallStatus
|
|
35
35
|
import vn.vihat.omicall.omisdk.utils.SipServiceConstants
|
|
36
36
|
import vn.vihat.omicall.omisdk.utils.Utils
|
|
37
|
+
import java.util.Timer
|
|
38
|
+
import java.util.TimerTask
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
@@ -51,7 +53,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
51
53
|
|
|
52
54
|
override fun incomingReceived(callerId: Int?, phoneNumber: String?, isVideo: Boolean?) {
|
|
53
55
|
isIncoming = true;
|
|
54
|
-
Log.d("OMISDK", "=>> START INCOMING CALL REVICED => ")
|
|
55
56
|
|
|
56
57
|
val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber ?: "")
|
|
57
58
|
|
|
@@ -82,15 +83,17 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
82
83
|
|
|
83
84
|
val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber ?: "")
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
86
|
+
// ✅ Sử dụng safe WritableMap creation
|
|
87
|
+
val eventData = mapOf(
|
|
88
|
+
"callerNumber" to (phoneNumber ?: ""),
|
|
89
|
+
"isVideo" to (isVideo ?: true),
|
|
90
|
+
"incoming" to isIncoming,
|
|
91
|
+
"transactionId" to (transactionId ?: ""),
|
|
92
|
+
"status" to CallState.confirmed.value,
|
|
93
|
+
"typeNumber" to typeNumber
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
val map = createSafeWritableMap(eventData)
|
|
94
97
|
sendEvent(CALL_STATE_CHANGED, map)
|
|
95
98
|
}, 200)
|
|
96
99
|
}
|
|
@@ -106,21 +109,24 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
106
109
|
val phoneNumber = (call["destination_number"] as? String) ?: (call["source_number"] as? String) ?: ""
|
|
107
110
|
val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber)
|
|
108
111
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
112
|
+
// ✅ Sử dụng safe WritableMap creation
|
|
113
|
+
val eventData = mapOf(
|
|
114
|
+
"direction" to (call["direction"] as? String ?: ""),
|
|
115
|
+
"transactionId" to (call["transaction_id"] as? String ?: ""),
|
|
116
|
+
"sourceNumber" to (call["source_number"] as? String ?: ""),
|
|
117
|
+
"destinationNumber" to (call["destination_number"] as? String ?: ""),
|
|
118
|
+
"timeStartToAnswer" to timeStartToAnswer.toDouble(),
|
|
119
|
+
"timeEnd" to timeEnd.toDouble(),
|
|
120
|
+
"sipUser" to (call["sip_user"] as? String ?: ""),
|
|
121
|
+
"codeEndCall" to statusCode,
|
|
122
|
+
"disposition" to (call["disposition"] as? String ?: ""),
|
|
123
|
+
"status" to CallState.disconnected.value,
|
|
124
|
+
"typeNumber" to typeNumber
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
val map = createSafeWritableMap(eventData)
|
|
122
128
|
|
|
123
|
-
Log.d("OMISDK RN", "=>> onCallEnd =>
|
|
129
|
+
Log.d("OMISDK RN", "=>> onCallEnd => ")
|
|
124
130
|
sendEvent(CALL_STATE_CHANGED, map)
|
|
125
131
|
}
|
|
126
132
|
|
|
@@ -151,14 +157,17 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
151
157
|
val prePhoneNumber = OmiClient.prePhoneNumber ?: ""
|
|
152
158
|
val typeNumber = OmiKitUtils().checkTypeNumber(prePhoneNumber)
|
|
153
159
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
// ✅ Sử dụng safe WritableMap creation
|
|
161
|
+
val eventData = mapOf(
|
|
162
|
+
"callerNumber" to if (callDirection == "inbound") prePhoneNumber else "",
|
|
163
|
+
"isVideo" to NotificationService.isVideo,
|
|
164
|
+
"transactionId" to (transactionId ?: ""),
|
|
165
|
+
"status" to if (callDirection == "inbound") CallState.incoming.value else CallState.early.value,
|
|
166
|
+
"incoming" to (callDirection == "inbound"),
|
|
167
|
+
"typeNumber" to typeNumber
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
val map = createSafeWritableMap(eventData)
|
|
162
171
|
|
|
163
172
|
Log.d("OMISDK", if (callDirection == "inbound") "=>> ON INCOMING CALL => " else "=>> ON RINGING CALL => ")
|
|
164
173
|
sendEvent(CALL_STATE_CHANGED, map)
|
|
@@ -288,17 +297,173 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
288
297
|
|
|
289
298
|
override fun initialize() {
|
|
290
299
|
super.initialize()
|
|
300
|
+
|
|
301
|
+
|
|
291
302
|
reactApplicationContext!!.addActivityEventListener(this)
|
|
292
303
|
Handler(Looper.getMainLooper()).post {
|
|
293
304
|
OmiClient.getInstance(reactApplicationContext!!).addCallStateListener(this)
|
|
305
|
+
|
|
306
|
+
// ✅ Add listener cho AUTO-UNREGISTER status
|
|
307
|
+
OmiClient.getInstance(reactApplicationContext!!).addCallStateListener(autoUnregisterListener)
|
|
308
|
+
|
|
294
309
|
OmiClient.getInstance(reactApplicationContext!!).setDebug(false)
|
|
295
310
|
}
|
|
296
311
|
}
|
|
297
312
|
|
|
313
|
+
|
|
298
314
|
@ReactMethod
|
|
299
315
|
fun startServices(promise: Promise) {
|
|
300
|
-
|
|
301
|
-
|
|
316
|
+
try {
|
|
317
|
+
// ✅ Prepare audio system trước khi start services
|
|
318
|
+
prepareAudioSystem()
|
|
319
|
+
|
|
320
|
+
OmiClient.getInstance(reactApplicationContext!!).addAccountListener(accountListener)
|
|
321
|
+
|
|
322
|
+
// ✅ Start services - không cần prevent auto-unregister với Silent API
|
|
323
|
+
OmiClient.getInstance(reactApplicationContext!!).setDebug(false)
|
|
324
|
+
promise.resolve(true)
|
|
325
|
+
} catch (e: Exception) {
|
|
326
|
+
Log.e("OmikitPlugin", "❌ Error in startServices: ${e.message}", e)
|
|
327
|
+
promise.resolve(false)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ✅ Helper function để sử dụng API mới (DEPRECATED - sử dụng Silent API thay thế)
|
|
332
|
+
private fun preventAutoUnregisterCrash(reason: String) {
|
|
333
|
+
try {
|
|
334
|
+
Log.w("OmikitPlugin", "⚠️ DEPRECATED: preventAutoUnregisterCrash() - Use Silent Registration API instead")
|
|
335
|
+
OmiClient.getInstance(reactApplicationContext!!).preventAutoUnregister(reason)
|
|
336
|
+
} catch (e: Exception) {
|
|
337
|
+
Log.e("OmikitPlugin", "❌ Failed to prevent AUTO-UNREGISTER: ${e.message}", e)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ✅ Method để check status AUTO-UNREGISTER (DEPRECATED)
|
|
342
|
+
@ReactMethod
|
|
343
|
+
fun getAutoUnregisterStatus(promise: Promise) {
|
|
344
|
+
Log.w("OmikitPlugin", "⚠️ DEPRECATED: getAutoUnregisterStatus() - Use Silent Registration API instead")
|
|
345
|
+
try {
|
|
346
|
+
OmiClient.getInstance(reactApplicationContext!!).getAutoUnregisterStatus { isScheduled, timeUntilExecution ->
|
|
347
|
+
try {
|
|
348
|
+
val status = mapOf(
|
|
349
|
+
"isScheduled" to isScheduled,
|
|
350
|
+
"timeUntilExecution" to timeUntilExecution,
|
|
351
|
+
"timestamp" to System.currentTimeMillis()
|
|
352
|
+
)
|
|
353
|
+
promise.resolve(Arguments.makeNativeMap(status))
|
|
354
|
+
} catch (e: Exception) {
|
|
355
|
+
Log.e("OmikitPlugin", "❌ Error in getAutoUnregisterStatus callback: ${e.message}", e)
|
|
356
|
+
promise.resolve(null)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
} catch (e: Exception) {
|
|
360
|
+
Log.e("OmikitPlugin", "❌ Error calling getAutoUnregisterStatus: ${e.message}", e)
|
|
361
|
+
promise.resolve(null)
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// ✅ Method để manually prevent AUTO-UNREGISTER (DEPRECATED)
|
|
366
|
+
@ReactMethod
|
|
367
|
+
fun preventAutoUnregister(reason: String, promise: Promise) {
|
|
368
|
+
Log.w("OmikitPlugin", "⚠️ DEPRECATED: preventAutoUnregister() - Use Silent Registration API instead")
|
|
369
|
+
try {
|
|
370
|
+
preventAutoUnregisterCrash(reason)
|
|
371
|
+
promise.resolve(true)
|
|
372
|
+
} catch (e: Exception) {
|
|
373
|
+
Log.e("OmikitPlugin", "❌ Manual prevent failed: ${e.message}", e)
|
|
374
|
+
promise.resolve(false)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ✅ Convenience methods cho các scenario phổ biến (DEPRECATED)
|
|
379
|
+
@ReactMethod
|
|
380
|
+
fun prepareForIncomingCall(promise: Promise) {
|
|
381
|
+
Log.w("OmikitPlugin", "⚠️ DEPRECATED: prepareForIncomingCall() - Use Silent Registration API instead")
|
|
382
|
+
try {
|
|
383
|
+
OmiClient.getInstance(reactApplicationContext!!).prepareForIncomingCall()
|
|
384
|
+
promise.resolve(true)
|
|
385
|
+
} catch (e: Exception) {
|
|
386
|
+
Log.e("OmikitPlugin", "❌ Prepare for incoming call failed: ${e.message}", e)
|
|
387
|
+
promise.resolve(false)
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
@ReactMethod
|
|
392
|
+
fun prepareForOutgoingCall(promise: Promise) {
|
|
393
|
+
Log.w("OmikitPlugin", "⚠️ DEPRECATED: prepareForOutgoingCall() - Use Silent Registration API instead")
|
|
394
|
+
try {
|
|
395
|
+
OmiClient.getInstance(reactApplicationContext!!).prepareForOutgoingCall()
|
|
396
|
+
promise.resolve(true)
|
|
397
|
+
} catch (e: Exception) {
|
|
398
|
+
Log.e("OmikitPlugin", "❌ Prepare for outgoing call failed: ${e.message}", e)
|
|
399
|
+
promise.resolve(false)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private fun prepareAudioSystem() {
|
|
404
|
+
try {
|
|
405
|
+
// ✅ Check network connectivity first
|
|
406
|
+
if (!isNetworkAvailable()) {
|
|
407
|
+
return
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Release any existing audio focus
|
|
411
|
+
val audioManager = reactApplicationContext?.getSystemService(android.content.Context.AUDIO_SERVICE) as? android.media.AudioManager
|
|
412
|
+
audioManager?.let {
|
|
413
|
+
// Reset audio mode
|
|
414
|
+
it.mode = android.media.AudioManager.MODE_NORMAL
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Small delay để audio system ổn định
|
|
418
|
+
Thread.sleep(200)
|
|
419
|
+
|
|
420
|
+
} catch (e: Exception) {
|
|
421
|
+
Log.w("OmikitPlugin", "⚠️ Audio preparation warning: ${e.message}")
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private fun isNetworkAvailable(): Boolean {
|
|
426
|
+
return try {
|
|
427
|
+
val connectivityManager = reactApplicationContext?.getSystemService(android.content.Context.CONNECTIVITY_SERVICE) as? android.net.ConnectivityManager
|
|
428
|
+
val activeNetwork = connectivityManager?.activeNetworkInfo
|
|
429
|
+
val isConnected = activeNetwork?.isConnectedOrConnecting == true
|
|
430
|
+
isConnected
|
|
431
|
+
} catch (e: Exception) {
|
|
432
|
+
Log.w("OmikitPlugin", "⚠️ Network check failed: ${e.message}")
|
|
433
|
+
true // Assume network is available if check fails
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// ✅ Safe wrapper cho OMISIP calls để tránh SIGSEGV
|
|
438
|
+
private fun <T> safePjsipCall(operation: String, block: () -> T): T? {
|
|
439
|
+
return try {
|
|
440
|
+
val result = block()
|
|
441
|
+
result
|
|
442
|
+
} catch (e: Exception) {
|
|
443
|
+
Log.e("OmikitPlugin", "❌ Safe OMISIP call failed: $operation - ${e.message}", e)
|
|
444
|
+
null
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ✅ Helper function để tạo WritableMap an toàn
|
|
449
|
+
private fun createSafeWritableMap(data: Map<String, Any?>): WritableMap {
|
|
450
|
+
val map = WritableNativeMap()
|
|
451
|
+
try {
|
|
452
|
+
data.forEach { (key, value) ->
|
|
453
|
+
when (value) {
|
|
454
|
+
is String -> map.putString(key, value)
|
|
455
|
+
is Int -> map.putInt(key, value)
|
|
456
|
+
is Double -> map.putDouble(key, value)
|
|
457
|
+
is Boolean -> map.putBoolean(key, value)
|
|
458
|
+
is Long -> map.putDouble(key, value.toDouble())
|
|
459
|
+
null -> map.putNull(key)
|
|
460
|
+
else -> map.putString(key, value.toString())
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
} catch (e: Exception) {
|
|
464
|
+
Log.e("OmikitPlugin", "❌ Error creating WritableMap: ${e.message}", e)
|
|
465
|
+
}
|
|
466
|
+
return map
|
|
302
467
|
}
|
|
303
468
|
|
|
304
469
|
@RequiresApi(Build.VERSION_CODES.M)
|
|
@@ -365,7 +530,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
365
530
|
@ReactMethod
|
|
366
531
|
fun initCallWithUserPassword(data: ReadableMap, promise: Promise) {
|
|
367
532
|
mainScope.launch {
|
|
368
|
-
var loginResult = false
|
|
369
533
|
val userName = data.getString("userName")
|
|
370
534
|
val password = data.getString("password")
|
|
371
535
|
val realm = data.getString("realm")
|
|
@@ -374,26 +538,52 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
374
538
|
val firebaseToken = data.getString("fcmToken")
|
|
375
539
|
val projectId = data.getString("projectId") ?: ""
|
|
376
540
|
|
|
541
|
+
// Validate required parameters
|
|
542
|
+
if (userName.isNullOrEmpty() || password.isNullOrEmpty() || realm.isNullOrEmpty() || firebaseToken.isNullOrEmpty()) {
|
|
543
|
+
Log.e("OmikitPlugin", "❌ Missing required parameters for SIP registration")
|
|
544
|
+
promise.resolve(false)
|
|
545
|
+
return@launch
|
|
546
|
+
}
|
|
547
|
+
|
|
377
548
|
withContext(Dispatchers.Default) {
|
|
378
549
|
try {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
isVideo ?: true,
|
|
386
|
-
firebaseToken,
|
|
387
|
-
host,
|
|
388
|
-
projectId
|
|
389
|
-
)
|
|
390
|
-
promise.resolve(loginResult)
|
|
550
|
+
// ✅ Cleanup trước khi register
|
|
551
|
+
try {
|
|
552
|
+
OmiClient.getInstance(reactApplicationContext!!).logout()
|
|
553
|
+
delay(500) // Chờ cleanup hoàn tất
|
|
554
|
+
} catch (e: Exception) {
|
|
555
|
+
Log.w("OmikitPlugin", "⚠️ Cleanup warning (expected): ${e.message}")
|
|
391
556
|
}
|
|
392
|
-
|
|
393
|
-
|
|
557
|
+
|
|
558
|
+
// ✅ Sử dụng Silent Registration API mới từ OmiSDK 2.3.67
|
|
559
|
+
Log.d("OmikitPlugin", "🔇 Using Silent Registration API for user: $userName")
|
|
560
|
+
|
|
561
|
+
OmiClient.getInstance(reactApplicationContext!!).silentRegister(
|
|
562
|
+
userName = userName,
|
|
563
|
+
password = password,
|
|
564
|
+
realm = realm,
|
|
565
|
+
isVideo = isVideo ?: true,
|
|
566
|
+
firebaseToken = firebaseToken,
|
|
567
|
+
host = host,
|
|
568
|
+
projectId = projectId
|
|
569
|
+
) { success, statusCode, message ->
|
|
570
|
+
Log.d("OmikitPlugin", "🔇 Silent registration callback - success: $success, status: $statusCode, message: $message")
|
|
571
|
+
|
|
572
|
+
if (success) {
|
|
573
|
+
Log.d("OmikitPlugin", "✅ Silent registration successful - no notification, no auto-unregister")
|
|
574
|
+
} else {
|
|
575
|
+
Log.e("OmikitPlugin", "❌ Silent registration failed: $message")
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// ✅ Resolve promise với kết quả từ callback
|
|
579
|
+
promise.resolve(success)
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
} catch (e: Exception) {
|
|
583
|
+
Log.e("OmikitPlugin", "❌ Error during silent registration: ${e.message}", e)
|
|
584
|
+
promise.resolve(false)
|
|
394
585
|
}
|
|
395
586
|
}
|
|
396
|
-
promise.resolve(loginResult)
|
|
397
587
|
}
|
|
398
588
|
}
|
|
399
589
|
|
|
@@ -422,6 +612,13 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
422
612
|
firebaseToken,
|
|
423
613
|
projectId
|
|
424
614
|
)
|
|
615
|
+
|
|
616
|
+
// ✅ Sử dụng API mới để ngăn chặn AUTO-UNREGISTER sau khi register thành công
|
|
617
|
+
if (loginResult) {
|
|
618
|
+
Log.d("OmikitPlugin", "🛡️ Preventing AUTO-UNREGISTER after successful API key registration")
|
|
619
|
+
preventAutoUnregisterCrash("Successful API key registration - userName: $usrName")
|
|
620
|
+
}
|
|
621
|
+
|
|
425
622
|
promise.resolve(true)
|
|
426
623
|
}
|
|
427
624
|
} catch (_: Throwable) {
|
|
@@ -509,9 +706,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
509
706
|
currentActivity?.runOnUiThread {
|
|
510
707
|
val phoneNumber = data.getString("phoneNumber") as String
|
|
511
708
|
val isVideo = data.getBoolean("isVideo") ?: false;
|
|
709
|
+
|
|
512
710
|
val startCallResult =
|
|
513
711
|
OmiClient.getInstance(reactApplicationContext!!).startCall(phoneNumber, isVideo)
|
|
514
|
-
Log.d("OMISDK", "=>> startCallResult START CALL => $startCallResult")
|
|
515
712
|
var statusCalltemp = startCallResult.value as Int;
|
|
516
713
|
if (startCallResult.value == 200 || startCallResult.value == 407) {
|
|
517
714
|
statusCalltemp = 8
|
|
@@ -525,7 +722,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
525
722
|
map.putInt("status", 4)
|
|
526
723
|
map.putString("_id", "")
|
|
527
724
|
map.putString("message", messageCall(406) as String)
|
|
528
|
-
Log.d("OMISDK", "=>> ON START CALL FAIL BECAUSE NEED PERMISSION => $map")
|
|
529
725
|
promise.resolve(map)
|
|
530
726
|
}
|
|
531
727
|
}
|
|
@@ -544,6 +740,7 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
544
740
|
try {
|
|
545
741
|
val uuid = data.getString("usrUuid") as String
|
|
546
742
|
val isVideo = data.getBoolean("isVideo")
|
|
743
|
+
|
|
547
744
|
callResult = OmiClient.getInstance(reactApplicationContext!!)
|
|
548
745
|
.startCallWithUuid(uuid = uuid, isVideo = isVideo)
|
|
549
746
|
} catch (_: Throwable) {
|
|
@@ -703,9 +900,10 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
703
900
|
var callResult: Any? = null
|
|
704
901
|
withContext(Dispatchers.Default) {
|
|
705
902
|
try {
|
|
903
|
+
// ✅ Gọi trực tiếp getCurrentUser() trong coroutine context
|
|
706
904
|
callResult = OmiClient.getInstance(reactApplicationContext!!).getCurrentUser()
|
|
707
|
-
} catch (
|
|
708
|
-
|
|
905
|
+
} catch (e: Throwable) {
|
|
906
|
+
Log.e("OmikitPlugin", "❌ getCurrentUser error: ${e.message}", e)
|
|
709
907
|
}
|
|
710
908
|
}
|
|
711
909
|
if (callResult != null && callResult is Map<*, *>) {
|
|
@@ -830,7 +1028,11 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
830
1028
|
const val NAME = "OmikitPlugin"
|
|
831
1029
|
|
|
832
1030
|
fun onDestroy() {
|
|
833
|
-
|
|
1031
|
+
try {
|
|
1032
|
+
// Cleanup OmiClient resources safely
|
|
1033
|
+
} catch (e: Exception) {
|
|
1034
|
+
Log.e("OmikitPlugin", "❌ Error during cleanup: ${e.message}", e)
|
|
1035
|
+
}
|
|
834
1036
|
}
|
|
835
1037
|
|
|
836
1038
|
fun onResume(act: ReactActivity) {
|
|
@@ -886,19 +1088,61 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
886
1088
|
}
|
|
887
1089
|
}
|
|
888
1090
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1091
|
+
// ✅ Di chuyển sendEvent vào trong class để có thể access reactApplicationContext
|
|
1092
|
+
private fun sendEvent(eventName: String?, params: Any?) {
|
|
1093
|
+
if (eventName == null) {
|
|
1094
|
+
Log.e("OmikitPlugin", "❌ eventName is null or empty. Không thể gửi event.")
|
|
1095
|
+
return
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
try {
|
|
1099
|
+
// ✅ Kiểm tra reactApplicationContext
|
|
1100
|
+
if (reactApplicationContext == null) {
|
|
1101
|
+
Log.e("OmikitPlugin", "❌ reactApplicationContext is null")
|
|
1102
|
+
return
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
if (!reactApplicationContext.hasActiveReactInstance()) {
|
|
1106
|
+
Log.w("OmikitPlugin", "⚠️ ReactApplicationContext không có active React instance")
|
|
892
1107
|
return
|
|
893
1108
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1109
|
+
|
|
1110
|
+
// ✅ Sử dụng RCTDeviceEventEmitter cho Android (tương thích với DeviceEventEmitter)
|
|
1111
|
+
try {
|
|
1112
|
+
reactApplicationContext
|
|
1113
|
+
.getJSModule(com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
1114
|
+
.emit(eventName, params)
|
|
1115
|
+
} catch (e1: Exception) {
|
|
1116
|
+
// ✅ Fallback to RCTNativeAppEventEmitter
|
|
1117
|
+
try {
|
|
1118
|
+
reactApplicationContext
|
|
1119
|
+
.getJSModule(com.facebook.react.modules.core.RCTNativeAppEventEmitter::class.java)
|
|
897
1120
|
.emit(eventName, params)
|
|
898
|
-
|
|
1121
|
+
} catch (e2: Exception) {
|
|
1122
|
+
Log.e("OmikitPlugin", "❌ Both event emitters failed: RCTDevice: ${e1.message}, RCTNativeApp: ${e2.message}")
|
|
899
1123
|
}
|
|
900
1124
|
}
|
|
1125
|
+
|
|
1126
|
+
} catch (e: Exception) {
|
|
1127
|
+
Log.e("OmikitPlugin", "❌ Error sending event $eventName: ${e.message}", e)
|
|
901
1128
|
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// ✅ Thêm method để React Native biết các event được hỗ trợ
|
|
1132
|
+
override fun getConstants(): MutableMap<String, Any> {
|
|
1133
|
+
return hashMapOf(
|
|
1134
|
+
"CALL_STATE_CHANGED" to CALL_STATE_CHANGED,
|
|
1135
|
+
"MUTED" to MUTED,
|
|
1136
|
+
"HOLD" to HOLD,
|
|
1137
|
+
"SPEAKER" to SPEAKER,
|
|
1138
|
+
"CALL_QUALITY" to CALL_QUALITY,
|
|
1139
|
+
"AUDIO_CHANGE" to AUDIO_CHANGE,
|
|
1140
|
+
"SWITCHBOARD_ANSWER" to SWITCHBOARD_ANSWER,
|
|
1141
|
+
"REQUEST_PERMISSION" to REQUEST_PERMISSION,
|
|
1142
|
+
"CLICK_MISSED_CALL" to CLICK_MISSED_CALL,
|
|
1143
|
+
"AUTO_UNREGISTER_STATUS" to "AUTO_UNREGISTER_STATUS"
|
|
1144
|
+
)
|
|
1145
|
+
}
|
|
902
1146
|
|
|
903
1147
|
private fun requestPermission(isVideo: Boolean) {
|
|
904
1148
|
var permissions = arrayOf(
|
|
@@ -920,38 +1164,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
920
1164
|
)
|
|
921
1165
|
}
|
|
922
1166
|
|
|
923
|
-
// private fun getPending(): {
|
|
924
|
-
// val pendingCall = OmiKitUtils().getPendingCall(reactApplicationContext)
|
|
925
|
-
// OmiKitUtils().clearPendingCall(reactApplicationContext)
|
|
926
|
-
// val isPending =
|
|
927
|
-
// pendingCall[PREFS_IS_PENDING] as Boolean
|
|
928
|
-
// val receiveTime = pendingCall[RECEIVE_TIME] as Long
|
|
929
|
-
//
|
|
930
|
-
// if ( isPending && System.currentTimeMillis() - receiveTime < 25000) {
|
|
931
|
-
// val callId = pendingCall[PREFS_CALL_ID] as Int
|
|
932
|
-
// val phoneNumber = pendingCall[PREFS_PHONE_NUMBER] as String
|
|
933
|
-
// val isVideo = pendingCall[PREFS_IS_VIDEO] as Boolean
|
|
934
|
-
// val startTime = pendingCall[PREFS_START_TIME] as Long
|
|
935
|
-
// val uuid = pendingCall[PREFS_UUID] as String
|
|
936
|
-
// val isReopen = pendingCall[PREFS_IS_REOPEN] as Boolean
|
|
937
|
-
// val isAccepted = pendingCall[PREFS_IS_ACCEPTED] as Boolean
|
|
938
|
-
//
|
|
939
|
-
// if (isReopen) {
|
|
940
|
-
// onCallEstablished(
|
|
941
|
-
// callId, phoneNumber, isVideo, startTime,
|
|
942
|
-
// uuid
|
|
943
|
-
// )
|
|
944
|
-
// } else if (isAccepted) {
|
|
945
|
-
// OmiClient.getInstance(reactApplicationContext!!).pickUp()
|
|
946
|
-
// onCallEstablished(
|
|
947
|
-
// callId, phoneNumber, isVideo, startTime,
|
|
948
|
-
// uuid
|
|
949
|
-
// )
|
|
950
|
-
// } else {
|
|
951
|
-
// incomingReceived(callId, phoneNumber, isVideo)
|
|
952
|
-
// }
|
|
953
|
-
// }
|
|
954
|
-
// }
|
|
955
1167
|
override fun onActivityResult(p0: Activity?, p1: Int, p2: Int, p3: Intent?) {
|
|
956
1168
|
}
|
|
957
1169
|
|
|
@@ -967,4 +1179,262 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
967
1179
|
}
|
|
968
1180
|
}
|
|
969
1181
|
}
|
|
1182
|
+
|
|
1183
|
+
override fun onCatalystInstanceDestroy() {
|
|
1184
|
+
super.onCatalystInstanceDestroy()
|
|
1185
|
+
try {
|
|
1186
|
+
// ✅ Cleanup resources
|
|
1187
|
+
} catch (e: Exception) {
|
|
1188
|
+
Log.e("OmikitPlugin", "❌ Error during module cleanup: ${e.message}", e)
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// ✅ Thêm listener cho AUTO-UNREGISTER status
|
|
1193
|
+
private val autoUnregisterListener = object : OmiListener {
|
|
1194
|
+
override fun onAutoUnregisterStatus(isScheduled: Boolean, timeUntilExecution: Long) {
|
|
1195
|
+
// ✅ Ngăn chặn nếu sắp thực hiện (< 3 giây)
|
|
1196
|
+
if (isScheduled && timeUntilExecution > 0 && timeUntilExecution < 3000) {
|
|
1197
|
+
Log.w("OmikitPlugin", "🚨 AUTO-UNREGISTER sắp thực hiện trong ${timeUntilExecution}ms - ngăn chặn khẩn cấp!")
|
|
1198
|
+
preventAutoUnregisterCrash("Emergency prevention from listener - ${timeUntilExecution}ms remaining")
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// ✅ Gửi event cho React Native
|
|
1202
|
+
try {
|
|
1203
|
+
val statusData = mapOf(
|
|
1204
|
+
"isScheduled" to isScheduled,
|
|
1205
|
+
"timeUntilExecution" to timeUntilExecution,
|
|
1206
|
+
"timestamp" to System.currentTimeMillis()
|
|
1207
|
+
)
|
|
1208
|
+
val map = createSafeWritableMap(statusData)
|
|
1209
|
+
sendEvent("AUTO_UNREGISTER_STATUS", map)
|
|
1210
|
+
} catch (e: Exception) {
|
|
1211
|
+
Log.e("OmikitPlugin", "❌ Error sending AUTO_UNREGISTER_STATUS event: ${e.message}", e)
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// ✅ Implement các method khác của OmiListener (delegate to main listener)
|
|
1216
|
+
override fun incomingReceived(callerId: Int?, phoneNumber: String?, isVideo: Boolean?) {
|
|
1217
|
+
this@OmikitPluginModule.incomingReceived(callerId, phoneNumber, isVideo)
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
override fun onCallEstablished(callerId: Int, phoneNumber: String?, isVideo: Boolean?, startTime: Long, transactionId: String?) {
|
|
1221
|
+
this@OmikitPluginModule.onCallEstablished(callerId, phoneNumber, isVideo, startTime, transactionId)
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
override fun onCallEnd(callInfo: MutableMap<String, Any?>, statusCode: Int) {
|
|
1225
|
+
this@OmikitPluginModule.onCallEnd(callInfo, statusCode)
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
override fun onConnecting() {
|
|
1229
|
+
this@OmikitPluginModule.onConnecting()
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
override fun onDescriptionError() {
|
|
1233
|
+
this@OmikitPluginModule.onDescriptionError()
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
override fun onFcmReceived(uuid: String, userName: String, avatar: String) {
|
|
1237
|
+
this@OmikitPluginModule.onFcmReceived(uuid, userName, avatar)
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
override fun onRinging(callerId: Int, transactionId: String?) {
|
|
1241
|
+
this@OmikitPluginModule.onRinging(callerId, transactionId)
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
override fun networkHealth(stat: Map<String, *>, quality: Int) {
|
|
1245
|
+
this@OmikitPluginModule.networkHealth(stat, quality)
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
override fun onAudioChanged(audioInfo: Map<String, Any>) {
|
|
1249
|
+
this@OmikitPluginModule.onAudioChanged(audioInfo)
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
override fun onHold(isHold: Boolean) {
|
|
1253
|
+
this@OmikitPluginModule.onHold(isHold)
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
override fun onMuted(isMuted: Boolean) {
|
|
1257
|
+
this@OmikitPluginModule.onMuted(isMuted)
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
override fun onOutgoingStarted(callerId: Int, phoneNumber: String?, isVideo: Boolean?) {
|
|
1261
|
+
this@OmikitPluginModule.onOutgoingStarted(callerId, phoneNumber, isVideo)
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
override fun onSwitchBoardAnswer(sip: String) {
|
|
1265
|
+
this@OmikitPluginModule.onSwitchBoardAnswer(sip)
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
override fun onRegisterCompleted(statusCode: Int) {
|
|
1269
|
+
this@OmikitPluginModule.onRegisterCompleted(statusCode)
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
override fun onRequestPermission(permissions: Array<String>) {
|
|
1273
|
+
this@OmikitPluginModule.onRequestPermission(permissions)
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
override fun onVideoSize(width: Int, height: Int) {
|
|
1277
|
+
this@OmikitPluginModule.onVideoSize(width, height)
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// ✅ Helper function để hide notification một cách an toàn
|
|
1282
|
+
@ReactMethod
|
|
1283
|
+
fun hideSystemNotificationSafely(promise: Promise) {
|
|
1284
|
+
try {
|
|
1285
|
+
// ✅ Delay 2 giây để đảm bảo registration hoàn tất
|
|
1286
|
+
Handler(Looper.getMainLooper()).postDelayed({
|
|
1287
|
+
try {
|
|
1288
|
+
// ✅ Gọi function hide notification với error handling
|
|
1289
|
+
OmiClient.getInstance(reactApplicationContext!!).hideSystemNotificationAndUnregister("Registration check completed")
|
|
1290
|
+
Log.d("OmikitPlugin", "✅ Successfully hidden system notification and unregistered")
|
|
1291
|
+
promise.resolve(true)
|
|
1292
|
+
} catch (e: Exception) {
|
|
1293
|
+
Log.e("OmikitPlugin", "❌ Failed to hide system notification: ${e.message}", e)
|
|
1294
|
+
promise.resolve(false)
|
|
1295
|
+
}
|
|
1296
|
+
}, 2000) // Delay 2 giây
|
|
1297
|
+
} catch (e: Exception) {
|
|
1298
|
+
Log.e("OmikitPlugin", "❌ Error in hideSystemNotificationSafely: ${e.message}", e)
|
|
1299
|
+
promise.resolve(false)
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// ✅ Function để chỉ ẩn notification mà không unregister
|
|
1304
|
+
@ReactMethod
|
|
1305
|
+
fun hideSystemNotificationOnly(promise: Promise) {
|
|
1306
|
+
try {
|
|
1307
|
+
OmiClient.getInstance(reactApplicationContext!!).hideSystemNotification()
|
|
1308
|
+
Log.d("OmikitPlugin", "✅ Successfully hidden system notification (keeping registration)")
|
|
1309
|
+
promise.resolve(true)
|
|
1310
|
+
} catch (e: Exception) {
|
|
1311
|
+
Log.e("OmikitPlugin", "❌ Failed to hide system notification only: ${e.message}", e)
|
|
1312
|
+
promise.resolve(false)
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// ✅ Function để ẩn notification và unregister với custom reason
|
|
1317
|
+
@ReactMethod
|
|
1318
|
+
fun hideSystemNotificationAndUnregister(reason: String, promise: Promise) {
|
|
1319
|
+
try {
|
|
1320
|
+
OmiClient.getInstance(reactApplicationContext!!).hideSystemNotificationAndUnregister(reason)
|
|
1321
|
+
Log.d("OmikitPlugin", "✅ Successfully hidden notification and unregistered: $reason")
|
|
1322
|
+
promise.resolve(true)
|
|
1323
|
+
} catch (e: Exception) {
|
|
1324
|
+
Log.e("OmikitPlugin", "❌ Failed to hide notification and unregister: ${e.message}", e)
|
|
1325
|
+
promise.resolve(false)
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// ✅ Function để chỉ kiểm tra credentials và tự động disconnect
|
|
1330
|
+
@ReactMethod
|
|
1331
|
+
fun checkCredentials(data: ReadableMap, promise: Promise) {
|
|
1332
|
+
mainScope.launch {
|
|
1333
|
+
val userName = data.getString("userName")
|
|
1334
|
+
val password = data.getString("password")
|
|
1335
|
+
val realm = data.getString("realm")
|
|
1336
|
+
val host = data.getString("host") ?: "vh.omicrm.com"
|
|
1337
|
+
val firebaseToken = data.getString("fcmToken")
|
|
1338
|
+
val projectId = data.getString("projectId") ?: ""
|
|
1339
|
+
|
|
1340
|
+
// Validate required parameters
|
|
1341
|
+
if (userName.isNullOrEmpty() || password.isNullOrEmpty() || realm.isNullOrEmpty() || firebaseToken.isNullOrEmpty()) {
|
|
1342
|
+
Log.e("OmikitPlugin", "❌ Missing required parameters for credential check")
|
|
1343
|
+
promise.resolve(mapOf("success" to false, "message" to "Missing required parameters"))
|
|
1344
|
+
return@launch
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
withContext(Dispatchers.Default) {
|
|
1348
|
+
try {
|
|
1349
|
+
Log.d("OmikitPlugin", "🔍 Checking credentials for user: $userName")
|
|
1350
|
+
|
|
1351
|
+
OmiClient.getInstance(reactApplicationContext!!).checkCredentials(
|
|
1352
|
+
userName = userName,
|
|
1353
|
+
password = password,
|
|
1354
|
+
realm = realm,
|
|
1355
|
+
firebaseToken = firebaseToken,
|
|
1356
|
+
host = host,
|
|
1357
|
+
projectId = projectId
|
|
1358
|
+
) { success, statusCode, message ->
|
|
1359
|
+
Log.d("OmikitPlugin", "🔍 Credential check callback - success: $success, status: $statusCode, message: $message")
|
|
1360
|
+
|
|
1361
|
+
val result = mapOf(
|
|
1362
|
+
"success" to success,
|
|
1363
|
+
"statusCode" to statusCode,
|
|
1364
|
+
"message" to (message ?: "")
|
|
1365
|
+
)
|
|
1366
|
+
|
|
1367
|
+
promise.resolve(Arguments.makeNativeMap(result))
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
} catch (e: Exception) {
|
|
1371
|
+
Log.e("OmikitPlugin", "❌ Error during credential check: ${e.message}", e)
|
|
1372
|
+
val errorResult = mapOf(
|
|
1373
|
+
"success" to false,
|
|
1374
|
+
"message" to e.message
|
|
1375
|
+
)
|
|
1376
|
+
promise.resolve(Arguments.makeNativeMap(errorResult))
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
// ✅ Function để register với full control options
|
|
1383
|
+
@ReactMethod
|
|
1384
|
+
fun registerWithOptions(data: ReadableMap, promise: Promise) {
|
|
1385
|
+
mainScope.launch {
|
|
1386
|
+
val userName = data.getString("userName")
|
|
1387
|
+
val password = data.getString("password")
|
|
1388
|
+
val realm = data.getString("realm")
|
|
1389
|
+
val host = data.getString("host") ?: "vh.omicrm.com"
|
|
1390
|
+
val isVideo = data.getBoolean("isVideo")
|
|
1391
|
+
val firebaseToken = data.getString("fcmToken")
|
|
1392
|
+
val projectId = data.getString("projectId") ?: ""
|
|
1393
|
+
val showNotification = data.getBoolean("showNotification") ?: true
|
|
1394
|
+
val enableAutoUnregister = data.getBoolean("enableAutoUnregister") ?: true
|
|
1395
|
+
|
|
1396
|
+
// Validate required parameters
|
|
1397
|
+
if (userName.isNullOrEmpty() || password.isNullOrEmpty() || realm.isNullOrEmpty() || firebaseToken.isNullOrEmpty()) {
|
|
1398
|
+
Log.e("OmikitPlugin", "❌ Missing required parameters for registration with options")
|
|
1399
|
+
promise.resolve(mapOf("success" to false, "message" to "Missing required parameters"))
|
|
1400
|
+
return@launch
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
withContext(Dispatchers.Default) {
|
|
1404
|
+
try {
|
|
1405
|
+
Log.d("OmikitPlugin", "⚙️ Registering with options for user: $userName - showNotification: $showNotification, enableAutoUnregister: $enableAutoUnregister")
|
|
1406
|
+
|
|
1407
|
+
OmiClient.getInstance(reactApplicationContext!!).registerWithOptions(
|
|
1408
|
+
userName = userName,
|
|
1409
|
+
password = password,
|
|
1410
|
+
realm = realm,
|
|
1411
|
+
isVideo = isVideo ?: true,
|
|
1412
|
+
firebaseToken = firebaseToken,
|
|
1413
|
+
host = host,
|
|
1414
|
+
projectId = projectId,
|
|
1415
|
+
showNotification = showNotification,
|
|
1416
|
+
enableAutoUnregister = enableAutoUnregister
|
|
1417
|
+
) { success, statusCode, message ->
|
|
1418
|
+
Log.d("OmikitPlugin", "⚙️ Registration with options callback - success: $success, status: $statusCode, message: $message")
|
|
1419
|
+
|
|
1420
|
+
val result = mapOf(
|
|
1421
|
+
"success" to success,
|
|
1422
|
+
"statusCode" to statusCode,
|
|
1423
|
+
"message" to (message ?: "")
|
|
1424
|
+
)
|
|
1425
|
+
|
|
1426
|
+
promise.resolve(Arguments.makeNativeMap(result))
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
} catch (e: Exception) {
|
|
1430
|
+
Log.e("OmikitPlugin", "❌ Error during registration with options: ${e.message}", e)
|
|
1431
|
+
val errorResult = mapOf(
|
|
1432
|
+
"success" to false,
|
|
1433
|
+
"message" to e.message
|
|
1434
|
+
)
|
|
1435
|
+
promise.resolve(Arguments.makeNativeMap(errorResult))
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
970
1440
|
}
|