omikit-plugin 3.3.28 → 4.0.1
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 +949 -1223
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/omikitplugin/FLLocalCameraModule.kt +1 -1
- package/android/src/main/java/com/omikitplugin/FLRemoteCameraModule.kt +1 -1
- package/android/src/main/java/com/omikitplugin/OmikitPluginModule.kt +277 -325
- package/android/src/main/java/com/omikitplugin/constants/constant.kt +2 -1
- package/ios/CallProcess/CallManager.swift +45 -35
- package/ios/Constant/Constant.swift +1 -0
- package/ios/Library/OmikitPlugin.m +75 -1
- package/ios/Library/OmikitPlugin.swift +199 -16
- package/ios/OmikitPlugin-Protocol.h +161 -0
- package/lib/commonjs/NativeOmikitPlugin.js +9 -0
- package/lib/commonjs/NativeOmikitPlugin.js.map +1 -0
- package/lib/commonjs/index.js +11 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/omi_audio_type.js +20 -0
- package/lib/commonjs/omi_audio_type.js.map +1 -0
- package/lib/commonjs/omi_local_camera.js +12 -2
- package/lib/commonjs/omi_local_camera.js.map +1 -1
- package/lib/commonjs/omi_remote_camera.js +12 -2
- package/lib/commonjs/omi_remote_camera.js.map +1 -1
- package/lib/commonjs/omi_start_call_status.js +30 -0
- package/lib/commonjs/omi_start_call_status.js.map +1 -1
- package/lib/commonjs/omikit.js +110 -16
- package/lib/commonjs/omikit.js.map +1 -1
- package/lib/module/NativeOmikitPlugin.js +3 -0
- package/lib/module/NativeOmikitPlugin.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/omi_audio_type.js +14 -0
- package/lib/module/omi_audio_type.js.map +1 -0
- package/lib/module/omi_local_camera.js +13 -2
- package/lib/module/omi_local_camera.js.map +1 -1
- package/lib/module/omi_remote_camera.js +13 -2
- package/lib/module/omi_remote_camera.js.map +1 -1
- package/lib/module/omi_start_call_status.js +30 -0
- package/lib/module/omi_start_call_status.js.map +1 -1
- package/lib/module/omikit.js +104 -17
- package/lib/module/omikit.js.map +1 -1
- package/omikit-plugin.podspec +26 -24
- package/package.json +11 -2
- package/src/NativeOmikitPlugin.ts +160 -0
- package/src/index.tsx +2 -1
- package/src/omi_audio_type.tsx +9 -0
- package/src/omi_local_camera.tsx +12 -3
- package/src/omi_remote_camera.tsx +12 -3
- package/src/omi_start_call_status.tsx +29 -10
- package/src/omikit.tsx +96 -19
- package/src/types/index.d.ts +111 -11
|
@@ -120,7 +120,7 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
120
120
|
private val mainScope = CoroutineScope(Dispatchers.Main)
|
|
121
121
|
private var isIncoming: Boolean = false
|
|
122
122
|
private var isAnswerCall: Boolean = false
|
|
123
|
-
private var permissionPromise: Promise? = null
|
|
123
|
+
@Volatile private var permissionPromise: Promise? = null
|
|
124
124
|
|
|
125
125
|
// Call state management to prevent concurrent calls
|
|
126
126
|
private var isCallInProgress: Boolean = false
|
|
@@ -134,7 +134,22 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
134
134
|
override fun getName(): String {
|
|
135
135
|
return NAME
|
|
136
136
|
}
|
|
137
|
-
|
|
137
|
+
|
|
138
|
+
override fun getConstants(): MutableMap<String, Any> {
|
|
139
|
+
return mutableMapOf(
|
|
140
|
+
"CALL_STATE_CHANGED" to CALL_STATE_CHANGED,
|
|
141
|
+
"MUTED" to MUTED,
|
|
142
|
+
"HOLD" to HOLD,
|
|
143
|
+
"SPEAKER" to SPEAKER,
|
|
144
|
+
"REMOTE_VIDEO_READY" to REMOTE_VIDEO_READY,
|
|
145
|
+
"CLICK_MISSED_CALL" to CLICK_MISSED_CALL,
|
|
146
|
+
"SWITCHBOARD_ANSWER" to SWITCHBOARD_ANSWER,
|
|
147
|
+
"CALL_QUALITY" to CALL_QUALITY,
|
|
148
|
+
"AUDIO_CHANGE" to AUDIO_CHANGE,
|
|
149
|
+
"REQUEST_PERMISSION" to REQUEST_PERMISSION
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
138
153
|
/**
|
|
139
154
|
* Check if we can start a new call (no concurrent calls, cooldown passed)
|
|
140
155
|
*/
|
|
@@ -145,12 +160,11 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
145
160
|
|
|
146
161
|
// Check if call is in progress or cooldown not passed
|
|
147
162
|
if (isCallInProgress) {
|
|
148
|
-
|
|
163
|
+
|
|
149
164
|
return false
|
|
150
165
|
}
|
|
151
166
|
|
|
152
167
|
if (timeSinceLastCall < callCooldownMs) {
|
|
153
|
-
Log.d("OMISDK", "🚫 Call blocked: Cooldown period (${callCooldownMs - timeSinceLastCall}ms remaining)")
|
|
154
168
|
return false
|
|
155
169
|
}
|
|
156
170
|
|
|
@@ -165,7 +179,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
165
179
|
synchronized(callStateLock) {
|
|
166
180
|
isCallInProgress = true
|
|
167
181
|
lastCallTime = System.currentTimeMillis()
|
|
168
|
-
Log.d("OMISDK", "📞 Call started, marking in progress")
|
|
169
182
|
}
|
|
170
183
|
}
|
|
171
184
|
|
|
@@ -175,7 +188,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
175
188
|
private fun markCallEnded() {
|
|
176
189
|
synchronized(callStateLock) {
|
|
177
190
|
isCallInProgress = false
|
|
178
|
-
Log.d("OMISDK", "📴 Call ended, clearing in progress flag")
|
|
179
191
|
}
|
|
180
192
|
}
|
|
181
193
|
|
|
@@ -183,11 +195,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
183
195
|
private val handler = Handler(Looper.getMainLooper())
|
|
184
196
|
|
|
185
197
|
override fun incomingReceived(callerId: Int?, phoneNumber: String?, isVideo: Boolean?) {
|
|
186
|
-
Log.d("OMISDK", "=>> incomingReceived CALLED - BEFORE: isIncoming: $isIncoming, isAnswerCall: $isAnswerCall")
|
|
187
198
|
isIncoming = true;
|
|
188
199
|
isAnswerCall = false; // Reset answer state for new incoming call
|
|
189
|
-
Log.d("OMISDK", "=>> incomingReceived AFTER SET - isIncoming: $isIncoming, isAnswerCall: $isAnswerCall, phoneNumber: $phoneNumber")
|
|
190
|
-
|
|
191
200
|
val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber ?: "")
|
|
192
201
|
|
|
193
202
|
val map: WritableMap = WritableNativeMap().apply {
|
|
@@ -210,11 +219,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
210
219
|
transactionId: String?,
|
|
211
220
|
) {
|
|
212
221
|
isAnswerCall = true
|
|
213
|
-
Log.d("OMISDK", "=>> ON CALL ESTABLISHED => ")
|
|
214
222
|
|
|
215
223
|
Handler(Looper.getMainLooper()).postDelayed({
|
|
216
|
-
Log.d("OmikitReactNative", "onCallEstablished")
|
|
217
|
-
|
|
218
224
|
val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber ?: "")
|
|
219
225
|
|
|
220
226
|
// ✅ Sử dụng safe WritableMap creation
|
|
@@ -233,18 +239,13 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
override fun onCallEnd(callInfo: MutableMap<String, Any?>, statusCode: Int) {
|
|
236
|
-
Log.d("OMISDK RN", "=>> onCallEnd CALLED - BEFORE RESET: isIncoming: $isIncoming, isAnswerCall: $isAnswerCall")
|
|
237
|
-
Log.d("OMISDK RN", "=>> onCallEnd callInfo => $callInfo")
|
|
238
|
-
|
|
239
242
|
// Reset call state variables
|
|
240
243
|
isIncoming = false
|
|
241
244
|
isAnswerCall = false
|
|
242
245
|
// Clear call progress state when remote party ends call
|
|
243
246
|
markCallEnded()
|
|
244
|
-
Log.d("OMISDK", "=>> onCallEnd AFTER RESET - isIncoming: $isIncoming, isAnswerCall: $isAnswerCall")
|
|
245
|
-
|
|
246
247
|
// Kiểm tra kiểu dữ liệu trước khi ép kiểu để tránh lỗi
|
|
247
|
-
val call = callInfo
|
|
248
|
+
val call = callInfo
|
|
248
249
|
|
|
249
250
|
val timeStartToAnswer = (call["time_start_to_answer"] as? Long) ?: 0L
|
|
250
251
|
val timeEnd = (call["time_end"] as? Long) ?: 0L
|
|
@@ -267,18 +268,14 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
267
268
|
)
|
|
268
269
|
|
|
269
270
|
val map = createSafeWritableMap(eventData)
|
|
270
|
-
|
|
271
|
-
Log.d("OMISDK RN", "=>> onCallEnd => ")
|
|
272
271
|
sendEvent(CALL_STATE_CHANGED, map)
|
|
273
272
|
}
|
|
274
273
|
|
|
275
274
|
override fun onConnecting() {
|
|
276
|
-
Log.d("OMISDK", "=>> ON CONNECTING CALL => ")
|
|
277
|
-
|
|
278
275
|
val map: WritableMap = WritableNativeMap().apply {
|
|
279
276
|
putString("callerNumber", "")
|
|
280
277
|
putBoolean("isVideo", NotificationService.isVideo)
|
|
281
|
-
putBoolean("incoming", isIncoming
|
|
278
|
+
putBoolean("incoming", isIncoming)
|
|
282
279
|
putString("transactionId", "")
|
|
283
280
|
putString("_id", "")
|
|
284
281
|
putInt("status", CallState.connecting.value)
|
|
@@ -299,18 +296,12 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
299
296
|
val prePhoneNumber = OmiClient.prePhoneNumber ?: ""
|
|
300
297
|
val typeNumber = OmiKitUtils().checkTypeNumber(prePhoneNumber)
|
|
301
298
|
|
|
302
|
-
Log.d("OMISDK", "=>> onRinging CALLED - BEFORE: isIncoming: $isIncoming, isAnswerCall: $isAnswerCall, callDirection: $callDirection")
|
|
303
|
-
|
|
304
299
|
if (callDirection == "inbound") {
|
|
305
300
|
isIncoming = true;
|
|
306
|
-
Log.d("OMISDK", "=>> onRinging SET isIncoming = true for inbound call")
|
|
307
301
|
} else if (callDirection == "outbound") {
|
|
308
302
|
isIncoming = false;
|
|
309
|
-
Log.d("OMISDK", "=>> onRinging SET isIncoming = false for outbound call")
|
|
310
303
|
}
|
|
311
304
|
|
|
312
|
-
Log.d("OMISDK", "=>> onRinging AFTER: isIncoming: $isIncoming, isAnswerCall: $isAnswerCall")
|
|
313
|
-
|
|
314
305
|
// ✅ Sử dụng safe WritableMap creation
|
|
315
306
|
val eventData = mapOf(
|
|
316
307
|
"callerNumber" to if (callDirection == "inbound") prePhoneNumber else "",
|
|
@@ -322,8 +313,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
322
313
|
)
|
|
323
314
|
|
|
324
315
|
val map = createSafeWritableMap(eventData)
|
|
325
|
-
|
|
326
|
-
Log.d("OMISDK", if (callDirection == "inbound") "=>> ON INCOMING CALL => " else "=>> ON RINGING CALL => ")
|
|
327
316
|
sendEvent(CALL_STATE_CHANGED, map)
|
|
328
317
|
}
|
|
329
318
|
|
|
@@ -331,13 +320,21 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
331
320
|
override fun networkHealth(stat: Map<String, *>, quality: Int) {
|
|
332
321
|
val map: WritableMap = WritableNativeMap()
|
|
333
322
|
map.putInt("quality", quality)
|
|
323
|
+
// Pass full diagnostics from stat map
|
|
324
|
+
val statMap: WritableMap = WritableNativeMap()
|
|
325
|
+
(stat["mos"] as? Number)?.let { statMap.putDouble("mos", it.toDouble()) }
|
|
326
|
+
(stat["jitter"] as? Number)?.let { statMap.putDouble("jitter", it.toDouble()) }
|
|
327
|
+
(stat["latency"] as? Number)?.let { statMap.putDouble("latency", it.toDouble()) }
|
|
328
|
+
(stat["ppl"] as? Number)?.let { statMap.putDouble("packetLoss", it.toDouble()) }
|
|
329
|
+
(stat["lcn"] as? Number)?.let { statMap.putInt("lcn", it.toInt()) }
|
|
330
|
+
map.putMap("stat", statMap)
|
|
334
331
|
sendEvent(CALL_QUALITY, map)
|
|
335
332
|
}
|
|
336
333
|
|
|
337
334
|
override fun onAudioChanged(audioInfo: Map<String, Any>) {
|
|
338
335
|
val audio: WritableMap = WritableNativeMap()
|
|
339
|
-
audio.putString("name", audioInfo["name"] as String)
|
|
340
|
-
audio.putInt("type", audioInfo["type"] as Int)
|
|
336
|
+
audio.putString("name", audioInfo["name"] as? String ?: "")
|
|
337
|
+
audio.putInt("type", audioInfo["type"] as? Int ?: 0)
|
|
341
338
|
val map: WritableMap = WritableNativeMap()
|
|
342
339
|
val writeList = WritableNativeArray()
|
|
343
340
|
writeList.pushMap(audio)
|
|
@@ -355,13 +352,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
355
352
|
}
|
|
356
353
|
|
|
357
354
|
override fun onOutgoingStarted(callerId: Int, phoneNumber: String?, isVideo: Boolean?) {
|
|
358
|
-
Log.d("OMISDK", "=>> onOutgoingStarted CALLED - BEFORE: isIncoming: $isIncoming, isAnswerCall: $isAnswerCall")
|
|
359
|
-
|
|
360
355
|
// For outgoing calls, set states appropriately
|
|
361
356
|
isIncoming = false;
|
|
362
357
|
isAnswerCall = false;
|
|
363
|
-
Log.d("OMISDK", "=>> onOutgoingStarted AFTER SET - isIncoming: $isIncoming, isAnswerCall: $isAnswerCall")
|
|
364
|
-
|
|
365
358
|
val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber ?: "")
|
|
366
359
|
|
|
367
360
|
val map: WritableMap = WritableNativeMap().apply {
|
|
@@ -384,8 +377,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
384
377
|
}
|
|
385
378
|
|
|
386
379
|
override fun onRegisterCompleted(statusCode: Int) {
|
|
387
|
-
Log.d("OMISDK", "=> ON REGISTER COMPLETED => status code: $statusCode")
|
|
388
|
-
|
|
389
380
|
if (statusCode != 200) {
|
|
390
381
|
val normalizedStatusCode = if (statusCode == 403) 853 else statusCode
|
|
391
382
|
val typeNumber = ""
|
|
@@ -413,7 +404,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
413
404
|
}
|
|
414
405
|
})
|
|
415
406
|
}
|
|
416
|
-
Log.d("OMISDK", "=>> onRequestPermission => $map")
|
|
417
407
|
sendEvent(REQUEST_PERMISSION, map)
|
|
418
408
|
|
|
419
409
|
}
|
|
@@ -456,16 +446,14 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
456
446
|
|
|
457
447
|
override fun initialize() {
|
|
458
448
|
super.initialize()
|
|
459
|
-
|
|
460
|
-
|
|
449
|
+
|
|
450
|
+
moduleInstance = this
|
|
461
451
|
reactApplicationContext!!.addActivityEventListener(this)
|
|
462
452
|
Handler(Looper.getMainLooper()).post {
|
|
463
|
-
OmiClient.getInstance(reactApplicationContext!!)
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
OmiClient.getInstance(reactApplicationContext!!).setDebug(false)
|
|
453
|
+
val client = OmiClient.getInstance(reactApplicationContext!!)
|
|
454
|
+
client.addCallStateListener(this)
|
|
455
|
+
client.addCallStateListener(autoUnregisterListener)
|
|
456
|
+
client.setDebug(false)
|
|
469
457
|
}
|
|
470
458
|
}
|
|
471
459
|
|
|
@@ -482,7 +470,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
482
470
|
OmiClient.getInstance(reactApplicationContext!!).setDebug(false)
|
|
483
471
|
promise.resolve(true)
|
|
484
472
|
} catch (e: Exception) {
|
|
485
|
-
Log.e("OmikitPlugin", "❌ Error in startServices: ${e.message}", e)
|
|
486
473
|
promise.resolve(false)
|
|
487
474
|
}
|
|
488
475
|
}
|
|
@@ -493,7 +480,7 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
493
480
|
// ✅ Method để check status AUTO-UNREGISTER (DEPRECATED)
|
|
494
481
|
@ReactMethod
|
|
495
482
|
fun getAutoUnregisterStatus(promise: Promise) {
|
|
496
|
-
|
|
483
|
+
|
|
497
484
|
try {
|
|
498
485
|
OmiClient.getInstance(reactApplicationContext!!).getAutoUnregisterStatus { isScheduled, timeUntilExecution ->
|
|
499
486
|
try {
|
|
@@ -504,12 +491,10 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
504
491
|
)
|
|
505
492
|
promise.resolve(Arguments.makeNativeMap(status))
|
|
506
493
|
} catch (e: Exception) {
|
|
507
|
-
Log.e("OmikitPlugin", "❌ Error in getAutoUnregisterStatus callback: ${e.message}", e)
|
|
508
494
|
promise.resolve(null)
|
|
509
495
|
}
|
|
510
496
|
}
|
|
511
497
|
} catch (e: Exception) {
|
|
512
|
-
Log.e("OmikitPlugin", "❌ Error calling getAutoUnregisterStatus: ${e.message}", e)
|
|
513
498
|
promise.resolve(null)
|
|
514
499
|
}
|
|
515
500
|
}
|
|
@@ -517,7 +502,7 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
517
502
|
// ✅ Method để manually prevent AUTO-UNREGISTER (DEPRECATED)
|
|
518
503
|
@ReactMethod
|
|
519
504
|
fun preventAutoUnregister(reason: String, promise: Promise) {
|
|
520
|
-
|
|
505
|
+
|
|
521
506
|
// Function removed - no longer supported
|
|
522
507
|
promise.resolve(false)
|
|
523
508
|
}
|
|
@@ -525,47 +510,37 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
525
510
|
// ✅ Convenience methods cho các scenario phổ biến (DEPRECATED)
|
|
526
511
|
@ReactMethod
|
|
527
512
|
fun prepareForIncomingCall(promise: Promise) {
|
|
528
|
-
Log.w("OmikitPlugin", "⚠️ DEPRECATED: prepareForIncomingCall() - Use Silent Registration API instead")
|
|
529
513
|
try {
|
|
530
514
|
OmiClient.getInstance(reactApplicationContext!!).prepareForIncomingCall()
|
|
531
515
|
promise.resolve(true)
|
|
532
516
|
} catch (e: Exception) {
|
|
533
|
-
Log.e("OmikitPlugin", "❌ Prepare for incoming call failed: ${e.message}", e)
|
|
534
517
|
promise.resolve(false)
|
|
535
518
|
}
|
|
536
519
|
}
|
|
537
520
|
|
|
538
521
|
@ReactMethod
|
|
539
522
|
fun prepareForOutgoingCall(promise: Promise) {
|
|
540
|
-
Log.w("OmikitPlugin", "⚠️ DEPRECATED: prepareForOutgoingCall() - Use Silent Registration API instead")
|
|
541
523
|
try {
|
|
542
524
|
OmiClient.getInstance(reactApplicationContext!!).prepareForOutgoingCall()
|
|
543
525
|
promise.resolve(true)
|
|
544
526
|
} catch (e: Exception) {
|
|
545
|
-
Log.e("OmikitPlugin", "❌ Prepare for outgoing call failed: ${e.message}", e)
|
|
546
527
|
promise.resolve(false)
|
|
547
528
|
}
|
|
548
529
|
}
|
|
549
530
|
|
|
550
531
|
private fun prepareAudioSystem() {
|
|
551
532
|
try {
|
|
552
|
-
// ✅ Check network connectivity first
|
|
553
533
|
if (!isNetworkAvailable()) {
|
|
554
534
|
return
|
|
555
535
|
}
|
|
556
|
-
|
|
536
|
+
|
|
557
537
|
// Release any existing audio focus
|
|
558
538
|
val audioManager = reactApplicationContext?.getSystemService(android.content.Context.AUDIO_SERVICE) as? android.media.AudioManager
|
|
559
539
|
audioManager?.let {
|
|
560
|
-
// Reset audio mode
|
|
561
540
|
it.mode = android.media.AudioManager.MODE_NORMAL
|
|
562
541
|
}
|
|
563
|
-
|
|
564
|
-
// Small delay để audio system ổn định
|
|
565
|
-
Thread.sleep(200)
|
|
566
|
-
|
|
567
542
|
} catch (e: Exception) {
|
|
568
|
-
Log.w("OmikitPlugin", "
|
|
543
|
+
Log.w("OmikitPlugin", "Audio preparation warning: ${e.message}")
|
|
569
544
|
}
|
|
570
545
|
}
|
|
571
546
|
|
|
@@ -573,11 +548,17 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
573
548
|
private fun isNetworkAvailable(): Boolean {
|
|
574
549
|
return try {
|
|
575
550
|
val connectivityManager = reactApplicationContext?.getSystemService(android.content.Context.CONNECTIVITY_SERVICE) as? android.net.ConnectivityManager
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
551
|
+
?: return true
|
|
552
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
553
|
+
val network = connectivityManager.activeNetwork ?: return false
|
|
554
|
+
val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
|
|
555
|
+
capabilities.hasCapability(android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
|
556
|
+
} else {
|
|
557
|
+
@Suppress("DEPRECATION")
|
|
558
|
+
connectivityManager.activeNetworkInfo?.isConnectedOrConnecting == true
|
|
559
|
+
}
|
|
579
560
|
} catch (e: Exception) {
|
|
580
|
-
Log.w("OmikitPlugin", "
|
|
561
|
+
Log.w("OmikitPlugin", "Network check failed: ${e.message}")
|
|
581
562
|
true // Assume network is available if check fails
|
|
582
563
|
}
|
|
583
564
|
}
|
|
@@ -681,15 +662,11 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
681
662
|
|
|
682
663
|
// Configure decline call behavior
|
|
683
664
|
OmiClient.getInstance(context).configureDeclineCallBehavior(isUserBusy)
|
|
684
|
-
|
|
685
|
-
Log.d("OmikitPlugin", "✅ Push notification configured successfully")
|
|
686
665
|
promise.resolve(true)
|
|
687
666
|
} catch (e: Exception) {
|
|
688
|
-
Log.e("OmikitPlugin", "❌ Error configuring push notification: ${e.message}", e)
|
|
689
667
|
promise.reject("E_CONFIG_FAILED", "Failed to configure push notification", e)
|
|
690
668
|
}
|
|
691
669
|
} ?: run {
|
|
692
|
-
Log.e("OmikitPlugin", "❌ Current activity is null")
|
|
693
670
|
promise.reject("E_NULL_ACTIVITY", "Current activity is null")
|
|
694
671
|
}
|
|
695
672
|
} catch (e: Exception) {
|
|
@@ -704,32 +681,34 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
704
681
|
val userName = data.getString("userName")
|
|
705
682
|
val password = data.getString("password")
|
|
706
683
|
val realm = data.getString("realm")
|
|
707
|
-
val host = data.getString("host")
|
|
684
|
+
val host = data.getString("host").let { if (it.isNullOrEmpty()) "vh.omicrm.com" else it }
|
|
708
685
|
val isVideo = data.getBoolean("isVideo")
|
|
709
686
|
val firebaseToken = data.getString("fcmToken")
|
|
710
687
|
val projectId = data.getString("projectId") ?: ""
|
|
688
|
+
val isSkipDevices = if (data.hasKey("isSkipDevices")) data.getBoolean("isSkipDevices") else false
|
|
711
689
|
|
|
712
690
|
// Validate required parameters
|
|
713
691
|
if (!ValidationHelper.validateRequired(mapOf(
|
|
714
692
|
"userName" to userName,
|
|
715
|
-
"password" to password,
|
|
693
|
+
"password" to password,
|
|
716
694
|
"realm" to realm,
|
|
717
695
|
"fcmToken" to firebaseToken
|
|
718
696
|
), promise)) return@launch
|
|
719
697
|
|
|
720
698
|
withContext(Dispatchers.Default) {
|
|
721
699
|
try {
|
|
722
|
-
//
|
|
700
|
+
// Cleanup before register using logout callback
|
|
723
701
|
try {
|
|
724
|
-
|
|
725
|
-
|
|
702
|
+
val logoutComplete = kotlinx.coroutines.CompletableDeferred<Unit>()
|
|
703
|
+
OmiClient.getInstance(reactApplicationContext!!).logout {
|
|
704
|
+
logoutComplete.complete(Unit)
|
|
705
|
+
}
|
|
706
|
+
// Wait for logout callback with timeout
|
|
707
|
+
kotlinx.coroutines.withTimeoutOrNull(3000) { logoutComplete.await() }
|
|
726
708
|
} catch (e: Exception) {
|
|
727
|
-
Log.w("OmikitPlugin", "
|
|
709
|
+
Log.w("OmikitPlugin", "Cleanup warning (expected): ${e.message}")
|
|
728
710
|
}
|
|
729
|
-
|
|
730
|
-
// ✅ Sử dụng Silent Registration API mới từ OmiSDK 2.3.67
|
|
731
|
-
Log.d("OmikitPlugin", "🔇 Using Silent Registration API for user: $userName")
|
|
732
|
-
|
|
711
|
+
|
|
733
712
|
OmiClient.getInstance(reactApplicationContext!!).silentRegister(
|
|
734
713
|
userName = userName ?: "",
|
|
735
714
|
password = password ?: "",
|
|
@@ -737,26 +716,18 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
737
716
|
isVideo = isVideo ?: true,
|
|
738
717
|
firebaseToken = firebaseToken ?: "",
|
|
739
718
|
host = host,
|
|
740
|
-
projectId = projectId
|
|
719
|
+
projectId = projectId,
|
|
720
|
+
isSkipDevices = isSkipDevices
|
|
741
721
|
) { success, statusCode, message ->
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
Log.d("OmikitPlugin", "✅ Silent registration successful - no notification, no auto-unregister")
|
|
745
|
-
// ✅ Resolve promise với kết quả từ callback
|
|
746
|
-
promise.resolve(success)
|
|
722
|
+
if (success || statusCode == 200) {
|
|
723
|
+
promise.resolve(true)
|
|
747
724
|
} else {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
promise.resolve(true)
|
|
751
|
-
} else {
|
|
752
|
-
val (errorCode, errorMessage) = OmiRegistrationStatus.getError(statusCode)
|
|
753
|
-
promise.reject(errorCode, "$errorMessage (Status: $statusCode)")
|
|
754
|
-
}
|
|
725
|
+
val (errorCode, errorMessage) = OmiRegistrationStatus.getError(statusCode)
|
|
726
|
+
promise.reject(errorCode, "$errorMessage (Status: $statusCode)")
|
|
755
727
|
}
|
|
756
728
|
}
|
|
757
729
|
|
|
758
730
|
} catch (e: Exception) {
|
|
759
|
-
Log.e("OmikitPlugin", "❌ Error during silent registration: ${e.message}", e)
|
|
760
731
|
promise.reject("ERROR_INITIALIZATION_EXCEPTION", "OMICALL initialization failed due to an unexpected error: ${e.message}. Please check your network connection and configuration.", e)
|
|
761
732
|
}
|
|
762
733
|
}
|
|
@@ -765,7 +736,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
765
736
|
|
|
766
737
|
@ReactMethod
|
|
767
738
|
fun initCallWithApiKey(data: ReadableMap, promise: Promise) {
|
|
768
|
-
Log.d("OmikitPlugin", "🔑 initCallWithApiKey called")
|
|
769
739
|
mainScope.launch {
|
|
770
740
|
var loginResult = false
|
|
771
741
|
val usrName = data.getString("fullName") ?: ""
|
|
@@ -776,11 +746,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
776
746
|
val firebaseToken = data.getString("fcmToken") ?: ""
|
|
777
747
|
val projectId = data.getString("projectId") ?: ""
|
|
778
748
|
|
|
779
|
-
Log.d("OmikitPlugin", "🔑 Parameters - usrName: $usrName, usrUuid: $usrUuid, isVideo: $isVideo")
|
|
780
|
-
|
|
781
749
|
withContext(Dispatchers.Default) {
|
|
782
750
|
try {
|
|
783
|
-
Log.d("OmikitPlugin", "🔑 Starting validation")
|
|
784
751
|
// Validate required parameters
|
|
785
752
|
if (!ValidationHelper.validateRequired(mapOf(
|
|
786
753
|
"fullName" to usrName,
|
|
@@ -792,8 +759,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
792
759
|
return@withContext
|
|
793
760
|
}
|
|
794
761
|
|
|
795
|
-
Log.d("OmikitPlugin", "✅ Validation passed")
|
|
796
|
-
|
|
797
762
|
// Check RECORD_AUDIO permission for Android 14+
|
|
798
763
|
val hasRecordAudio = ContextCompat.checkSelfPermission(
|
|
799
764
|
reactApplicationContext,
|
|
@@ -801,28 +766,22 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
801
766
|
) == PackageManager.PERMISSION_GRANTED
|
|
802
767
|
|
|
803
768
|
if (!hasRecordAudio) {
|
|
804
|
-
Log.e("OmikitPlugin", "❌ RECORD_AUDIO permission is required for Android 14+")
|
|
805
769
|
promise.resolve(false)
|
|
806
770
|
return@withContext
|
|
807
771
|
}
|
|
808
|
-
|
|
809
|
-
Log.d("OmikitPlugin", "✅ RECORD_AUDIO permission granted")
|
|
810
|
-
|
|
811
|
-
// ✅ Cleanup trước khi register với mutex
|
|
772
|
+
// Cleanup before register using logout callback
|
|
812
773
|
try {
|
|
813
|
-
Log.d("OmikitPlugin", "🧹 Starting cleanup")
|
|
814
774
|
omiClientMutex.withLock {
|
|
815
|
-
|
|
775
|
+
val logoutComplete = kotlinx.coroutines.CompletableDeferred<Unit>()
|
|
776
|
+
OmiClient.getInstance(reactApplicationContext!!).logout {
|
|
777
|
+
logoutComplete.complete(Unit)
|
|
778
|
+
}
|
|
779
|
+
kotlinx.coroutines.withTimeoutOrNull(3000) { logoutComplete.await() }
|
|
816
780
|
}
|
|
817
|
-
delay(1000) // Chờ cleanup hoàn tất
|
|
818
|
-
Log.d("OmikitPlugin", "✅ Cleanup completed")
|
|
819
781
|
} catch (e: Exception) {
|
|
820
|
-
Log.w("OmikitPlugin", "
|
|
782
|
+
Log.w("OmikitPlugin", "Cleanup warning (expected): ${e.message}")
|
|
821
783
|
}
|
|
822
784
|
|
|
823
|
-
Log.d("OmikitPlugin", "🔑 Using API key registration for user: $usrName")
|
|
824
|
-
|
|
825
|
-
Log.d("OmikitPlugin", "🔑 Calling OmiClient.registerWithApiKey...")
|
|
826
785
|
omiClientMutex.withLock {
|
|
827
786
|
loginResult = OmiClient.registerWithApiKey(
|
|
828
787
|
apiKey ?: "",
|
|
@@ -834,16 +793,7 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
834
793
|
projectId
|
|
835
794
|
)
|
|
836
795
|
}
|
|
837
|
-
|
|
838
|
-
Log.d("OmikitPlugin", "🔑 OmiClient.registerWithApiKey returned: $loginResult")
|
|
839
|
-
|
|
840
|
-
if (loginResult) {
|
|
841
|
-
Log.d("OmikitPlugin", "✅ API key registration successful")
|
|
842
|
-
promise.resolve(true)
|
|
843
|
-
} else {
|
|
844
|
-
Log.e("OmikitPlugin", "❌ API key registration failed")
|
|
845
|
-
promise.resolve(false)
|
|
846
|
-
}
|
|
796
|
+
promise.resolve(loginResult)
|
|
847
797
|
} catch (e: Exception) {
|
|
848
798
|
Log.e("OmikitPlugin", "❌ Error during API key registration: ${e.message}", e)
|
|
849
799
|
promise.resolve(false)
|
|
@@ -855,21 +805,17 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
855
805
|
@ReactMethod
|
|
856
806
|
fun getInitialCall(counter: Int = 1, promise: Promise) {
|
|
857
807
|
val context = reactApplicationContext ?: run {
|
|
858
|
-
Log.e("getInitialCall", "❌ React context is null")
|
|
859
808
|
promise.resolve(false)
|
|
860
809
|
return
|
|
861
810
|
}
|
|
862
811
|
|
|
863
812
|
val call = Utils.getActiveCall(context)
|
|
864
|
-
Log.d("getInitialCall RN", "📞 Active call: $call")
|
|
865
|
-
|
|
866
813
|
if (call == null) {
|
|
867
814
|
if (counter <= 0) {
|
|
868
815
|
promise.resolve(false)
|
|
869
816
|
} else {
|
|
870
817
|
mainScope.launch {
|
|
871
|
-
|
|
872
|
-
delay(1000) // Wait 2 seconds
|
|
818
|
+
delay(1000) // Wait 1 second before retry
|
|
873
819
|
getInitialCall(counter - 1, promise) // Retry recursively
|
|
874
820
|
}
|
|
875
821
|
}
|
|
@@ -893,11 +839,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
893
839
|
statusPendingCall == 5 // 5 = User clicked pickup (CONFIRMED)
|
|
894
840
|
|
|
895
841
|
if (shouldAutoAnswer) {
|
|
896
|
-
Log.d("getInitialCall RN", "🚀 AUTO-ANSWER: User clicked pickup (statusPendingCall=$statusPendingCall), answering call immediately")
|
|
897
842
|
try {
|
|
898
843
|
OmiClient.getInstance(context).pickUp()
|
|
899
|
-
Log.d("getInitialCall RN", "✅ AUTO-ANSWER: Call answered successfully")
|
|
900
|
-
|
|
901
844
|
// Status already cleared by getStatusPendingCall()
|
|
902
845
|
} catch (e: Exception) {
|
|
903
846
|
Log.e("getInitialCall RN", "❌ AUTO-ANSWER: Failed to answer call: ${e.message}", e)
|
|
@@ -922,8 +865,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
922
865
|
promise.resolve(map)
|
|
923
866
|
|
|
924
867
|
if (statusPendingCall == 2 && call.state != 5) {
|
|
925
|
-
Log.d("getInitialCall RN", "🚀 Incoming Receive Triggered ($statusPendingCall)")
|
|
926
|
-
|
|
927
868
|
val eventMap: WritableMap = WritableNativeMap().apply {
|
|
928
869
|
putBoolean("isVideo", call.isVideo ?: false)
|
|
929
870
|
putBoolean("incoming", true)
|
|
@@ -946,25 +887,34 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
946
887
|
)
|
|
947
888
|
val map: WritableMap = WritableNativeMap()
|
|
948
889
|
if (audio == PackageManager.PERMISSION_GRANTED) {
|
|
949
|
-
currentActivity
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
890
|
+
val activity = currentActivity
|
|
891
|
+
if (activity == null) {
|
|
892
|
+
promise.reject("E_NO_ACTIVITY", "Current activity is null")
|
|
893
|
+
return
|
|
894
|
+
}
|
|
895
|
+
activity.runOnUiThread {
|
|
896
|
+
val phoneNumber = data.getString("phoneNumber")
|
|
897
|
+
if (phoneNumber.isNullOrEmpty()) {
|
|
898
|
+
promise.reject("E_INVALID_PHONE", "Phone number is required")
|
|
899
|
+
return@runOnUiThread
|
|
900
|
+
}
|
|
901
|
+
val isVideo = data.getBoolean("isVideo")
|
|
902
|
+
|
|
953
903
|
val startCallResult =
|
|
954
904
|
OmiClient.getInstance(reactApplicationContext!!).startCall(phoneNumber, isVideo)
|
|
955
|
-
var statusCalltemp = startCallResult.value as Int
|
|
905
|
+
var statusCalltemp = startCallResult.value as Int
|
|
956
906
|
if (startCallResult.value == 200 || startCallResult.value == 407) {
|
|
957
907
|
statusCalltemp = 8
|
|
958
908
|
}
|
|
959
909
|
map.putInt("status", statusCalltemp)
|
|
960
910
|
map.putString("_id", "")
|
|
961
|
-
map.putString("message", messageCall(startCallResult.value)
|
|
911
|
+
map.putString("message", messageCall(startCallResult.value))
|
|
962
912
|
promise.resolve(map)
|
|
963
913
|
}
|
|
964
914
|
} else {
|
|
965
915
|
map.putInt("status", 4)
|
|
966
916
|
map.putString("_id", "")
|
|
967
|
-
map.putString("message", messageCall(406)
|
|
917
|
+
map.putString("message", messageCall(406))
|
|
968
918
|
promise.resolve(map)
|
|
969
919
|
}
|
|
970
920
|
}
|
|
@@ -976,28 +926,27 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
976
926
|
reactApplicationContext!!,
|
|
977
927
|
Manifest.permission.RECORD_AUDIO
|
|
978
928
|
)
|
|
979
|
-
Log.d("OMISDK", "📤 Start Call With UUID")
|
|
980
929
|
val map: WritableMap = WritableNativeMap()
|
|
981
930
|
if (audio == PackageManager.PERMISSION_GRANTED) {
|
|
982
931
|
mainScope.launch {
|
|
983
932
|
val uuid = data.getString("usrUuid") ?: ""
|
|
984
|
-
val isVideo = data.getBoolean("isVideo")
|
|
985
|
-
|
|
933
|
+
val isVideo = data.getBoolean("isVideo")
|
|
934
|
+
|
|
986
935
|
val startCallResult =
|
|
987
936
|
OmiClient.getInstance(reactApplicationContext!!).startCallWithUuid(uuid, isVideo)
|
|
988
|
-
var statusCalltemp = startCallResult.value as Int
|
|
937
|
+
var statusCalltemp = startCallResult.value as Int
|
|
989
938
|
if (startCallResult.value == 200 || startCallResult.value == 407) {
|
|
990
939
|
statusCalltemp = 8
|
|
991
940
|
}
|
|
992
941
|
map.putInt("status", statusCalltemp)
|
|
993
942
|
map.putString("_id", "")
|
|
994
|
-
map.putString("message", messageCall(startCallResult.value)
|
|
943
|
+
map.putString("message", messageCall(startCallResult.value))
|
|
995
944
|
promise.resolve(map)
|
|
996
945
|
}
|
|
997
946
|
} else {
|
|
998
947
|
map.putInt("status", 4)
|
|
999
948
|
map.putString("_id", "")
|
|
1000
|
-
map.putString("message", messageCall(406)
|
|
949
|
+
map.putString("message", messageCall(406))
|
|
1001
950
|
promise.resolve(map)
|
|
1002
951
|
}
|
|
1003
952
|
}
|
|
@@ -1008,7 +957,7 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1008
957
|
val appContext = reactApplicationContext.applicationContext
|
|
1009
958
|
val activity = currentActivity
|
|
1010
959
|
|
|
1011
|
-
if (appContext == null) {
|
|
960
|
+
if (appContext == null) {
|
|
1012
961
|
promise.reject("E_NULL_CONTEXT", "Application context is null")
|
|
1013
962
|
return
|
|
1014
963
|
}
|
|
@@ -1044,15 +993,11 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1044
993
|
|
|
1045
994
|
@ReactMethod
|
|
1046
995
|
fun rejectCall(promise: Promise) {
|
|
1047
|
-
Log.d("OMISDK", "➡️ rejectCall called - isIncoming: $isIncoming, isAnswerCall: $isAnswerCall")
|
|
1048
996
|
if (isIncoming) {
|
|
1049
|
-
Log.d("OMISDK", "📞 Incoming call")
|
|
1050
997
|
ValidationHelper.safeOmiClientAccess(reactApplicationContext!!) { omiClient ->
|
|
1051
998
|
if (!isAnswerCall) {
|
|
1052
|
-
Log.d("OMISDK", "🚫 Declining call with declineWithCode(true)")
|
|
1053
999
|
omiClient.declineWithCode(true) // 486 Busy Here
|
|
1054
1000
|
} else {
|
|
1055
|
-
Log.d("OMISDK", "📴 Call already answered, hanging up")
|
|
1056
1001
|
omiClient.hangUp()
|
|
1057
1002
|
}
|
|
1058
1003
|
}
|
|
@@ -1060,7 +1005,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1060
1005
|
markCallEnded()
|
|
1061
1006
|
promise.resolve(true)
|
|
1062
1007
|
} else {
|
|
1063
|
-
Log.d("OMISDK", "📤 Not incoming call, skipping reject")
|
|
1064
1008
|
promise.resolve(false)
|
|
1065
1009
|
}
|
|
1066
1010
|
}
|
|
@@ -1120,7 +1064,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1120
1064
|
|
|
1121
1065
|
@ReactMethod
|
|
1122
1066
|
fun toggleSpeaker(promise: Promise) {
|
|
1123
|
-
currentActivity
|
|
1067
|
+
val activity = currentActivity
|
|
1068
|
+
if (activity == null) { promise.resolve(null); return }
|
|
1069
|
+
activity.runOnUiThread {
|
|
1124
1070
|
val newStatus = OmiClient.getInstance(reactApplicationContext!!).toggleSpeaker()
|
|
1125
1071
|
promise.resolve(newStatus)
|
|
1126
1072
|
sendEvent(SPEAKER, newStatus)
|
|
@@ -1129,7 +1075,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1129
1075
|
|
|
1130
1076
|
@ReactMethod
|
|
1131
1077
|
fun sendDTMF(data: ReadableMap, promise: Promise) {
|
|
1132
|
-
currentActivity
|
|
1078
|
+
val activity = currentActivity
|
|
1079
|
+
if (activity == null) { promise.resolve(false); return }
|
|
1080
|
+
activity.runOnUiThread {
|
|
1133
1081
|
val character = data.getString("character")
|
|
1134
1082
|
var characterCode: Int? = character?.toIntOrNull()
|
|
1135
1083
|
if (character == "*") {
|
|
@@ -1147,7 +1095,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1147
1095
|
|
|
1148
1096
|
@ReactMethod
|
|
1149
1097
|
fun switchOmiCamera(promise: Promise) {
|
|
1150
|
-
currentActivity
|
|
1098
|
+
val activity = currentActivity
|
|
1099
|
+
if (activity == null) { promise.resolve(false); return }
|
|
1100
|
+
activity.runOnUiThread {
|
|
1151
1101
|
OmiClient.getInstance(reactApplicationContext!!).switchCamera()
|
|
1152
1102
|
promise.resolve(true)
|
|
1153
1103
|
}
|
|
@@ -1155,7 +1105,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1155
1105
|
|
|
1156
1106
|
@ReactMethod
|
|
1157
1107
|
fun toggleOmiVideo(promise: Promise) {
|
|
1158
|
-
currentActivity
|
|
1108
|
+
val activity = currentActivity
|
|
1109
|
+
if (activity == null) { promise.resolve(false); return }
|
|
1110
|
+
activity.runOnUiThread {
|
|
1159
1111
|
OmiClient.getInstance(reactApplicationContext!!).toggleCamera()
|
|
1160
1112
|
promise.resolve(true)
|
|
1161
1113
|
}
|
|
@@ -1261,14 +1213,111 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1261
1213
|
}
|
|
1262
1214
|
}
|
|
1263
1215
|
|
|
1216
|
+
// MARK: - Getter Functions
|
|
1217
|
+
@ReactMethod
|
|
1218
|
+
fun getProjectId(promise: Promise) {
|
|
1219
|
+
try {
|
|
1220
|
+
val info = OmiClient.registrationInfo
|
|
1221
|
+
if (info?.projectId != null) {
|
|
1222
|
+
promise.resolve(info.projectId)
|
|
1223
|
+
return
|
|
1224
|
+
}
|
|
1225
|
+
// Fallback: get from Firebase project ID
|
|
1226
|
+
val firebaseProjectId = try {
|
|
1227
|
+
com.google.firebase.FirebaseApp.getInstance().options.projectId
|
|
1228
|
+
} catch (e: Exception) { null }
|
|
1229
|
+
promise.resolve(firebaseProjectId)
|
|
1230
|
+
} catch (e: Throwable) {
|
|
1231
|
+
promise.resolve(null)
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
@ReactMethod
|
|
1236
|
+
fun getAppId(promise: Promise) {
|
|
1237
|
+
try {
|
|
1238
|
+
val info = OmiClient.registrationInfo
|
|
1239
|
+
if (info?.appId != null) {
|
|
1240
|
+
promise.resolve(info.appId)
|
|
1241
|
+
return
|
|
1242
|
+
}
|
|
1243
|
+
// Fallback: get package name of the host app
|
|
1244
|
+
promise.resolve(reactApplicationContext?.packageName)
|
|
1245
|
+
} catch (e: Throwable) {
|
|
1246
|
+
promise.resolve(null)
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
@ReactMethod
|
|
1251
|
+
fun getDeviceId(promise: Promise) {
|
|
1252
|
+
try {
|
|
1253
|
+
val info = OmiClient.registrationInfo
|
|
1254
|
+
if (info?.deviceId != null) {
|
|
1255
|
+
promise.resolve(info.deviceId)
|
|
1256
|
+
return
|
|
1257
|
+
}
|
|
1258
|
+
// Fallback: get Android ID directly
|
|
1259
|
+
val androidId = try {
|
|
1260
|
+
Settings.Secure.getString(
|
|
1261
|
+
reactApplicationContext?.contentResolver,
|
|
1262
|
+
Settings.Secure.ANDROID_ID
|
|
1263
|
+
)
|
|
1264
|
+
} catch (e: Exception) { null }
|
|
1265
|
+
promise.resolve(androidId)
|
|
1266
|
+
} catch (e: Throwable) {
|
|
1267
|
+
promise.resolve(null)
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
@ReactMethod
|
|
1272
|
+
fun getFcmToken(promise: Promise) {
|
|
1273
|
+
try {
|
|
1274
|
+
val info = OmiClient.registrationInfo
|
|
1275
|
+
if (info?.firebaseToken != null) {
|
|
1276
|
+
promise.resolve(info.firebaseToken)
|
|
1277
|
+
return
|
|
1278
|
+
}
|
|
1279
|
+
// Fallback: get FCM token directly from Firebase Messaging
|
|
1280
|
+
try {
|
|
1281
|
+
com.google.firebase.messaging.FirebaseMessaging.getInstance().token
|
|
1282
|
+
.addOnSuccessListener { token -> promise.resolve(token) }
|
|
1283
|
+
.addOnFailureListener { promise.resolve(null) }
|
|
1284
|
+
} catch (e: Exception) {
|
|
1285
|
+
promise.resolve(null)
|
|
1286
|
+
}
|
|
1287
|
+
} catch (e: Throwable) {
|
|
1288
|
+
promise.resolve(null)
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
@ReactMethod
|
|
1293
|
+
fun getSipInfo(promise: Promise) {
|
|
1294
|
+
try {
|
|
1295
|
+
val sipUser = OmiClient.getInstance(reactApplicationContext!!).getSipUser()
|
|
1296
|
+
val sipRealm = OmiClient.getInstance(reactApplicationContext!!).getSipRealm()
|
|
1297
|
+
if (!sipUser.isNullOrEmpty() && !sipRealm.isNullOrEmpty()) {
|
|
1298
|
+
promise.resolve("$sipUser@$sipRealm")
|
|
1299
|
+
} else {
|
|
1300
|
+
promise.resolve(sipUser)
|
|
1301
|
+
}
|
|
1302
|
+
} catch (e: Throwable) {
|
|
1303
|
+
promise.resolve(null)
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
@ReactMethod
|
|
1308
|
+
fun getVoipToken(promise: Promise) {
|
|
1309
|
+
// VoIP token is iOS only, Android returns null
|
|
1310
|
+
promise.resolve(null)
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1264
1313
|
@ReactMethod
|
|
1265
1314
|
fun getAudio(promise: Promise) {
|
|
1266
1315
|
val inputs = OmiClient.getInstance(reactApplicationContext!!).getAudioOutputs()
|
|
1267
1316
|
val writeList = WritableNativeArray()
|
|
1268
1317
|
inputs.forEach {
|
|
1269
1318
|
val map = WritableNativeMap()
|
|
1270
|
-
map.putString("name", it["name"] as String)
|
|
1271
|
-
map.putInt("type", it["type"] as Int)
|
|
1319
|
+
map.putString("name", it["name"] as? String ?: "")
|
|
1320
|
+
map.putInt("type", it["type"] as? Int ?: 0)
|
|
1272
1321
|
writeList.pushMap(map)
|
|
1273
1322
|
}
|
|
1274
1323
|
promise.resolve(writeList)
|
|
@@ -1278,8 +1327,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1278
1327
|
fun getCurrentAudio(promise: Promise) {
|
|
1279
1328
|
val currentAudio = OmiClient.getInstance(reactApplicationContext!!).getCurrentAudio()
|
|
1280
1329
|
val map: WritableMap = WritableNativeMap()
|
|
1281
|
-
map.putString("name", currentAudio["name"] as String)
|
|
1282
|
-
map.putInt("type", currentAudio["type"] as Int)
|
|
1330
|
+
map.putString("name", currentAudio["name"] as? String ?: "")
|
|
1331
|
+
map.putInt("type", currentAudio["type"] as? Int ?: 0)
|
|
1283
1332
|
val writeList = WritableNativeArray()
|
|
1284
1333
|
writeList.pushMap(map)
|
|
1285
1334
|
promise.resolve(writeList)
|
|
@@ -1294,14 +1343,16 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1294
1343
|
|
|
1295
1344
|
@ReactMethod
|
|
1296
1345
|
fun transferCall(data: ReadableMap, promise: Promise) {
|
|
1297
|
-
currentActivity
|
|
1346
|
+
val activity = currentActivity
|
|
1347
|
+
if (activity == null) { promise.resolve(false); return }
|
|
1348
|
+
activity.runOnUiThread {
|
|
1298
1349
|
val phone = data.getString("phoneNumber")
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
OmiClient.getInstance(reactApplicationContext!!).forwardCallTo(phone as String)
|
|
1303
|
-
promise.resolve(true)
|
|
1350
|
+
if (phone.isNullOrEmpty()) {
|
|
1351
|
+
promise.reject("E_INVALID_PHONE", "Phone number is required for transfer")
|
|
1352
|
+
return@runOnUiThread
|
|
1304
1353
|
}
|
|
1354
|
+
OmiClient.getInstance(reactApplicationContext!!).forwardCallTo(phone)
|
|
1355
|
+
promise.resolve(true)
|
|
1305
1356
|
}
|
|
1306
1357
|
}
|
|
1307
1358
|
|
|
@@ -1310,6 +1361,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1310
1361
|
const val REQUEST_PERMISSIONS_CODE = 1001
|
|
1311
1362
|
const val REQUEST_OVERLAY_PERMISSION_CODE = 1002
|
|
1312
1363
|
|
|
1364
|
+
// Singleton reference for companion methods to access module instance
|
|
1365
|
+
@Volatile var moduleInstance: OmikitPluginModule? = null
|
|
1366
|
+
|
|
1313
1367
|
fun onDestroy() {
|
|
1314
1368
|
try {
|
|
1315
1369
|
// Cleanup OmiClient resources safely
|
|
@@ -1320,7 +1374,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1320
1374
|
|
|
1321
1375
|
fun onResume(act: ReactActivity) {
|
|
1322
1376
|
act.let { context ->
|
|
1323
|
-
Log.d("OMISDK_REACT", "=>> onResume => ")
|
|
1324
1377
|
OmiClient.getInstance(context, true)
|
|
1325
1378
|
OmiClient.isAppReady = true;
|
|
1326
1379
|
}
|
|
@@ -1349,37 +1402,28 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1349
1402
|
act: ReactActivity
|
|
1350
1403
|
) {
|
|
1351
1404
|
try {
|
|
1352
|
-
val deniedPermissions = mutableListOf<String>()
|
|
1353
1405
|
val grantedPermissions = mutableListOf<String>()
|
|
1354
|
-
|
|
1406
|
+
|
|
1355
1407
|
for (i in permissions.indices) {
|
|
1356
1408
|
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
|
1357
1409
|
grantedPermissions.add(permissions[i])
|
|
1358
|
-
} else {
|
|
1359
|
-
deniedPermissions.add(permissions[i])
|
|
1360
1410
|
}
|
|
1361
1411
|
}
|
|
1362
|
-
|
|
1363
|
-
Log.d("OmikitPlugin", "✅ Granted: ${grantedPermissions.joinToString()}")
|
|
1364
|
-
if (deniedPermissions.isNotEmpty()) {
|
|
1365
|
-
Log.w("OmikitPlugin", "❌ Denied: ${deniedPermissions.joinToString()}")
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1412
|
+
|
|
1368
1413
|
// Check if we have essential permissions for VoIP
|
|
1369
1414
|
val hasRecordAudio = grantedPermissions.contains(Manifest.permission.RECORD_AUDIO)
|
|
1370
1415
|
val hasCallPhone = grantedPermissions.contains(Manifest.permission.CALL_PHONE)
|
|
1371
1416
|
val hasModifyAudio = grantedPermissions.contains(Manifest.permission.MODIFY_AUDIO_SETTINGS)
|
|
1372
|
-
|
|
1417
|
+
|
|
1373
1418
|
val canProceed = hasRecordAudio && hasCallPhone && hasModifyAudio
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
Log.e("OmikitPlugin", "⚠️ Missing essential VoIP permissions - app may not work properly")
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1419
|
+
|
|
1420
|
+
// Resolve the stored permission promise
|
|
1421
|
+
moduleInstance?.permissionPromise?.resolve(canProceed)
|
|
1422
|
+
moduleInstance?.permissionPromise = null
|
|
1381
1423
|
} catch (e: Exception) {
|
|
1382
|
-
Log.e("OmikitPlugin", "
|
|
1424
|
+
Log.e("OmikitPlugin", "Error handling permission results: ${e.message}", e)
|
|
1425
|
+
moduleInstance?.permissionPromise?.resolve(false)
|
|
1426
|
+
moduleInstance?.permissionPromise = null
|
|
1383
1427
|
}
|
|
1384
1428
|
}
|
|
1385
1429
|
|
|
@@ -1392,7 +1436,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1392
1436
|
try {
|
|
1393
1437
|
val isIncoming = intent.getBooleanExtra(SipServiceConstants.ACTION_IS_INCOMING_CALL, false)
|
|
1394
1438
|
if (!isIncoming) {
|
|
1395
|
-
Log.d("PICKUP-FIX", "Not an incoming call intent, skipping")
|
|
1396
1439
|
return
|
|
1397
1440
|
}
|
|
1398
1441
|
|
|
@@ -1400,20 +1443,17 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1400
1443
|
SipServiceConstants.ACTION_ACCEPT_INCOMING_CALL, false
|
|
1401
1444
|
)
|
|
1402
1445
|
|
|
1403
|
-
Log.d("PICKUP-FIX", "🚀 Early intent handler - isIncoming: $isIncoming, isAccepted: $isAcceptedCall")
|
|
1404
1446
|
|
|
1405
1447
|
// Save to SharedPreferences so getInitialCall() can detect it later
|
|
1406
1448
|
// setStatusPendingCall(true) → saves status=5 (CONFIRMED)
|
|
1407
1449
|
// setStatusPendingCall(false) → saves status=2 (INCOMING)
|
|
1408
1450
|
OmiKitUtils().setStatusPendingCall(act, isAcceptedCall)
|
|
1409
|
-
Log.d("PICKUP-FIX", "✅ Saved pickup state to SharedPreferences (isAccepted=$isAcceptedCall)")
|
|
1410
1451
|
|
|
1411
1452
|
if (isAcceptedCall) {
|
|
1412
1453
|
// Try to answer immediately if possible (may fail if SDK not ready)
|
|
1413
1454
|
try {
|
|
1414
1455
|
OmiClient.getInstance(act, true)?.let { client ->
|
|
1415
1456
|
client.pickUp()
|
|
1416
|
-
Log.d("PICKUP-FIX", "✅ Successfully answered call immediately")
|
|
1417
1457
|
} ?: Log.w("PICKUP-FIX", "⚠️ OmiClient not ready, will auto-answer in getInitialCall()")
|
|
1418
1458
|
} catch (e: Exception) {
|
|
1419
1459
|
Log.w("PICKUP-FIX", "⚠️ Cannot answer immediately (SDK not ready): ${e.message}. Will auto-answer in getInitialCall()")
|
|
@@ -1440,7 +1480,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1440
1480
|
)
|
|
1441
1481
|
if (isReopenCall) {
|
|
1442
1482
|
val activeCall = Utils.getActiveCall(context)
|
|
1443
|
-
|
|
1483
|
+
// Use singleton module instance instead of creating a throwaway one
|
|
1484
|
+
moduleInstance?.onCallEstablished(
|
|
1444
1485
|
activeCall?.id ?: 0,
|
|
1445
1486
|
activeCall?.remoteNumber,
|
|
1446
1487
|
activeCall?.isVideo,
|
|
@@ -1453,7 +1494,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1453
1494
|
}
|
|
1454
1495
|
OmiKitUtils().setStatusPendingCall(context, isAcceptedCall)
|
|
1455
1496
|
}
|
|
1456
|
-
|
|
1457
1497
|
}
|
|
1458
1498
|
}
|
|
1459
1499
|
}
|
|
@@ -1461,19 +1501,16 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1461
1501
|
// ✅ Di chuyển sendEvent vào trong class để có thể access reactApplicationContext
|
|
1462
1502
|
private fun sendEvent(eventName: String?, params: Any?) {
|
|
1463
1503
|
if (eventName == null) {
|
|
1464
|
-
Log.e("OmikitPlugin", "❌ eventName is null or empty. Không thể gửi event.")
|
|
1465
1504
|
return
|
|
1466
1505
|
}
|
|
1467
1506
|
|
|
1468
1507
|
try {
|
|
1469
1508
|
// ✅ Kiểm tra reactApplicationContext
|
|
1470
1509
|
if (reactApplicationContext == null) {
|
|
1471
|
-
Log.e("OmikitPlugin", "❌ reactApplicationContext is null")
|
|
1472
1510
|
return
|
|
1473
1511
|
}
|
|
1474
1512
|
|
|
1475
1513
|
if (!reactApplicationContext.hasActiveReactInstance()) {
|
|
1476
|
-
Log.w("OmikitPlugin", "⚠️ ReactApplicationContext không có active React instance")
|
|
1477
1514
|
return
|
|
1478
1515
|
}
|
|
1479
1516
|
|
|
@@ -1498,35 +1535,16 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1498
1535
|
}
|
|
1499
1536
|
}
|
|
1500
1537
|
|
|
1501
|
-
// ✅ Thêm method để React Native biết các event được hỗ trợ
|
|
1502
|
-
override fun getConstants(): MutableMap<String, Any> {
|
|
1503
|
-
return hashMapOf(
|
|
1504
|
-
"CALL_STATE_CHANGED" to CALL_STATE_CHANGED,
|
|
1505
|
-
"MUTED" to MUTED,
|
|
1506
|
-
"HOLD" to HOLD,
|
|
1507
|
-
"SPEAKER" to SPEAKER,
|
|
1508
|
-
"CALL_QUALITY" to CALL_QUALITY,
|
|
1509
|
-
"AUDIO_CHANGE" to AUDIO_CHANGE,
|
|
1510
|
-
"SWITCHBOARD_ANSWER" to SWITCHBOARD_ANSWER,
|
|
1511
|
-
"REQUEST_PERMISSION" to REQUEST_PERMISSION,
|
|
1512
|
-
"CLICK_MISSED_CALL" to CLICK_MISSED_CALL,
|
|
1513
|
-
"AUTO_UNREGISTER_STATUS" to "AUTO_UNREGISTER_STATUS"
|
|
1514
|
-
)
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
1538
|
@ReactMethod
|
|
1518
1539
|
fun checkAndRequestPermissions(isVideo: Boolean, promise: Promise) {
|
|
1519
1540
|
try {
|
|
1520
1541
|
val missingPermissions = getMissingPermissions(isVideo)
|
|
1521
1542
|
|
|
1522
1543
|
if (missingPermissions.isEmpty()) {
|
|
1523
|
-
Log.d("OmikitPlugin", "✅ All permissions already granted")
|
|
1524
1544
|
promise.resolve(true)
|
|
1525
1545
|
return
|
|
1526
1546
|
}
|
|
1527
1547
|
|
|
1528
|
-
Log.d("OmikitPlugin", "📋 Missing permissions: ${missingPermissions.joinToString()}")
|
|
1529
|
-
|
|
1530
1548
|
// Store promise for callback
|
|
1531
1549
|
permissionPromise = promise
|
|
1532
1550
|
|
|
@@ -1536,7 +1554,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1536
1554
|
REQUEST_PERMISSIONS_CODE,
|
|
1537
1555
|
)
|
|
1538
1556
|
} catch (e: Exception) {
|
|
1539
|
-
Log.e("OmikitPlugin", "❌ Error checking permissions: ${e.message}", e)
|
|
1540
1557
|
promise.resolve(false)
|
|
1541
1558
|
}
|
|
1542
1559
|
}
|
|
@@ -1704,8 +1721,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1704
1721
|
// Store promise for callback
|
|
1705
1722
|
permissionPromise = promise
|
|
1706
1723
|
|
|
1707
|
-
Log.d("OmikitPlugin", "🔐 Requesting permissions for codes ${permissionCodes.joinToString()}: ${permissionsToRequest.joinToString()}")
|
|
1708
|
-
|
|
1709
1724
|
ActivityCompat.requestPermissions(
|
|
1710
1725
|
reactApplicationContext.currentActivity!!,
|
|
1711
1726
|
permissionsToRequest.toTypedArray(),
|
|
@@ -1713,7 +1728,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1713
1728
|
)
|
|
1714
1729
|
|
|
1715
1730
|
} catch (e: Exception) {
|
|
1716
|
-
Log.e("OmikitPlugin", "❌ Error requesting permissions by codes: ${e.message}", e)
|
|
1717
1731
|
promise.reject("ERROR_PERMISSION_REQUEST", "Failed to request permissions: ${e.message}")
|
|
1718
1732
|
}
|
|
1719
1733
|
}
|
|
@@ -1722,12 +1736,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1722
1736
|
val missingPermissions = getMissingPermissions(isVideo)
|
|
1723
1737
|
|
|
1724
1738
|
if (missingPermissions.isEmpty()) {
|
|
1725
|
-
Log.d("OmikitPlugin", "✅ All permissions already granted")
|
|
1726
1739
|
return
|
|
1727
1740
|
}
|
|
1728
1741
|
|
|
1729
|
-
Log.d("OmikitPlugin", "📋 Requesting missing permissions for Android ${Build.VERSION.SDK_INT}: ${missingPermissions.joinToString()}")
|
|
1730
|
-
|
|
1731
1742
|
ActivityCompat.requestPermissions(
|
|
1732
1743
|
reactApplicationContext.currentActivity!!,
|
|
1733
1744
|
missingPermissions.toTypedArray(),
|
|
@@ -1735,7 +1746,16 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1735
1746
|
)
|
|
1736
1747
|
}
|
|
1737
1748
|
|
|
1738
|
-
override fun onActivityResult(p0: Activity?,
|
|
1749
|
+
override fun onActivityResult(p0: Activity?, requestCode: Int, resultCode: Int, p3: Intent?) {
|
|
1750
|
+
if (requestCode == REQUEST_OVERLAY_PERMISSION_CODE) {
|
|
1751
|
+
val granted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
1752
|
+
Settings.canDrawOverlays(reactApplicationContext)
|
|
1753
|
+
} else {
|
|
1754
|
+
true
|
|
1755
|
+
}
|
|
1756
|
+
permissionPromise?.resolve(granted)
|
|
1757
|
+
permissionPromise = null
|
|
1758
|
+
}
|
|
1739
1759
|
}
|
|
1740
1760
|
|
|
1741
1761
|
override fun onNewIntent(p0: Intent?) {
|
|
@@ -1760,16 +1780,10 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1760
1780
|
}
|
|
1761
1781
|
}
|
|
1762
1782
|
|
|
1763
|
-
//
|
|
1783
|
+
// Auto-unregister listener - only handles onAutoUnregisterStatus
|
|
1784
|
+
// Other callbacks are NO-OP to prevent double-firing events (Fix #1)
|
|
1764
1785
|
private val autoUnregisterListener = object : OmiListener {
|
|
1765
1786
|
override fun onAutoUnregisterStatus(isScheduled: Boolean, timeUntilExecution: Long) {
|
|
1766
|
-
// ✅ Auto-unregister prevention removed - no longer supported in new SDK
|
|
1767
|
-
if (isScheduled && timeUntilExecution > 0 && timeUntilExecution < 3000) {
|
|
1768
|
-
Log.w("OmikitPlugin", "🚨 AUTO-UNREGISTER sắp thực hiện trong ${timeUntilExecution}ms - SDK tự xử lý")
|
|
1769
|
-
// preventAutoUnregisterCrash deprecated - SDK handles automatically
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
|
-
// ✅ Gửi event cho React Native
|
|
1773
1787
|
try {
|
|
1774
1788
|
val statusData = mapOf(
|
|
1775
1789
|
"isScheduled" to isScheduled,
|
|
@@ -1779,94 +1793,48 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1779
1793
|
val map = createSafeWritableMap(statusData)
|
|
1780
1794
|
sendEvent("AUTO_UNREGISTER_STATUS", map)
|
|
1781
1795
|
} catch (e: Exception) {
|
|
1782
|
-
Log.e("OmikitPlugin", "
|
|
1796
|
+
Log.e("OmikitPlugin", "Error sending AUTO_UNREGISTER_STATUS event: ${e.message}", e)
|
|
1783
1797
|
}
|
|
1784
1798
|
}
|
|
1785
|
-
|
|
1786
|
-
//
|
|
1787
|
-
override fun incomingReceived(callerId: Int?, phoneNumber: String?, isVideo: Boolean?) {
|
|
1788
|
-
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
override fun
|
|
1792
|
-
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
|
-
override fun
|
|
1796
|
-
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
override fun
|
|
1800
|
-
|
|
1801
|
-
}
|
|
1802
|
-
|
|
1803
|
-
override fun onDescriptionError() {
|
|
1804
|
-
this@OmikitPluginModule.onDescriptionError()
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
override fun onFcmReceived(uuid: String, userName: String, avatar: String) {
|
|
1808
|
-
this@OmikitPluginModule.onFcmReceived(uuid, userName, avatar)
|
|
1809
|
-
}
|
|
1810
|
-
|
|
1811
|
-
override fun onRinging(callerId: Int, transactionId: String?) {
|
|
1812
|
-
this@OmikitPluginModule.onRinging(callerId, transactionId)
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
override fun networkHealth(stat: Map<String, *>, quality: Int) {
|
|
1816
|
-
this@OmikitPluginModule.networkHealth(stat, quality)
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
override fun onAudioChanged(audioInfo: Map<String, Any>) {
|
|
1820
|
-
this@OmikitPluginModule.onAudioChanged(audioInfo)
|
|
1821
|
-
}
|
|
1822
|
-
|
|
1823
|
-
override fun onHold(isHold: Boolean) {
|
|
1824
|
-
this@OmikitPluginModule.onHold(isHold)
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
override fun onMuted(isMuted: Boolean) {
|
|
1828
|
-
this@OmikitPluginModule.onMuted(isMuted)
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
override fun onOutgoingStarted(callerId: Int, phoneNumber: String?, isVideo: Boolean?) {
|
|
1832
|
-
this@OmikitPluginModule.onOutgoingStarted(callerId, phoneNumber, isVideo)
|
|
1833
|
-
}
|
|
1834
|
-
|
|
1835
|
-
override fun onSwitchBoardAnswer(sip: String) {
|
|
1836
|
-
this@OmikitPluginModule.onSwitchBoardAnswer(sip)
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
|
-
override fun onRegisterCompleted(statusCode: Int) {
|
|
1840
|
-
this@OmikitPluginModule.onRegisterCompleted(statusCode)
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
override fun onRequestPermission(permissions: Array<String>) {
|
|
1844
|
-
this@OmikitPluginModule.onRequestPermission(permissions)
|
|
1845
|
-
}
|
|
1846
|
-
|
|
1847
|
-
override fun onVideoSize(width: Int, height: Int) {
|
|
1848
|
-
this@OmikitPluginModule.onVideoSize(width, height)
|
|
1849
|
-
}
|
|
1799
|
+
|
|
1800
|
+
// NO-OP: main module listener handles these - avoid double-firing
|
|
1801
|
+
override fun incomingReceived(callerId: Int?, phoneNumber: String?, isVideo: Boolean?) {}
|
|
1802
|
+
override fun onCallEstablished(callerId: Int, phoneNumber: String?, isVideo: Boolean?, startTime: Long, transactionId: String?) {}
|
|
1803
|
+
override fun onCallEnd(callInfo: MutableMap<String, Any?>, statusCode: Int) {}
|
|
1804
|
+
override fun onConnecting() {}
|
|
1805
|
+
override fun onDescriptionError() {}
|
|
1806
|
+
override fun onFcmReceived(uuid: String, userName: String, avatar: String) {}
|
|
1807
|
+
override fun onRinging(callerId: Int, transactionId: String?) {}
|
|
1808
|
+
override fun networkHealth(stat: Map<String, *>, quality: Int) {}
|
|
1809
|
+
override fun onAudioChanged(audioInfo: Map<String, Any>) {}
|
|
1810
|
+
override fun onHold(isHold: Boolean) {}
|
|
1811
|
+
override fun onMuted(isMuted: Boolean) {}
|
|
1812
|
+
override fun onOutgoingStarted(callerId: Int, phoneNumber: String?, isVideo: Boolean?) {}
|
|
1813
|
+
override fun onSwitchBoardAnswer(sip: String) {}
|
|
1814
|
+
override fun onRegisterCompleted(statusCode: Int) {}
|
|
1815
|
+
override fun onRequestPermission(permissions: Array<String>) {}
|
|
1816
|
+
override fun onVideoSize(width: Int, height: Int) {}
|
|
1850
1817
|
}
|
|
1851
1818
|
|
|
1852
1819
|
// ✅ Helper function để hide notification một cách an toàn
|
|
1853
1820
|
@ReactMethod
|
|
1854
1821
|
fun hideSystemNotificationSafely(promise: Promise) {
|
|
1855
1822
|
try {
|
|
1856
|
-
|
|
1857
|
-
|
|
1823
|
+
val context = reactApplicationContext ?: run {
|
|
1824
|
+
promise.resolve(false)
|
|
1825
|
+
return
|
|
1826
|
+
}
|
|
1827
|
+
// Delay to ensure registration completes before hiding
|
|
1828
|
+
mainScope.launch {
|
|
1858
1829
|
try {
|
|
1859
|
-
|
|
1860
|
-
OmiClient.getInstance(
|
|
1861
|
-
Log.d("OmikitPlugin", "✅ Successfully hidden system notification and unregistered")
|
|
1830
|
+
delay(2000)
|
|
1831
|
+
OmiClient.getInstance(context).hideSystemNotificationAndUnregister("Registration check completed")
|
|
1862
1832
|
promise.resolve(true)
|
|
1863
1833
|
} catch (e: Exception) {
|
|
1864
|
-
Log.e("OmikitPlugin", "❌ Failed to hide system notification: ${e.message}", e)
|
|
1865
1834
|
promise.resolve(false)
|
|
1866
1835
|
}
|
|
1867
|
-
}
|
|
1836
|
+
}
|
|
1868
1837
|
} catch (e: Exception) {
|
|
1869
|
-
Log.e("OmikitPlugin", "❌ Error in hideSystemNotificationSafely: ${e.message}", e)
|
|
1870
1838
|
promise.resolve(false)
|
|
1871
1839
|
}
|
|
1872
1840
|
}
|
|
@@ -1876,10 +1844,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1876
1844
|
fun hideSystemNotificationOnly(promise: Promise) {
|
|
1877
1845
|
try {
|
|
1878
1846
|
OmiClient.getInstance(reactApplicationContext!!).hideSystemNotification()
|
|
1879
|
-
Log.d("OmikitPlugin", "✅ Successfully hidden system notification (keeping registration)")
|
|
1880
1847
|
promise.resolve(true)
|
|
1881
1848
|
} catch (e: Exception) {
|
|
1882
|
-
Log.e("OmikitPlugin", "❌ Failed to hide system notification only: ${e.message}", e)
|
|
1883
1849
|
promise.resolve(false)
|
|
1884
1850
|
}
|
|
1885
1851
|
}
|
|
@@ -1889,10 +1855,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1889
1855
|
fun hideSystemNotificationAndUnregister(reason: String, promise: Promise) {
|
|
1890
1856
|
try {
|
|
1891
1857
|
OmiClient.getInstance(reactApplicationContext!!).hideSystemNotificationAndUnregister(reason)
|
|
1892
|
-
Log.d("OmikitPlugin", "✅ Successfully hidden notification and unregistered: $reason")
|
|
1893
1858
|
promise.resolve(true)
|
|
1894
1859
|
} catch (e: Exception) {
|
|
1895
|
-
Log.e("OmikitPlugin", "❌ Failed to hide notification and unregister: ${e.message}", e)
|
|
1896
1860
|
promise.resolve(false)
|
|
1897
1861
|
}
|
|
1898
1862
|
}
|
|
@@ -1904,21 +1868,18 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1904
1868
|
val userName = data.getString("userName")
|
|
1905
1869
|
val password = data.getString("password")
|
|
1906
1870
|
val realm = data.getString("realm")
|
|
1907
|
-
val host = data.getString("host")
|
|
1871
|
+
val host = data.getString("host").let { if (it.isNullOrEmpty()) "vh.omicrm.com" else it }
|
|
1908
1872
|
val firebaseToken = data.getString("fcmToken")
|
|
1909
1873
|
val projectId = data.getString("projectId") ?: ""
|
|
1910
1874
|
|
|
1911
1875
|
// Validate required parameters
|
|
1912
1876
|
if (userName.isNullOrEmpty() || password.isNullOrEmpty() || realm.isNullOrEmpty() || firebaseToken.isNullOrEmpty()) {
|
|
1913
|
-
Log.e("OmikitPlugin", "❌ Missing required parameters for credential check")
|
|
1914
1877
|
promise.resolve(mapOf("success" to false, "message" to "Missing required parameters"))
|
|
1915
1878
|
return@launch
|
|
1916
1879
|
}
|
|
1917
1880
|
|
|
1918
1881
|
withContext(Dispatchers.Default) {
|
|
1919
1882
|
try {
|
|
1920
|
-
Log.d("OmikitPlugin", "🔍 Checking credentials for user: $userName")
|
|
1921
|
-
|
|
1922
1883
|
OmiClient.getInstance(reactApplicationContext!!).checkCredentials(
|
|
1923
1884
|
userName = userName ?: "",
|
|
1924
1885
|
password = password ?: "",
|
|
@@ -1927,8 +1888,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1927
1888
|
host = host,
|
|
1928
1889
|
projectId = projectId
|
|
1929
1890
|
) { success, statusCode, message ->
|
|
1930
|
-
Log.d("OmikitPlugin", "🔍 Credential check callback - success: $success, status: $statusCode, message: $message")
|
|
1931
|
-
|
|
1932
1891
|
val result = mapOf(
|
|
1933
1892
|
"success" to success,
|
|
1934
1893
|
"statusCode" to statusCode,
|
|
@@ -1939,7 +1898,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1939
1898
|
}
|
|
1940
1899
|
|
|
1941
1900
|
} catch (e: Exception) {
|
|
1942
|
-
Log.e("OmikitPlugin", "❌ Error during credential check: ${e.message}", e)
|
|
1943
1901
|
val errorResult = mapOf(
|
|
1944
1902
|
"success" to false,
|
|
1945
1903
|
"message" to e.message
|
|
@@ -1957,7 +1915,7 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1957
1915
|
val userName = data.getString("userName")
|
|
1958
1916
|
val password = data.getString("password")
|
|
1959
1917
|
val realm = data.getString("realm")
|
|
1960
|
-
val host = data.getString("host")
|
|
1918
|
+
val host = data.getString("host").let { if (it.isNullOrEmpty()) "vh.omicrm.com" else it }
|
|
1961
1919
|
val isVideo = data.getBoolean("isVideo")
|
|
1962
1920
|
val firebaseToken = data.getString("fcmToken")
|
|
1963
1921
|
val projectId = data.getString("projectId") ?: ""
|
|
@@ -1966,15 +1924,12 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1966
1924
|
|
|
1967
1925
|
// Validate required parameters
|
|
1968
1926
|
if (userName.isNullOrEmpty() || password.isNullOrEmpty() || realm.isNullOrEmpty() || firebaseToken.isNullOrEmpty()) {
|
|
1969
|
-
Log.e("OmikitPlugin", "❌ Missing required parameters for registration with options")
|
|
1970
1927
|
promise.resolve(mapOf("success" to false, "message" to "Missing required parameters"))
|
|
1971
1928
|
return@launch
|
|
1972
1929
|
}
|
|
1973
1930
|
|
|
1974
1931
|
withContext(Dispatchers.Default) {
|
|
1975
1932
|
try {
|
|
1976
|
-
Log.d("OmikitPlugin", "⚙️ Registering with options for user: $userName - showNotification: $showNotification, enableAutoUnregister: $enableAutoUnregister")
|
|
1977
|
-
|
|
1978
1933
|
OmiClient.getInstance(reactApplicationContext!!).registerWithOptions(
|
|
1979
1934
|
userName = userName ?: "",
|
|
1980
1935
|
password = password ?: "",
|
|
@@ -1986,8 +1941,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1986
1941
|
showNotification = showNotification,
|
|
1987
1942
|
enableAutoUnregister = enableAutoUnregister
|
|
1988
1943
|
) { success, statusCode, message ->
|
|
1989
|
-
Log.d("OmikitPlugin", "⚙️ Registration with options callback - success: $success, status: $statusCode, message: $message")
|
|
1990
|
-
|
|
1991
1944
|
val result = mapOf(
|
|
1992
1945
|
"success" to success,
|
|
1993
1946
|
"statusCode" to statusCode,
|
|
@@ -1998,7 +1951,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
|
|
|
1998
1951
|
}
|
|
1999
1952
|
|
|
2000
1953
|
} catch (e: Exception) {
|
|
2001
|
-
Log.e("OmikitPlugin", "❌ Error during registration with options: ${e.message}", e)
|
|
2002
1954
|
val errorResult = mapOf(
|
|
2003
1955
|
"success" to false,
|
|
2004
1956
|
"message" to e.message
|