@moxtradeveloper/react-native-moxo-module 10.11.2 → 10.13.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.
@@ -3,80 +3,54 @@ package com.moxomodule
3
3
  import android.app.Application
4
4
  import android.content.Intent
5
5
  import android.util.Log
6
- import com.facebook.react.bridge.*
7
- import com.facebook.react.modules.core.DeviceEventManagerModule
6
+ import com.facebook.react.bridge.Arguments
7
+ import com.facebook.react.bridge.Promise
8
+ import com.facebook.react.bridge.ReactApplicationContext
9
+ import com.facebook.react.bridge.ReadableMap
10
+ import com.facebook.react.bridge.ReadableType
11
+ import com.facebook.react.module.annotations.ReactModule
8
12
  import com.moxtra.mepsdk.MEPClient
9
13
  import com.moxtra.mepsdk.MEPClientDelegate
10
14
  import com.moxtra.mepsdk.data.MEPStartMeetOptions
11
15
  import com.moxtra.sdk.LinkConfig
12
16
  import com.moxtra.sdk.common.ActionListener
13
17
  import com.moxtra.sdk.common.ApiCallback
14
- import org.json.JSONArray
15
- import org.json.JSONException
16
- import org.json.JSONObject
17
18
 
18
- class MoxoModuleModule(reactContext: ReactApplicationContext) :
19
- ReactContextBaseJavaModule(reactContext) {
19
+ @ReactModule(name = MoxoModuleModule.NAME)
20
+ class MoxoModuleModule(private val reactContext: ReactApplicationContext) :
21
+ NativeMoxoModuleSpec(reactContext) {
20
22
 
21
- private val TAG: String = "MoxoModuleModule"
23
+ @Volatile private var tapWindowCloseEnabled = false
22
24
 
23
- private var mReactContext = reactContext
25
+ override fun getName(): String = NAME
24
26
 
25
- private val UNREAD_MSG_EVENT = "onUnreadMessageCountUpdated"
26
- private val TAP_WINDOW_CLOSE_EVENT = "tapWindowCloseEvent"
27
-
28
- override fun getName(): String {
29
- return NAME
30
- }
31
-
32
- @ReactMethod
33
- fun setup(domain: String?, options: String?) {
27
+ override fun setup(domain: String, options: ReadableMap?) {
34
28
  Log.d(TAG, "setup called with domain: $domain, options: $options")
35
- var linkConfig: LinkConfig? = null
36
- try {
37
- if (!domain.isNullOrEmpty() && !options.isNullOrEmpty()) {
38
- linkConfig = LinkConfig()
39
- val jsonObject = JSONObject(options)
40
- val certOrgName: String? = jsonObject.getString("certOrgName")
41
- val certPublicKey: String? = jsonObject.getString("certPublicKey")
42
- val ignoreBadCert: Boolean = jsonObject.getBoolean("ignoreBadCert")
43
- linkConfig.certOrganization = certOrgName
44
- linkConfig.certPublicKey = certPublicKey
45
- linkConfig.isIgnoreBadCert = ignoreBadCert
46
- }
47
- } catch (e: JSONException) {
29
+ val linkConfig = if (domain.isNotEmpty() && options != null) toLinkConfig(options) else null
48
30
 
49
- }
50
-
51
- mReactContext.runOnUiQueueThread {
52
- MEPClient.initialize(mReactContext?.applicationContext as Application)
31
+ runOnSdkThread {
32
+ MEPClient.initialize(reactContext.applicationContext as Application)
53
33
  MEPClient.setupDomain(domain, linkConfig)
34
+ registerClientListeners()
54
35
  }
55
36
  }
56
37
 
57
- @ReactMethod
58
- fun link(token: String?, promise: Promise?) {
59
- Log.d(TAG, "link called with token: $token")
60
- if (token.isNullOrEmpty()) {
61
- promise?.reject("-1", "token is empty or null")
38
+ override fun link(token: String, promise: Promise) {
39
+ Log.d(TAG, "link called")
40
+ if (token.isEmpty()) {
41
+ promise.reject("-1", "token is empty or null")
62
42
  return
63
43
  }
64
- mReactContext.runOnUiQueueThread {
44
+ runOnSdkThread(promise) {
65
45
  MEPClient.linkWithAccessToken(token, object : ApiCallback<Void?> {
66
- override fun onCompleted(rlt: Void?) {
67
- Log.d(
68
- TAG,
69
- "initWithAccessToken successful..."
70
- )
71
- promise?.resolve(true)
46
+ override fun onCompleted(result: Void?) {
47
+ Log.d(TAG, "linkWithAccessToken successful...")
48
+ promise.resolve(true)
72
49
  }
73
50
 
74
51
  override fun onError(errorCode: Int, errorMsg: String) {
75
- Log.d(
76
- TAG,
77
- "initWithAccessToken failed, errCode:$errorCode, errMsg:$errorMsg"
78
- )
79
- promise?.reject(
52
+ Log.d(TAG, "linkWithAccessToken failed, errCode:$errorCode, errMsg:$errorMsg")
53
+ promise.reject(
80
54
  errorCode.toString(),
81
55
  "initWithAccessToken failed, errCode:$errorCode, errMsg:$errorMsg"
82
56
  )
@@ -85,11 +59,10 @@ class MoxoModuleModule(reactContext: ReactApplicationContext) :
85
59
  }
86
60
  }
87
61
 
88
- @ReactMethod
89
- fun unlink(keepNotification: Boolean) {
62
+ override fun unlink(keepNotification: Boolean) {
90
63
  Log.d(TAG, "unlink called with keep notification $keepNotification")
91
- val callback: ApiCallback<Void?> = object : ApiCallback<Void?> {
92
- override fun onCompleted(rult: Void?) {
64
+ val callback = object : ApiCallback<Void?> {
65
+ override fun onCompleted(result: Void?) {
93
66
  Log.d(TAG, "unlink successful...")
94
67
  }
95
68
 
@@ -97,369 +70,284 @@ class MoxoModuleModule(reactContext: ReactApplicationContext) :
97
70
  Log.w(TAG, "unlink failed with error code $errorCode and error message $errorMsg ...")
98
71
  }
99
72
  }
100
- if (keepNotification) {
101
- MEPClient.localUnlink(callback)
102
- } else {
103
- MEPClient.unlink(callback)
73
+ runOnSdkThread {
74
+ if (keepNotification) {
75
+ MEPClient.localUnlink(callback)
76
+ } else {
77
+ MEPClient.unlink(callback)
78
+ }
104
79
  }
105
80
  }
106
81
 
107
- @ReactMethod
108
- fun getUnreadMessageCount(options: ReadableMap?, promise: Promise?) {
109
- var unreadCount = MEPClient.getUnreadMessageCount()
110
- promise?.resolve(unreadCount)
82
+ override fun getUnreadMessageCount(options: ReadableMap?, promise: Promise) {
83
+ runOnSdkThread(promise) {
84
+ promise.resolve(MEPClient.getUnreadMessageCount())
85
+ }
111
86
  }
112
87
 
113
- @ReactMethod
114
- fun showMEPWindow() {
88
+ override fun showMEPWindow() {
115
89
  Log.d(TAG, "showMEPWindow called...")
116
- if (MEPClient.isLinked()) {
117
- Log.d(TAG, "MEP is linked and showMEPWindow...")
118
- MEPClient.showMEPWindow(mReactContext?.baseContext)
90
+ runOnSdkThread {
91
+ if (MEPClient.isLinked()) {
92
+ MEPClient.showMEPWindow(reactContext.baseContext)
93
+ }
119
94
  }
120
95
  }
121
96
 
122
- @ReactMethod
123
- fun hideMEPWindow() {
97
+ override fun hideMEPWindow() {
124
98
  Log.d(TAG, "hideMEPWindow called...")
125
- if (MEPClient.isLinked()) {
126
- Log.d(TAG, "MEP is linked and hideMEPWindow...")
127
- MEPClient.destroyMEPWindow()
99
+ runOnSdkThread {
100
+ if (MEPClient.isLinked()) {
101
+ MEPClient.destroyMEPWindow()
102
+ }
128
103
  }
129
104
  }
130
105
 
131
- @ReactMethod
132
- fun openChat(chatId: String?, feedSequence: Double?, promise: Promise?) {
106
+ override fun openChat(chatId: String, feedSequence: Double?, promise: Promise) {
133
107
  Log.d(TAG, "openChat called...")
134
- if (!MEPClient.isLinked()) {
135
- promise?.reject("-1", "Not linked")
136
- return
137
- }
138
- if (!chatId.isNullOrEmpty()) {
108
+ runOnSdkThread(promise) {
109
+ if (!MEPClient.isLinked()) {
110
+ promise.reject("-1", "Not linked")
111
+ return@runOnSdkThread
112
+ }
113
+ if (chatId.isEmpty()) {
114
+ promise.reject("-1", "Invalid parameter")
115
+ return@runOnSdkThread
116
+ }
139
117
  MEPClient.openChat(chatId, feedSequence?.toLong() ?: 0, object : ApiCallback<Void?> {
140
- override fun onCompleted(rult: Void?) {
118
+ override fun onCompleted(result: Void?) {
141
119
  Log.d(TAG, "openChat successful...")
142
- promise?.resolve(true)
120
+ promise.resolve(true)
143
121
  }
144
122
 
145
123
  override fun onError(errorCode: Int, errorMsg: String) {
146
- promise?.reject(
147
- errorCode.toString(),
148
- "openChat failed, errCode:$errorCode, errMsg:$errorMsg"
149
- )
124
+ promise.reject(errorCode.toString(), "openChat failed, errCode:$errorCode, errMsg:$errorMsg")
150
125
  }
151
126
  })
152
- } else {
153
- promise?.reject("-1", "Invalid parameter")
154
127
  }
155
128
  }
156
129
 
157
- @ReactMethod
158
- fun openRelation(options: ReadableMap?, promise: Promise?) {
130
+ override fun openRelation(options: ReadableMap, promise: Promise) {
159
131
  Log.d(TAG, "openRelation called...")
160
- if (!MEPClient.isLinked()) {
161
- promise?.reject("-1", "Not linked")
162
- return
163
- }
164
- val uniqueId: String? = options?.getString("uniqueId")
165
- val email: String? = options?.getString("email")
166
- val callback: ApiCallback<Void?> = object : ApiCallback<Void?> {
167
- override fun onCompleted(rult: Void?) {
132
+ val uniqueId = options.stringOrNull("uniqueId")
133
+ val email = options.stringOrNull("email")
134
+ val callback = object : ApiCallback<Void?> {
135
+ override fun onCompleted(result: Void?) {
168
136
  Log.d(TAG, "openRelationChat successful...")
169
- promise?.resolve(true)
137
+ promise.resolve(true)
170
138
  }
171
139
 
172
140
  override fun onError(errorCode: Int, errorMsg: String) {
173
- promise?.reject(
141
+ promise.reject(
174
142
  errorCode.toString(),
175
143
  "openRelationChat failed, errCode:$errorCode, errMsg:$errorMsg"
176
144
  )
177
145
  }
178
146
  }
179
- if (!uniqueId.isNullOrEmpty()) {
180
- MEPClient.openRelationChat(uniqueId, callback)
181
- } else if (!email.isNullOrEmpty()) {
182
- MEPClient.openRelationChatWithEmail(email, callback)
183
- } else {
184
- promise?.reject("-1", "Invalid parameter")
185
- }
186
- }
187
-
188
- private fun onUnreadMessageCountUpdated() {
189
- Log.d(TAG, "onUnreadMessageCountUpdated called...")
190
- val listener: MEPClientDelegate.OnUnreadMessageListener =
191
- MEPClientDelegate.OnUnreadMessageListener { unreadCount ->
192
- Log.d(TAG, "onUnreadMessageCountUpdated, count is $unreadCount")
193
- sendEvent(mReactContext, UNREAD_MSG_EVENT, unreadCount)
147
+ runOnSdkThread(promise) {
148
+ if (!MEPClient.isLinked()) {
149
+ promise.reject("-1", "Not linked")
150
+ return@runOnSdkThread
194
151
  }
195
-
196
- MEPClient.getClientDelegate().setOnUnreadMessageListener(listener)
197
- }
198
-
199
- private fun onTapWindowClose() {
200
- Log.d(TAG, "onTapWindowClose called...")
201
- val listener: ActionListener<Void> = ActionListener { _, _ ->
202
- run {
203
- Log.d(TAG, "onTapWindowClose button clicked...")
204
- sendEvent(mReactContext, TAP_WINDOW_CLOSE_EVENT, null)
152
+ when {
153
+ !uniqueId.isNullOrEmpty() -> MEPClient.openRelationChat(uniqueId, callback)
154
+ !email.isNullOrEmpty() -> MEPClient.openRelationChatWithEmail(email, callback)
155
+ else -> promise.reject("-1", "Invalid parameter")
205
156
  }
206
157
  }
207
- MEPClient.getClientDelegate().setOnCloseButtonListener(listener)
208
- }
209
-
210
- private fun sendEvent(reactContext: ReactContext, eventName: String, params: Any?) {
211
- reactContext
212
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
213
- .emit(eventName, params)
214
158
  }
215
159
 
216
- @ReactMethod
217
- fun addListener(eventName: String) {
218
- Log.d(TAG, "addListener called...")
219
- when (eventName) {
220
- TAP_WINDOW_CLOSE_EVENT ->
221
- onTapWindowClose()
160
+ override fun registerNotification(deviceToken: String, promise: Promise) {
161
+ Log.d(TAG, "registerNotification called and token is " + if (deviceToken.isEmpty()) "empty" else "not empty")
162
+ if (deviceToken.isEmpty()) {
163
+ promise.reject("-1", "device token is empty")
164
+ return
222
165
  }
223
- }
224
-
225
- @ReactMethod
226
- fun removeListeners(count: Integer) {
227
- Log.d(TAG, "removeListeners called...")
228
- }
229
-
230
- @ReactMethod
231
- fun registerNotification(deviceToken: String?) {
232
- Log.d(
233
- TAG,
234
- "registerNotification called and token is " + (if (deviceToken == null) "null" else "not null")
235
- )
236
- if (!deviceToken.isNullOrEmpty()) {
237
- MEPClient.registerNotification(
238
- deviceToken,
239
- null, null, null,
240
- object : ApiCallback<Void> {
241
- override fun onCompleted(rlt: Void?) {
166
+ runOnSdkThread(promise) {
167
+ MEPClient.registerNotification(deviceToken, null, null, null, object : ApiCallback<Void> {
168
+ override fun onCompleted(result: Void?) {
242
169
  Log.d(TAG, "registerNotification called and completed...")
170
+ promise.resolve(true)
243
171
  }
244
172
 
245
173
  override fun onError(errorCode: Int, errorMsg: String?) {
246
174
  Log.w(TAG, "registerNotification called but failed with $errorCode and $errorMsg ...")
175
+ promise.reject(errorCode.toString(), "registerNotification failed, errCode:$errorCode, errMsg:$errorMsg")
247
176
  }
248
177
  })
249
178
  }
250
179
  }
251
180
 
252
- @ReactMethod
253
- fun parseNotification(payload: ReadableMap?, promise: Promise?) {
254
- Log.d(
255
- TAG,
256
- "parseNotification called and payload is " + (payload ?: "null")
257
- )
258
- if (payload == null) {
259
- promise?.reject("-1", "payload data is null or empty")
260
- return
261
- }
262
- try {
263
- var intent = map2Intent(payload)
264
- if (MEPClient.isMEPNotification(intent)) {
265
- MEPClient.parseRemoteNotification(
266
- intent,
267
- object : ApiCallback<Map<String, String>> {
268
- override fun onCompleted(rlt: Map<String, String>) {
269
- var jsonObject = JSONObject()
270
- var keys = rlt.keys
271
- for (key in keys) {
272
- when (key) {
273
- "chat_id", "feed_sequence", "session_id" -> jsonObject.put(key, rlt.getValue(key))
274
- "meet_id" -> {
275
- var meetId = rlt.getValue(key)
276
- jsonObject.put("session_id", meetId)
277
- }
278
-
279
- else -> continue
280
- }
281
- }
282
- promise?.resolve(json2WritableNativeMap(jsonObject))
181
+ override fun parseNotification(payload: ReadableMap, promise: Promise) {
182
+ Log.d(TAG, "parseNotification called and payload is $payload")
183
+ val intent = toIntent(payload)
184
+ runOnSdkThread(promise) {
185
+ if (!MEPClient.isMEPNotification(intent)) {
186
+ promise.reject("-1", "It is not moxo payload!")
187
+ return@runOnSdkThread
188
+ }
189
+ MEPClient.parseRemoteNotification(intent, object : ApiCallback<Map<String, String>> {
190
+ override fun onCompleted(result: Map<String, String>) {
191
+ val map = Arguments.createMap()
192
+ for ((key, value) in result) {
193
+ when (key) {
194
+ "chat_id", "feed_sequence", "session_id" -> map.putString(key, value)
195
+ "meet_id" -> map.putString("session_id", value)
283
196
  }
197
+ }
198
+ promise.resolve(map)
199
+ }
284
200
 
285
- override fun onError(errorCode: Int, errorMsg: String?) {
286
- promise?.reject(
287
- errorCode.toString(),
288
- "parseRemoteNotification failed, errCode:$errorCode, errMsg:$errorMsg"
289
- )
290
- }
291
- })
292
- } else {
293
- promise?.reject("-1", "It is not moxo payload!")
294
- }
295
- } catch (e: JSONException) {
296
- e.printStackTrace()
297
- promise?.reject("-1", "Invalid parameter: Not fcm payload")
201
+ override fun onError(errorCode: Int, errorMsg: String?) {
202
+ promise.reject(
203
+ errorCode.toString(),
204
+ "parseRemoteNotification failed, errCode:$errorCode, errMsg:$errorMsg"
205
+ )
206
+ }
207
+ })
298
208
  }
299
209
  }
300
210
 
301
- @ReactMethod
302
- fun showMeetRinger(sessionId: String, promise: Promise?) {
211
+ override fun showMeetRinger(sessionId: String, promise: Promise) {
303
212
  Log.d(TAG, "show meet ringer called with session id $sessionId")
304
- if (!MEPClient.isLinked()) {
305
- promise?.reject("-1", "Not linked")
306
- return
307
- }
308
- if (sessionId.isEmpty()) {
309
- promise?.reject("-1", "session id is empty!")
310
- return
311
- }
312
- MEPClient.showMeetRinger(sessionId, object : ApiCallback<Void> {
313
- override fun onCompleted(rlt: Void?) {
314
- Log.d(TAG, "show meet ringer success ...")
315
- promise?.resolve("Show meet ringer success")
213
+ runOnSdkThread(promise) {
214
+ if (!MEPClient.isLinked()) {
215
+ promise.reject("-1", "Not linked")
216
+ return@runOnSdkThread
316
217
  }
317
-
318
- override fun onError(errorCode: Int, errorMsg: String?) {
319
- Log.w(TAG, "show meet ringer failed with $errorCode and $errorMsg ...")
320
- promise?.reject("-1", "Show meet ringer failed with $errorCode and $errorMsg")
218
+ if (sessionId.isEmpty()) {
219
+ promise.reject("-1", "session id is empty!")
220
+ return@runOnSdkThread
321
221
  }
322
- })
222
+ MEPClient.showMeetRinger(sessionId, object : ApiCallback<Void> {
223
+ override fun onCompleted(result: Void?) {
224
+ Log.d(TAG, "show meet ringer success ...")
225
+ promise.resolve("Show meet ringer success")
226
+ }
227
+
228
+ override fun onError(errorCode: Int, errorMsg: String?) {
229
+ Log.w(TAG, "show meet ringer failed with $errorCode and $errorMsg ...")
230
+ promise.reject("-1", "Show meet ringer failed with $errorCode and $errorMsg")
231
+ }
232
+ })
233
+ }
323
234
  }
324
235
 
325
- @ReactMethod
326
- fun changeLanguage(lang: String) {
327
- Log.d(TAG, "changeLanguage called with $lang ...")
328
- if (lang.isNotEmpty()) {
329
- MEPClient.changeLanguage(lang)
236
+ override fun changeLanguage(language: String) {
237
+ Log.d(TAG, "changeLanguage called with $language ...")
238
+ if (language.isEmpty()) {
239
+ return
240
+ }
241
+ runOnSdkThread {
242
+ MEPClient.changeLanguage(language)
330
243
  }
331
244
  }
332
245
 
333
- @ReactMethod
334
- fun startMeet(topic: String, options: ReadableMap?, promise: Promise?) {
335
- Log.d(TAG, "startMeet called with topic: $topic, options:" + options?.toString())
336
- if (topic.isNullOrEmpty()) {
337
- promise?.reject("-1", "Topic is empty!")
246
+ override fun startMeet(topic: String, options: ReadableMap?, promise: Promise) {
247
+ Log.d(TAG, "startMeet called with topic: $topic, options: $options")
248
+ if (topic.isEmpty()) {
249
+ promise.reject("-1", "Topic is empty!")
338
250
  return
339
251
  }
340
252
 
341
- var callback: ApiCallback<String> = object : ApiCallback<String> {
342
- override fun onCompleted(sessionId: String) {
343
- Log.d(TAG, "startMeet success ...")
344
- val jsonObject = JSONObject()
345
- with(jsonObject) {
346
- put("session_id", sessionId)
253
+ val meetOptions = MEPStartMeetOptions().apply { this.topic = topic }
254
+ if (options != null) {
255
+ options.stringOrNull("chat_id")?.let { meetOptions.chatID = it }
256
+ options.booleanOrNull("auto_join_audio")?.let { meetOptions.setAutoJoinAudio(it) }
257
+ options.booleanOrNull("auto_start_video")?.let { meetOptions.setAutoStartVideo(it) }
258
+ options.booleanOrNull("auto_recording")?.let { meetOptions.isAutoRecordingEnabled = it }
259
+ options.booleanOrNull("instant_call")?.let { meetOptions.setInstantCall(it) }
260
+ if (options.hasKey("unique_ids") && options.getType("unique_ids") == ReadableType.Array) {
261
+ val ids = options.getArray("unique_ids")
262
+ if (ids != null) {
263
+ meetOptions.uniqueIDs = ArrayList((0 until ids.size()).mapNotNull { ids.getString(it) })
347
264
  }
348
- promise?.resolve(json2WritableNativeMap(jsonObject))
349
- }
350
-
351
- override fun onError(errorCode: Int, errorMsg: String?) {
352
- Log.w(TAG, "startMeet failed with $errorCode and $errorMsg ...")
353
- promise?.reject(errorCode.toString(), "startMeet failed with $errorCode and $errorMsg")
354
265
  }
355
266
  }
356
267
 
357
- //map to MEPStartMeetOptions
358
- var mepStartMeetOptions = MEPStartMeetOptions()
359
- mepStartMeetOptions.topic = topic
360
-
361
- var mapValues = options?.entryIterator;
362
- while (mapValues?.hasNext() == true) {
363
- var mapEntry = mapValues.next()
364
- when (mapEntry.key) {
365
- "chat_id" -> mepStartMeetOptions.chatID = mapEntry.value.toString()
366
- "auto_join_audio" -> mepStartMeetOptions.setAutoJoinAudio(mapEntry.value as Boolean)
367
- "auto_start_video" -> mepStartMeetOptions.setAutoStartVideo(mapEntry.value as Boolean)
368
- "auto_recording" -> mepStartMeetOptions.isAutoRecordingEnabled = mapEntry.value as Boolean
369
- "instant_call" -> mepStartMeetOptions.setInstantCall(mapEntry.value as Boolean)
370
- "unique_ids" -> {
371
- val ids = mapEntry.value
372
- if (ids is ReadableNativeArray) {
373
- ids.toArrayList().also { mepStartMeetOptions.uniqueIDs = it as ArrayList<String> }
374
- }
268
+ runOnSdkThread(promise) {
269
+ MEPClient.startMeet(meetOptions, object : ApiCallback<String> {
270
+ override fun onCompleted(sessionId: String) {
271
+ Log.d(TAG, "startMeet success ...")
272
+ promise.resolve(Arguments.createMap().apply { putString("session_id", sessionId) })
375
273
  }
376
- }
377
- }
378
274
 
379
- MEPClient.startMeet(mepStartMeetOptions, callback)
275
+ override fun onError(errorCode: Int, errorMsg: String?) {
276
+ Log.w(TAG, "startMeet failed with $errorCode and $errorMsg ...")
277
+ promise.reject(errorCode.toString(), "startMeet failed with $errorCode and $errorMsg")
278
+ }
279
+ })
280
+ }
380
281
  }
381
282
 
382
- private fun json2Intent(jsonObject: JSONObject?): Intent {
383
- var intent = Intent()
384
- var keys = jsonObject?.keys();
385
- while (keys?.hasNext() == true) {
386
- var key = keys.next()
387
- intent.putExtra(key, jsonObject?.getString(key))
283
+ override fun enableTapWindowCloseEvent() {
284
+ Log.d(TAG, "enableTapWindowCloseEvent called...")
285
+ tapWindowCloseEnabled = true
286
+ runOnSdkThread {
287
+ registerCloseButtonListener()
388
288
  }
389
- return intent
390
289
  }
391
290
 
392
- private fun map2Intent(readableMap: ReadableMap?): Intent {
393
- var intent = Intent()
394
- var mapValues = readableMap?.entryIterator;
395
- while (mapValues?.hasNext() == true) {
396
- var mapEntry = mapValues.next()
397
- when (mapEntry.value) {
398
- is Double -> intent.putExtra(mapEntry.key, mapEntry.value as Double)
399
- is String -> intent.putExtra(mapEntry.key, mapEntry.value as String)
400
- is Map<*, *> -> intent.putExtra(mapEntry.key, mapEntry.value as String)
401
- is ArrayList<*> -> intent.putExtra(mapEntry.key, mapEntry.value as ArrayList<*>)
291
+ // SDK calls share the queue that setup() initializes MEPClient on, so they
292
+ // always run after it. SDK exceptions are reported instead of crashing the app.
293
+ private fun runOnSdkThread(promise: Promise? = null, block: () -> Unit) {
294
+ reactContext.runOnUiQueueThread {
295
+ try {
296
+ block()
297
+ } catch (e: RuntimeException) {
298
+ Log.w(TAG, "MEPClient call failed", e)
299
+ promise?.reject("-1", e.message, e)
402
300
  }
403
301
  }
404
- return intent
405
302
  }
406
303
 
407
- private fun json2WritableNativeMap(jsonObject: JSONObject?): WritableNativeMap {
408
- val writableMap = WritableNativeMap()
409
- jsonObject?.let {
410
- for (key in jsonObject.keys()) {
411
- when (val value = jsonObject.get(key)) {
412
- is JSONObject -> {
413
- writableMap.putMap(key, json2WritableNativeMap(value))
414
- }
415
-
416
- is JSONArray -> {
417
- writableMap.putArray(key, json2WritableNativeArray(value))
418
- }
419
-
420
- is String -> {
421
- writableMap.putString(key, value)
422
- }
423
-
424
- is Boolean -> {
425
- writableMap.putBoolean(key, value)
426
- }
427
-
428
- is Double -> {
429
- writableMap.putDouble(key, value)
430
- }
431
-
432
- is Int -> {
433
- writableMap.putInt(key, value)
434
- }
435
-
436
- is Long -> {
437
- writableMap.putDouble(key, value.toDouble());
438
- }
439
-
440
- else -> {
441
- writableMap.putString(key, value.toString())
442
- }
443
- }
304
+ private fun registerClientListeners() {
305
+ MEPClient.getClientDelegate().setOnUnreadMessageListener(
306
+ MEPClientDelegate.OnUnreadMessageListener { unreadCount ->
307
+ Log.d(TAG, "onUnreadMessageCountUpdated, count is $unreadCount")
308
+ emitOnUnreadMessageCountUpdated(unreadCount.toDouble())
444
309
  }
310
+ )
311
+ if (tapWindowCloseEnabled) {
312
+ registerCloseButtonListener()
445
313
  }
446
- return writableMap
447
314
  }
448
315
 
449
- private fun json2WritableNativeArray(jsonArray: JSONArray?): WritableNativeArray {
450
- val writableArray = WritableNativeArray()
451
- jsonArray?.let {
452
- for (i in 0 until jsonArray.length()) {
453
- val jsonObject = jsonArray.get(i)
454
- if (jsonObject is JSONObject) {
455
- writableArray.pushMap(json2WritableNativeMap(jsonObject))
456
- }
316
+ // Setting this listener replaces the SDK's default close behavior, so it is
317
+ // only installed once JS subscribes to onTapWindowClose.
318
+ private fun registerCloseButtonListener() {
319
+ MEPClient.getClientDelegate().setOnCloseButtonListener(ActionListener<Void> { _, _ ->
320
+ Log.d(TAG, "onTapWindowClose button clicked...")
321
+ emitOnTapWindowClose()
322
+ })
323
+ }
324
+
325
+ private fun toLinkConfig(options: ReadableMap): LinkConfig = LinkConfig().apply {
326
+ options.stringOrNull("certOrgName")?.let { certOrganization = it }
327
+ options.stringOrNull("certPublicKey")?.let { certPublicKey = it }
328
+ options.booleanOrNull("ignoreBadCert")?.let { isIgnoreBadCert = it }
329
+ }
330
+
331
+ // Only top-level strings and numbers are passed on to the SDK.
332
+ private fun toIntent(payload: ReadableMap): Intent {
333
+ val intent = Intent()
334
+ for ((key, value) in payload.toHashMap()) {
335
+ when (value) {
336
+ is String -> intent.putExtra(key, value)
337
+ is Double -> intent.putExtra(key, value)
457
338
  }
458
339
  }
459
- return writableArray
340
+ return intent
460
341
  }
461
342
 
343
+ private fun ReadableMap.stringOrNull(key: String): String? =
344
+ if (hasKey(key) && getType(key) == ReadableType.String) getString(key) else null
345
+
346
+ private fun ReadableMap.booleanOrNull(key: String): Boolean? =
347
+ if (hasKey(key) && getType(key) == ReadableType.Boolean) getBoolean(key) else null
348
+
462
349
  companion object {
463
- const val NAME = "MoxoModule"
350
+ const val NAME = NativeMoxoModuleSpec.NAME
351
+ private const val TAG = "MoxoModuleModule"
464
352
  }
465
353
  }