omikit-plugin 3.3.10 → 3.3.14

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.
@@ -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.3.91"
125
+ api "io.omicrm.vihat:omi-sdk:2.3.94"
126
126
 
127
127
  implementation "com.facebook.react:react-native:+" // From node_modules
128
128
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
@@ -79,7 +79,7 @@ object OmiRegistrationStatus {
79
79
  }
80
80
 
81
81
  /**
82
- * Helper functions for parameter validation
82
+ * Helper functions for parameter validation and safe OmiClient access
83
83
  */
84
84
  object ValidationHelper {
85
85
  fun validateRequired(params: Map<String, String?>, promise: Promise): Boolean {
@@ -93,6 +93,24 @@ object ValidationHelper {
93
93
  }
94
94
  return true
95
95
  }
96
+
97
+ /**
98
+ * Safe OmiClient access to prevent crashes during service shutdown
99
+ */
100
+ fun safeOmiClientAccess(context: ReactApplicationContext, action: (OmiClient) -> Unit): Boolean {
101
+ return try {
102
+ val omiClient = OmiClient.getInstance(context)
103
+ if (omiClient != null) {
104
+ action(omiClient)
105
+ true
106
+ } else {
107
+ false
108
+ }
109
+ } catch (e: Exception) {
110
+ Log.e("OMISDK", "Error accessing OmiClient: ${e.message}")
111
+ false
112
+ }
113
+ }
96
114
  }
97
115
 
98
116
  class OmikitPluginModule(reactContext: ReactApplicationContext?) :
@@ -101,10 +119,60 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
101
119
  private var isIncoming: Boolean = false
102
120
  private var isAnswerCall: Boolean = false
103
121
  private var permissionPromise: Promise? = null
122
+
123
+ // Call state management to prevent concurrent calls
124
+ private var isCallInProgress: Boolean = false
125
+ private var lastCallTime: Long = 0
126
+ private val callCooldownMs: Long = 2000 // 2 seconds cooldown between calls
127
+ private val callStateLock = Any()
104
128
 
105
129
  override fun getName(): String {
106
130
  return NAME
107
131
  }
