omikit-plugin 3.3.19 → 3.3.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -235,7 +235,6 @@ public class MainActivity extends ReactActivity {
235
235
 
236
236
  ### ✅ For React Native > 0.74
237
237
 
238
-
239
238
  ```kotlin
240
239
  class MainActivity : ReactActivity() {
241
240
  // your config ....
@@ -243,6 +242,14 @@ class MainActivity : ReactActivity() {
243
242
  override fun onCreate(savedInstanceState: Bundle?) {
244
243
  super.onCreate(savedInstanceState)
245
244
 
245
+ intent?.let { intentData ->
246
+ try {
247
+ OmikitPluginModule.Companion.handlePickupIntentEarly(this, intentData)
248
+ } catch (e: Exception) {
249
+ Log.e("MainActivity", "⚠️ PICKUP-FIX: Error handling early intent: ${e.message}")
250
+ }
251
+ }
252
+
246
253
  val reactInstanceManager: ReactInstanceManager = reactNativeHost.reactInstanceManager
247
254
  val currentContext = reactInstanceManager.currentReactContext
248
255
  if (currentContext != null && currentContext is ReactApplicationContext) {
@@ -263,21 +270,31 @@ class MainActivity : ReactActivity() {
263
270
  }
264
271
 
265
272
  override fun onNewIntent(intent: Intent?) {
266
- super.onNewIntent(intent)
267
- if (intent != null) {
268
- reactApplicationContext?.let {
269
- OmikitPluginModule.Companion.onGetIntentFromNotification(it, intent, this)
270
- } ?: Log.e("MainActivity", "ReactApplicationContext has not been initialized in onNewIntent.")
271
- } else {
272
- Log.e("MainActivity", "Intent in onNewIntent is null.")
273
- }
273
+ super.onNewIntent(intent)
274
+ intent?.let { newIntent ->
275
+ Log.d("MainActivity", "🚀 PICKUP-FIX: New intent received (warm start)")
276
+ // IMPORTANT: Update the activity's intent to the new one
277
+ setIntent(newIntent)
278
+ try {
279
+ // Try to handle immediately if React context is ready
280
+ reactApplicationContext?.let {
281
+ OmikitPluginModule.Companion.onGetIntentFromNotification(it, newIntent, this)
282
+ } ?: run {
283
+ OmikitPluginModule.Companion.handlePickupIntentEarly(this, newIntent)
284
+ }
285
+ } catch (e: Exception) {
286
+ Log.e("MainActivity", "❌ PICKUP-FIX: Error in onNewIntent: ${e.message}")
287
+ }
288
+ } ?: Log.e("MainActivity", "Intent in onNewIntent is null.")
274
289
  }
290
+
275
291
  override fun onResume() {
276
292
  super.onResume()
277
- reactApplicationContext?.let {
293
+ reactApplicationContext?.let { context ->
278
294
  OmikitPluginModule.Companion.onResume(this)
279
- intent?.let { intent ->
280
- OmikitPluginModule.Companion.onGetIntentFromNotification(it, intent, this)
295
+ // Handle intent if exists (already updated by onNewIntent or from onCreate)
296
+ intent?.let { intentData ->
297
+ OmikitPluginModule.Companion.onGetIntentFromNotification(context, intentData, this)
281
298
  }
282
299
  } ?: Log.e("MainActivity", "ReactApplicationContext has not been initialized in onResume.")
283
300
  }
@@ -122,7 +122,7 @@ dependencies {
122
122
  implementation("androidx.work:work-runtime:2.8.1")
123
123
  implementation "androidx.security:security-crypto:1.1.0-alpha06"
124
124
  // api 'vn.vihat.omicall:omi-sdk:2.3.23'
125
- api "io.omicrm.vihat:omi-sdk:2.4.3"
125
+ api "io.omicrm.vihat:omi-sdk:2.4.14"
126
126
 
127
127
  implementation "com.facebook.react:react-native:+" // From node_modules
128
128
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
@@ -25,6 +25,8 @@ import kotlinx.coroutines.Dispatchers
25
25
  import kotlinx.coroutines.launch
26
26
  import kotlinx.coroutines.withContext
27
27
  import kotlinx.coroutines.delay
28
+ import kotlinx.coroutines.sync.Mutex
29
+ import kotlinx.coroutines.sync.withLock
28
30
  import vn.vihat.omicall.omisdk.OmiAccountListener
29
31
  import vn.vihat.omicall.omisdk.OmiClient
30
32
  import vn.vihat.omicall.omisdk.OmiListener
@@ -125,6 +127,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
125
127
  private var lastCallTime: Long = 0
126
128
  private val callCooldownMs: Long = 2000 // 2 seconds cooldown between calls
127
129
  private val callStateLock = Any()
130
+
131
+ // Mutex for thread-safe OmiClient operations
132
+ private val omiClientMutex = Mutex()
128
133
 
129
134
  override fun getName(): String {
130
135
  return NAME
@@ -760,6 +765,7 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
760
765
 
761
766
  @ReactMethod
762
767
  fun initCallWithApiKey(data: ReadableMap, promise: Promise) {
768
+ Log.d("OmikitPlugin", "🔑 initCallWithApiKey called")
763
769
  mainScope.launch {
764
770
  var loginResult = false
765
771
  val usrName = data.getString("fullName") ?: ""
@@ -769,47 +775,78 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
769
775
  val phone = data.getString("phone")
770
776
  val firebaseToken = data.getString("fcmToken") ?: ""
771
777
  val projectId = data.getString("projectId") ?: ""
778
+
779
+ Log.d("OmikitPlugin", "🔑 Parameters - usrName: $usrName, usrUuid: $usrUuid, isVideo: $isVideo")
772
780
 
773
781
  withContext(Dispatchers.Default) {
774
782
  try {
783
+ Log.d("OmikitPlugin", "🔑 Starting validation")
775
784
  // Validate required parameters
776
785
  if (!ValidationHelper.validateRequired(mapOf(
777
786
  "fullName" to usrName,
778
787
  "usrUuid" to usrUuid,
779
788
  "apiKey" to apiKey,
780
789
  "fcmToken" to firebaseToken
781
- ), promise)) return@withContext
790
+ ), promise)) {
791
+ Log.e("OmikitPlugin", "❌ Validation failed")
792
+ return@withContext
793
+ }
782
794
 
783
- //Cleanup trước khi register
795
+ Log.d("OmikitPlugin", "Validation passed")
796
+
797
+ // Check RECORD_AUDIO permission for Android 14+
798
+ val hasRecordAudio = ContextCompat.checkSelfPermission(
799
+ reactApplicationContext,
800
+ Manifest.permission.RECORD_AUDIO
801
+ ) == PackageManager.PERMISSION_GRANTED
802
+
803
+ if (!hasRecordAudio) {
804
+ Log.e("OmikitPlugin", "❌ RECORD_AUDIO permission is required for Android 14+")
805
+ promise.resolve(false)
806
+ return@withContext
807
+ }
808
+
809
+ Log.d("OmikitPlugin", "✅ RECORD_AUDIO permission granted")
810
+
811
+ // ✅ Cleanup trước khi register với mutex
784
812
  try {
785
- OmiClient.getInstance(reactApplicationContext!!).logout()
786
- delay(500) // Chờ cleanup hoàn tất
813
+ Log.d("OmikitPlugin", "🧹 Starting cleanup")
814
+ omiClientMutex.withLock {
815
+ OmiClient.getInstance(reactApplicationContext!!).logout()
816
+ }
817
+ delay(1000) // Chờ cleanup hoàn tất
818
+ Log.d("OmikitPlugin", "✅ Cleanup completed")
787
819
  } catch (e: Exception) {
788
820
  Log.w("OmikitPlugin", "⚠️ Cleanup warning (expected): ${e.message}")
789
821
  }
790
822
 
791
823
  Log.d("OmikitPlugin", "🔑 Using API key registration for user: $usrName")
792
824
 
793
- loginResult = OmiClient.registerWithApiKey(
794
- apiKey ?: "",
795
- usrName ?: "",
796
- usrUuid ?: "",
797
- phone ?: "",
798
- isVideo,
799
- firebaseToken,
800
- projectId
801
- )
825
+ Log.d("OmikitPlugin", "🔑 Calling OmiClient.registerWithApiKey...")
826
+ omiClientMutex.withLock {
827
+ loginResult = OmiClient.registerWithApiKey(
828
+ apiKey ?: "",
829
+ usrName ?: "",
830
+ usrUuid ?: "",
831
+ phone ?: "",
832
+ isVideo,
833
+ firebaseToken,
834
+ projectId
835
+ )
836
+ }
837
+
838
+ Log.d("OmikitPlugin", "🔑 OmiClient.registerWithApiKey returned: $loginResult")
802
839
 
803
840
  if (loginResult) {
804
841
  Log.d("OmikitPlugin", "✅ API key registration successful")
805
842
  promise.resolve(true)
806
843
  } else {
807
844
  Log.e("OmikitPlugin", "❌ API key registration failed")
808
- promise.reject("ERROR_API_KEY_REGISTRATION_FAILED", "OMICALL API key initialization failed. Please check your API key, UUID, and network connection.")
845
+ promise.resolve(false)
809
846
  }
810
847
  } catch (e: Exception) {
811
848
  Log.e("OmikitPlugin", "❌ Error during API key registration: ${e.message}", e)
812
- promise.reject("ERROR_API_KEY_INITIALIZATION_EXCEPTION", "OMICALL API key initialization failed due to an unexpected error: ${e.message}. Please check your configuration and network connection.", e)
849
+ promise.resolve(false)
813
850
  }
814
851
  }
815
852
  }
@@ -832,8 +869,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
832
869
  } else {
833
870
  mainScope.launch {
834
871
  Log.d("getInitialCall RN", "🔄 Retrying in 2s... (Attempts left: $counter)")
835
- delay(1000) // Chờ 2 giây
836
- getInitialCall(counter - 1, promise) // Gọi lại hàm đệ quy
872
+ delay(1000) // Wait 2 seconds
873
+ getInitialCall(counter - 1, promise) // Retry recursively
837
874
  }
838
875
  }
839
876
  return
@@ -846,6 +883,26 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
846
883
  }
847
884
 
848
885
  val typeNumber = OmiKitUtils().checkTypeNumber(phoneNumber ?: "")
886
+ val statusPendingCall = OmiKitUtils().getStatusPendingCall(context)
887
+
888
+ // 🔥 CRITICAL FIX: Auto-answer if user already clicked pickup button
889
+ // statusPendingCall: 0 = no action, 2 = incoming (opened notification), 5 = accepted (clicked pickup)
890
+ val shouldAutoAnswer = call.direction == "inbound" &&
891
+ !call.isAccepted &&
892
+ call.state == 3 &&
893
+ statusPendingCall == 5 // 5 = User clicked pickup (CONFIRMED)
894
+
895
+ if (shouldAutoAnswer) {
896
+ Log.d("getInitialCall RN", "🚀 AUTO-ANSWER: User clicked pickup (statusPendingCall=$statusPendingCall), answering call immediately")
897
+ try {
898
+ OmiClient.getInstance(context).pickUp()
899
+ Log.d("getInitialCall RN", "✅ AUTO-ANSWER: Call answered successfully")
900
+
901
+ // Status already cleared by getStatusPendingCall()
902
+ } catch (e: Exception) {
903
+ Log.e("getInitialCall RN", "❌ AUTO-ANSWER: Failed to answer call: ${e.message}", e)
904
+ }
905
+ }
849
906
 
850
907
  val map: WritableMap = WritableNativeMap().apply {
851
908
  putString("callerNumber", phoneNumber)
@@ -855,9 +912,9 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
855
912
  putBoolean("muted", false)
856
913
  putBoolean("isVideo", call.isVideo ?: false)
857
914
  putString("typeNumber", typeNumber)
915
+ putBoolean("autoAnswered", shouldAutoAnswer) // Add flag for RN side
858
916
  }
859
917
 
860
- val statusPendingCall = OmiKitUtils().getStatusPendingCall(context)
861
918
  if (call.state == 3 && statusPendingCall != 0) {
862
919
  call.state = statusPendingCall
863
920
  }
@@ -867,7 +924,6 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
867
924
  if (statusPendingCall == 2 && call.state != 5) {
868
925
  Log.d("getInitialCall RN", "🚀 Incoming Receive Triggered ($statusPendingCall)")
869
926
 
870
-
871
927
  val eventMap: WritableMap = WritableNativeMap().apply {
872
928
  putBoolean("isVideo", call.isVideo ?: false)
873
929
  putBoolean("incoming", true)
@@ -1327,6 +1383,47 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
1327
1383
  }
1328
1384
  }
1329
1385
 
1386
+ /**
1387
+ * 🔥 CRITICAL FIX: Handle pickup intent EARLY (before React Native ready)
1388
+ * This method can be called from onCreate/onNewIntent when React context is not ready yet
1389
+ * It will save the pickup state to SharedPreferences for later processing in getInitialCall()
1390
+ */
1391
+ fun handlePickupIntentEarly(act: Activity, intent: Intent) {
1392
+ try {
1393
+ val isIncoming = intent.getBooleanExtra(SipServiceConstants.ACTION_IS_INCOMING_CALL, false)
1394
+ if (!isIncoming) {
1395
+ Log.d("PICKUP-FIX", "Not an incoming call intent, skipping")
1396
+ return
1397
+ }
1398
+
1399
+ val isAcceptedCall = intent.getBooleanExtra(
1400
+ SipServiceConstants.ACTION_ACCEPT_INCOMING_CALL, false
1401
+ )
1402
+
1403
+ Log.d("PICKUP-FIX", "🚀 Early intent handler - isIncoming: $isIncoming, isAccepted: $isAcceptedCall")
1404
+
1405
+ // Save to SharedPreferences so getInitialCall() can detect it later
1406
+ // setStatusPendingCall(true) → saves status=5 (CONFIRMED)
1407
+ // setStatusPendingCall(false) → saves status=2 (INCOMING)
1408
+ OmiKitUtils().setStatusPendingCall(act, isAcceptedCall)
1409
+ Log.d("PICKUP-FIX", "✅ Saved pickup state to SharedPreferences (isAccepted=$isAcceptedCall)")
1410
+
1411
+ if (isAcceptedCall) {
1412
+ // Try to answer immediately if possible (may fail if SDK not ready)
1413
+ try {
1414
+ OmiClient.getInstance(act, true)?.let { client ->
1415
+ client.pickUp()
1416
+ Log.d("PICKUP-FIX", "✅ Successfully answered call immediately")
1417
+ } ?: Log.w("PICKUP-FIX", "⚠️ OmiClient not ready, will auto-answer in getInitialCall()")
1418
+ } catch (e: Exception) {
1419
+ Log.w("PICKUP-FIX", "⚠️ Cannot answer immediately (SDK not ready): ${e.message}. Will auto-answer in getInitialCall()")
1420
+ }
1421
+ }
1422
+ } catch (e: Exception) {
1423
+ Log.e("PICKUP-FIX", "❌ Error in handlePickupIntentEarly: ${e.message}", e)
1424
+ }
1425
+ }
1426
+
1330
1427
  fun onGetIntentFromNotification(
1331
1428
  context: ReactApplicationContext,
1332
1429
  intent: Intent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omikit-plugin",
3
- "version": "3.3.19",
3
+ "version": "3.3.21",
4
4
  "description": "Omikit Plugin by ViHAT",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",