132
+
133
+ /**
134
+ * Check if we can start a new call (no concurrent calls, cooldown passed)
135
+ */
136
+ private fun canStartNewCall(): Boolean {
137
+ synchronized(callStateLock) {
138
+ val currentTime = System.currentTimeMillis()
139
+ val timeSinceLastCall = currentTime - lastCallTime
140
+
141
+ // Check if call is in progress or cooldown not passed
142
+ if (isCallInProgress) {
143
+ Log.d("OMISDK", "🚫 Call blocked: Call already in progress")
144
+ return false
145
+ }
146
+
147
+ if (timeSinceLastCall < callCooldownMs) {
148
+ Log.d("OMISDK", "🚫 Call blocked: Cooldown period (${callCooldownMs - timeSinceLastCall}ms remaining)")
149
+ return false
150
+ }
151
+
152
+ return true
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Mark call as started
158
+ */
159
+ private fun markCallStarted() {
160
+ synchronized(callStateLock) {
161
+ isCallInProgress = true
162
+ lastCallTime = System.currentTimeMillis()
163
+ Log.d("OMISDK", "📞 Call started, marking in progress")
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Mark call as ended
169
+ */
170
+ private fun markCallEnded() {
171
+ synchronized(callStateLock) {
172
+ isCallInProgress = false
173
+ Log.d("OMISDK", "📴 Call ended, clearing in progress flag")
174
+ }
175
+ }
108
176
 
109
177
 
110
178
  private val handler = Handler(Looper.getMainLooper())
@@ -166,6 +234,8 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
166
234
  // Reset call state variables
167
235
  isIncoming = false
168
236
  isAnswerCall = false
237
+ // Clear call progress state when remote party ends call
238
+ markCallEnded()
169
239
  Log.d("OMISDK", "=>> onCallEnd AFTER RESET - isIncoming: $isIncoming, isAnswerCall: $isAnswerCall")
170
240
 
171
241
  // Kiểm tra kiểu dữ liệu trước khi ép kiểu để tránh lỗi
@@ -851,35 +921,55 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
851
921
  Manifest.permission.RECORD_AUDIO
852
922
  )
853
923
  val map: WritableMap = WritableNativeMap()
924
+
925
+ // Check if we can start a new call first
926
+ if (!canStartNewCall()) {
927
+ map.putInt("status", 8) // HAVE_ANOTHER_CALL
928
+ map.putString("_id", "")
929
+ map.putString("message", messageCall(8) as String)
930
+ promise.resolve(map)
931
+ return
932
+ }
933
+
854
934
  if (audio == PackageManager.PERMISSION_GRANTED) {
855
935
  mainScope.launch {
856
936
  var callResult: OmiStartCallStatus? = null
857
- withContext(Dispatchers.Default) {
858
- try {
859
- val uuid = data.getString("usrUuid") as String
860
- val isVideo = data.getBoolean("isVideo")
861
-
862
- // Check if OmiClient instance and service are ready before making call
863
- val omiClient = OmiClient.getInstance(reactApplicationContext!!)
864
- if (omiClient == null) {
865
- callResult = null
866
- return@withContext
867
- }
868
-
937
+ try {
938
+ val uuid = data.getString("usrUuid") as String
939
+ val isVideo = data.getBoolean("isVideo")
940
+
941
+ // Mark call as started before making the actual call
942
+ markCallStarted()
943
+
944
+ // Check if OmiClient instance and service are ready before making call
945
+ val omiClient = OmiClient.getInstance(reactApplicationContext!!)
946
+ if (omiClient == null) {
947
+ callResult = null
948
+ markCallEnded() // Clean up state
949
+ } else {
869
950
  // Add small delay to ensure service is fully initialized
870
- kotlinx.coroutines.delay(100)
951
+ kotlinx.coroutines.delay(200) // Increased delay for better stability
871
952
 
953
+ // Call on main thread to avoid PJSIP thread registration issues
872
954
  callResult = omiClient.startCallWithUuid(uuid = uuid, isVideo = isVideo)
873
- } catch (e: IllegalStateException) {
874
- // Handle service not ready state
875
- callResult = null
876
- } catch (e: NullPointerException) {
877
- // Handle null pointer exceptions
878
- callResult = null
879
- } catch (e: Throwable) {
880
- // Handle any other exceptions
881
- callResult = null
955
+
956
+ // If call failed, mark as ended
957
+ if (callResult == null || callResult.ordinal <= 7) { // 0-7 are failure statuses
958
+ markCallEnded()
959
+ }
882
960
  }
961
+ } catch (e: IllegalStateException) {
962
+ // Handle service not ready state
963
+ callResult = null
964
+ markCallEnded()
965
+ } catch (e: NullPointerException) {
966
+ // Handle null pointer exceptions
967
+ callResult = null
968
+ markCallEnded()
969
+ } catch (e: Throwable) {
970
+ // Handle any other exceptions including PJSIP thread issues
971
+ callResult = null
972
+ markCallEnded()
883
973
  }
884
974
  var statusCalltemp = callResult?.ordinal ?: 8
885
975
  map.putInt("status", statusCalltemp)
@@ -922,11 +1012,15 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
922
1012
 
923
1013
  @ReactMethod
924
1014
  fun endCall(promise: Promise) {
925
- if (isIncoming && !isAnswerCall) {
926
- OmiClient.getInstance(reactApplicationContext!!).decline()
927
- } else {
928
- OmiClient.getInstance(reactApplicationContext!!).hangUp()
1015
+ ValidationHelper.safeOmiClientAccess(reactApplicationContext!!) { omiClient ->
1016
+ if (isIncoming && !isAnswerCall) {
1017
+ omiClient.decline()
1018
+ } else {
1019
+ omiClient.hangUp()
1020
+ }
929
1021
  }
1022
+ // Clear call state when ending call
1023
+ markCallEnded()
930
1024
  promise.resolve(true)
931
1025
  }
932
1026
 
@@ -935,15 +1029,17 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
935
1029
  Log.d("OMISDK", "➡️ rejectCall called - isIncoming: $isIncoming, isAnswerCall: $isAnswerCall")
936
1030
  if (isIncoming) {
937
1031
  Log.d("OMISDK", "📞 Incoming call")
938
-
939
- if (!isAnswerCall) {
940
- Log.d("OMISDK", "🚫 Declining call with declineWithCode(true)")
941
- OmiClient.getInstance(reactApplicationContext!!).declineWithCode(true) // 486 Busy Here
942
- } else {
943
- Log.d("OMISDK", "📴 Call already answered, hanging up")
944
- OmiClient.getInstance(reactApplicationContext!!).hangUp()
1032
+ ValidationHelper.safeOmiClientAccess(reactApplicationContext!!) { omiClient ->
1033
+ if (!isAnswerCall) {
1034
+ Log.d("OMISDK", "🚫 Declining call with declineWithCode(true)")
1035
+ omiClient.declineWithCode(true) // 486 Busy Here
1036
+ } else {
1037
+ Log.d("OMISDK", "📴 Call already answered, hanging up")
1038
+ omiClient.hangUp()
1039
+ }
945
1040
  }
946
-
1041
+ // Clear call state when rejecting call
1042
+ markCallEnded()
947
1043
  promise.resolve(true)
948
1044
  } else {
949
1045
  Log.d("OMISDK", "📤 Not incoming call, skipping reject")
@@ -953,11 +1049,15 @@ class OmikitPluginModule(reactContext: ReactApplicationContext?) :
953
1049
 
954
1050
  @ReactMethod
955
1051
  fun dropCall(promise: Promise) {
956
- if (isIncoming && !isAnswerCall) {
957
- OmiClient.getInstance(reactApplicationContext!!).declineWithCode(false) // 603
958
- } else {
959
- OmiClient.getInstance(reactApplicationContext!!).hangUp()
1052
+ ValidationHelper.safeOmiClientAccess(reactApplicationContext!!) { omiClient ->
1053
+ if (isIncoming && !isAnswerCall) {
1054
+ omiClient.declineWithCode(false) // 603
1055
+ } else {
1056
+ omiClient.hangUp()
1057
+ }
960
1058
  }
1059
+ // Clear call state when dropping call
1060
+ markCallEnded()
961
1061
  promise.resolve(true)
962
1062
  }
963
1063
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omikit-plugin",
3
- "version": "3.3.10",
3
+ "version": "3.3.14",
4
4
  "description": "Omikit Plugin by ViHAT",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",