react-native-acoustic-connect-beta 18.0.14 → 18.0.15

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.
Files changed (97) hide show
  1. package/Examples/SampleUI/.detoxrc.js +83 -0
  2. package/Examples/SampleUI/ConnectConfig.json +2 -2
  3. package/Examples/SampleUI/android/app/build.gradle +3 -0
  4. package/Examples/SampleUI/android/app/src/main/java/com/sampleui/MainActivity.kt +0 -35
  5. package/Examples/SampleUI/android/settings.gradle +1 -1
  6. package/Examples/SampleUI/package.json +8 -6
  7. package/Examples/SampleUI/scripts/integration-test-android.sh +292 -0
  8. package/Examples/SampleUI/src/Examples/DialogExample.tsx +88 -2
  9. package/Examples/SampleUI/src/Examples/Dialogs/DialogTrackingTest.tsx +307 -0
  10. package/Examples/SampleUI/src/Examples/Dialogs/index.tsx +37 -0
  11. package/Examples/SampleUI/src/index.native.tsx +4 -5
  12. package/android/build.gradle +2 -2
  13. package/android/src/main/assets/ConnectAdvancedConfig.json +1 -1
  14. package/android/src/main/java/com/acousticconnectrn/HybridAcousticConnectRN.kt +787 -490
  15. package/ios/HybridAcousticConnectRN.swift +75 -0
  16. package/lib/commonjs/TLTRN.js +69 -0
  17. package/lib/commonjs/TLTRN.js.map +1 -1
  18. package/lib/commonjs/components/Connect.js +5 -1
  19. package/lib/commonjs/components/Connect.js.map +1 -1
  20. package/lib/commonjs/docs/DialogTracking.md +252 -0
  21. package/lib/commonjs/docs/NativeImplementation.md +176 -0
  22. package/lib/commonjs/examples/DialogTrackingExample.js +175 -0
  23. package/lib/commonjs/examples/DialogTrackingExample.js.map +1 -0
  24. package/lib/commonjs/examples/HOCDialogExample.js +296 -0
  25. package/lib/commonjs/examples/HOCDialogExample.js.map +1 -0
  26. package/lib/commonjs/index.js +28 -0
  27. package/lib/commonjs/index.js.map +1 -1
  28. package/lib/commonjs/utils/DialogDebugger.js +216 -0
  29. package/lib/commonjs/utils/DialogDebugger.js.map +1 -0
  30. package/lib/commonjs/utils/DialogListener.js +203 -0
  31. package/lib/commonjs/utils/DialogListener.js.map +1 -0
  32. package/lib/commonjs/utils/useDialogTracking.js +107 -0
  33. package/lib/commonjs/utils/useDialogTracking.js.map +1 -0
  34. package/lib/commonjs/utils/withAcousticAutoDialog.js +282 -0
  35. package/lib/commonjs/utils/withAcousticAutoDialog.js.map +1 -0
  36. package/lib/module/TLTRN.js +69 -0
  37. package/lib/module/TLTRN.js.map +1 -1
  38. package/lib/module/components/Connect.js +5 -1
  39. package/lib/module/components/Connect.js.map +1 -1
  40. package/lib/module/docs/DialogTracking.md +252 -0
  41. package/lib/module/docs/NativeImplementation.md +176 -0
  42. package/lib/module/examples/DialogTrackingExample.js +172 -0
  43. package/lib/module/examples/DialogTrackingExample.js.map +1 -0
  44. package/lib/module/examples/HOCDialogExample.js +292 -0
  45. package/lib/module/examples/HOCDialogExample.js.map +1 -0
  46. package/lib/module/index.js +5 -1
  47. package/lib/module/index.js.map +1 -1
  48. package/lib/module/utils/DialogDebugger.js +211 -0
  49. package/lib/module/utils/DialogDebugger.js.map +1 -0
  50. package/lib/module/utils/DialogListener.js +199 -0
  51. package/lib/module/utils/DialogListener.js.map +1 -0
  52. package/lib/module/utils/useDialogTracking.js +102 -0
  53. package/lib/module/utils/useDialogTracking.js.map +1 -0
  54. package/lib/module/utils/withAcousticAutoDialog.js +275 -0
  55. package/lib/module/utils/withAcousticAutoDialog.js.map +1 -0
  56. package/lib/typescript/src/TLTRN.d.ts +7 -0
  57. package/lib/typescript/src/TLTRN.d.ts.map +1 -1
  58. package/lib/typescript/src/components/Connect.d.ts +1 -0
  59. package/lib/typescript/src/components/Connect.d.ts.map +1 -1
  60. package/lib/typescript/src/examples/DialogTrackingExample.d.ts +17 -0
  61. package/lib/typescript/src/examples/DialogTrackingExample.d.ts.map +1 -0
  62. package/lib/typescript/src/examples/HOCDialogExample.d.ts +21 -0
  63. package/lib/typescript/src/examples/HOCDialogExample.d.ts.map +1 -0
  64. package/lib/typescript/src/index.d.ts +5 -1
  65. package/lib/typescript/src/index.d.ts.map +1 -1
  66. package/lib/typescript/src/specs/react-native-acoustic-connect.nitro.d.ts +4 -0
  67. package/lib/typescript/src/specs/react-native-acoustic-connect.nitro.d.ts.map +1 -1
  68. package/lib/typescript/src/utils/DialogDebugger.d.ts +58 -0
  69. package/lib/typescript/src/utils/DialogDebugger.d.ts.map +1 -0
  70. package/lib/typescript/src/utils/DialogListener.d.ts +85 -0
  71. package/lib/typescript/src/utils/DialogListener.d.ts.map +1 -0
  72. package/lib/typescript/src/utils/useDialogTracking.d.ts +25 -0
  73. package/lib/typescript/src/utils/useDialogTracking.d.ts.map +1 -0
  74. package/lib/typescript/src/utils/withAcousticAutoDialog.d.ts +37 -0
  75. package/lib/typescript/src/utils/withAcousticAutoDialog.d.ts.map +1 -0
  76. package/nitrogen/generated/android/c++/JHybridAcousticConnectRNSpec.cpp +26 -0
  77. package/nitrogen/generated/android/c++/JHybridAcousticConnectRNSpec.hpp +4 -0
  78. package/nitrogen/generated/android/kotlin/com/margelo/nitro/acousticconnectrn/HybridAcousticConnectRNSpec.kt +16 -0
  79. package/nitrogen/generated/ios/c++/HybridAcousticConnectRNSpecSwift.hpp +32 -0
  80. package/nitrogen/generated/ios/swift/HybridAcousticConnectRNSpec.swift +4 -0
  81. package/nitrogen/generated/ios/swift/HybridAcousticConnectRNSpec_cxx.swift +71 -0
  82. package/nitrogen/generated/shared/c++/HybridAcousticConnectRNSpec.cpp +4 -0
  83. package/nitrogen/generated/shared/c++/HybridAcousticConnectRNSpec.hpp +4 -0
  84. package/package.json +1 -1
  85. package/scripts/ConnectConfig.json +1 -1
  86. package/src/TLTRN.ts +75 -0
  87. package/src/components/Connect.tsx +6 -1
  88. package/src/docs/DialogTracking.md +252 -0
  89. package/src/docs/NativeImplementation.md +176 -0
  90. package/src/examples/DialogTrackingExample.tsx +163 -0
  91. package/src/examples/HOCDialogExample.tsx +253 -0
  92. package/src/index.ts +5 -1
  93. package/src/specs/react-native-acoustic-connect.nitro.ts +5 -0
  94. package/src/utils/DialogDebugger.ts +224 -0
  95. package/src/utils/DialogListener.ts +238 -0
  96. package/src/utils/useDialogTracking.ts +102 -0
  97. package/src/utils/withAcousticAutoDialog.tsx +312 -0
@@ -13,16 +13,23 @@
13
13
  package com.acousticconnectrn
14
14
 
15
15
  import android.app.Activity
16
+ import android.app.AlertDialog
16
17
  import android.app.Application
18
+ import android.app.Dialog
17
19
  import android.content.Context
20
+ import android.content.ContextWrapper
21
+ import android.os.Build
18
22
  import android.os.Handler
19
23
  import android.os.Looper
20
24
  import android.text.TextUtils
25
+ import android.util.Log
21
26
  import android.view.View
22
27
  import android.view.View.OnFocusChangeListener
28
+ import android.view.WindowManager
23
29
  import android.view.inputmethod.InputMethodManager
24
30
  import android.widget.EditText
25
31
  import android.widget.TextView
32
+ import androidx.fragment.app.DialogFragment
26
33
  import com.acoustic.connect.android.connectmod.Connect
27
34
  import com.acoustic.connect.android.connectmod.Connect.TLF_ON_FOCUS_CHANGE_IN
28
35
  import com.acoustic.connect.android.connectmod.Connect.TLF_ON_FOCUS_CHANGE_OUT
@@ -46,470 +53,759 @@ import com.facebook.react.uimanager.NativeViewHierarchyManager
46
53
  import com.facebook.react.uimanager.UIManagerModule
47
54
  import com.ibm.eo.EOCore
48
55
  import com.ibm.eo.model.EOMonitoringLevel
49
- import com.margelo.nitro.NitroModules
56
+ import com.margelo.nitro.NitroModules.Companion.applicationContext
50
57
  import com.margelo.nitro.acousticconnectrn.HybridAcousticConnectRNSpec
51
58
  import com.margelo.nitro.acousticconnectrn.Variant_String_Double_Boolean
52
59
  import com.tl.uic.Tealeaf
53
60
  import com.tl.uic.model.ScreenviewType
61
+ import com.tl.uic.util.DialogUtil
54
62
  import com.tl.uic.util.LayoutUtil
55
63
  import com.tl.uic.util.keyboardview.KeyboardView
56
64
  import java.util.Objects
57
65
 
58
- class HybridAcousticConnectRN(private val reactContext: ReactApplicationContext) : HybridAcousticConnectRNSpec(), LifecycleEventListener {
59
-
60
- init {
61
- // Add your listener logic here
62
- println("HybridAcousticConnectRNSpec has been initialized")
63
-
64
- Handler(Looper.getMainLooper()).post {
65
- if (reactContext.hasActiveCatalystInstance()) {
66
- reactContext.addLifecycleEventListener(this)
67
- } else {
68
- println("ReactContext is not ready. LifecycleEventListener not registered.")
69
- }
70
- }
71
- }
72
-
73
- /**
74
- * Sets the module's boolean configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key.
75
- *
76
- * @param key Map Key.
77
- * @param value Boolean Value.
78
- * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
79
- * @return True if the operation was successful, false otherwise.
80
- */
81
- override fun setBooleanConfigItemForKey(
82
- key: String,
83
- value: Boolean,
84
- moduleName: String
85
- ): Boolean {
86
- val result: Boolean =
87
- EOCore.updateConfig(key, value.toString(), EOCore.getLifecycleObject(moduleName))
88
- return result
89
- }
90
-
91
- /**
92
- * Sets the module's string configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key.
93
- *
94
- * @param key Map Key.
95
- * @param value String Value.
96
- * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
97
- * @return True if the operation was successful, false otherwise.
98
- */
99
- override fun setStringItemForKey(
100
- key: String,
101
- value: String,
102
- moduleName: String
103
- ): Boolean {
104
- val result = EOCore.updateConfig(key, value, EOCore.getLifecycleObject(moduleName))
105
- return result
106
- }
107
-
108
- /**
109
- * Sets the module's number configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key.
110
- *
111
- * @param key Map Key.
112
- * @param value Number Value.
113
- * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
114
- * @return True if the operation was successful, false otherwise.
115
- */
116
- override fun setNumberItemForKey(
117
- key: String,
118
- value: Double,
119
- moduleName: String
120
- ): Boolean {
121
- val result = EOCore.updateConfig(key, value.toString(), EOCore.getLifecycleObject(moduleName))
122
- return result
123
- }
124
-
125
- /**
126
- * Sets the module's configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key.
127
- *
128
- * @param key Map Key.
129
- * @param value Map Value.
130
- * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
131
- * @param promise Javascript Promise interface.
132
- */
133
- override fun setConfigItemForKey(
134
- key: String,
135
- value: Variant_String_Double_Boolean,
136
- moduleName: String
137
- ): Boolean {
138
- val result = EOCore.updateConfig(key, value.toString(), EOCore.getLifecycleObject(moduleName))
139
- return result
140
- }
141
-
142
- /**
143
- * Gets the module's configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key as a BOOL value.
144
- *
145
- * @param theDefault In case no value if found, use this value as default.
146
- * @param key Key value.
147
- * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
148
- * @return True if the operation was successful, false otherwise.
149
- */
150
- override fun getBooleanConfigItemForKey(
151
- theDefault: Boolean,
152
- key: String,
153
- moduleName: String
154
- ): Boolean {
155
- val result = EOCore.getConfigItemBoolean(key, EOCore.getLifecycleObject(moduleName))
156
- if (result == false) {
157
- return theDefault
158
- }
159
- return result
160
- }
161
-
162
- /**
163
- * Gets the module's configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key as a String value.
164
- *
165
- * @param theDefault In case no value if found, use this value as default.
166
- * @param key Key value.
167
- * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
168
- * @return String value if the operation was successful, null otherwise.
169
- */
170
- override fun getStringItemForKey(theDefault: String, key: String, moduleName: String): String? {
171
- var result = EOCore.getConfigItemString(key, EOCore.getLifecycleObject(moduleName))
172
- if (TextUtils.isEmpty(result)) {
173
- result = theDefault
174
- }
175
- return result
176
- }
177
-
178
- /**
179
- * Gets the module's configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key as a double value.
180
- *
181
- * @param theDefault In case no value if found, use this value as default.
182
- * @param key Key value.
183
- * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
184
- * @return Double value if the operation was successful, 0.0 otherwise.
185
- */
186
- override fun getNumberItemForKey(theDefault: Double, key: String, moduleName: String): Double {
187
- var result = EOCore.getConfigItemDouble(key, EOCore.getLifecycleObject(moduleName))
188
- if (result == -1.0) {
189
- result = theDefault
190
- }
191
- return result
192
- }
193
-
194
- /**
195
- * Logs a custom event with the specified name and values.
196
- *
197
- * @param eventName The name of the event to be logged this will appear in the posted json.
198
- * @param values A map of values associated with the event.
199
- * @param level Set a custom log level to the event. This will override the configured log level for that event.
200
- * @return True if the operation was successful, false otherwise.
201
- */
202
- override fun logCustomEvent(
203
- eventName: String,
204
- values: Map<String, Variant_String_Double_Boolean>,
205
- level: Double
206
- ): Boolean {
207
- val result = Connect.logCustomEvent(eventName, convertToMap(values), level.toInt())
208
- return result
209
- }
210
-
211
- /**
212
- * Logs a signal with the specified values.
213
- *
214
- * @param values A map of values associated with the signal.
215
- * @param level Set a custom log level to the event. This will override the configured log level for that event.
216
- * @return True if the operation was successful, false otherwise.
217
- */
218
- override fun logSignal(
219
- values: Map<String, Variant_String_Double_Boolean>,
220
- level: Double
221
- ): Boolean {
222
- val result = Connect.logSignal(convertToMapAny(values), level.toInt())
223
- return result
224
- }
225
-
226
- /**
227
- * Logs an exception event with the specified message and stack information.
228
- *
229
- * @param message The message associated with the exception.
230
- * @param stackInfo The stack information associated with the exception.
231
- * @param unhandled Indicates whether the exception is unhandled.
232
- * @return True if the operation was successful, false otherwise.
233
- */
234
- override fun logExceptionEvent(message: String, stackInfo: String, unhandled: Boolean): Boolean {
235
- val result = Connect.logExceptionEvent("React Plugin", message, stackInfo, unhandled)
236
- return result
237
- }
238
-
239
- /**
240
- * Logs the current location.
241
- *
242
- * @return True if the operation was successful, false otherwise.
243
- */
244
- override fun logLocation(): Boolean {
245
- val result = logGeolocation(EOMonitoringLevel.kEOMonitoringLevelInfo.value)
246
- return result
247
- }
248
-
249
- /**
250
- * Logs the current location with the specified latitude and longitude.
251
- *
252
- * @param latitude The latitude of the location.
253
- * @param longitude The longitude of the location.
254
- * @param level Set a custom log level to the event. This will override the configured log level for that event.
255
- * @return True if the operation was successful, false otherwise.
256
- */
257
- override fun logLocationWithLatitudeLongitude(
258
- latitude: Double,
259
- longitude: Double,
260
- level: Double
261
- ): Boolean {
262
- val result = logLocationUpdateEventWithLatitude(latitude, longitude, level.toInt())
263
- return result
264
- }
265
-
266
- /**
267
- * Log click events on react native control.
268
- *
269
- * @param target Target id of the control.
270
- * @param controlId Accessibility ID(virtual id).
271
- * @return True if the operation was successful, false otherwise.
272
- */
273
- override fun logClickEvent(target: Double, controlId: String): Boolean {
274
- var result = false
275
- try {
276
- val context: ReactApplicationContext = reactContext
277
- // Add UI-block so we can get a valid reference to the map-view
278
- val uiManager = context.getNativeModule(UIManagerModule::class.java)
279
-
280
- Objects.requireNonNull<UIManagerModule?>(uiManager)
281
- .addUIBlock { nvhm: NativeViewHierarchyManager ->
282
- val view = nvhm.resolveView(target.toInt())
283
- if (view == null) {
284
- result = false
285
- } else {
286
- if (view is EditText) {
287
- addFocusAndRegister(view, null, getCurrentActivity()!!)
66
+
67
+ class HybridAcousticConnectRN(private val reactContext: ReactApplicationContext) : HybridAcousticConnectRNSpec(),
68
+ LifecycleEventListener {
69
+
70
+ init {
71
+ // Add your listener logic here
72
+ Log.v(TAG, "HybridAcousticConnectRNSpec has been initialized")
73
+
74
+ Handler(Looper.getMainLooper()).post {
75
+ if (reactContext.hasActiveCatalystInstance()) {
76
+ reactContext.addLifecycleEventListener(this)
288
77
  } else {
289
- if (!TextUtils.isEmpty(controlId)) {
290
- logEvent(view, "click", controlId)
291
- } else {
292
- logEvent(view, "click")
293
- }
78
+ Log.v(TAG, "ReactContext is not ready. LifecycleEventListener not registered.")
294
79
  }
295
- result = true
296
- }
297
80
  }
298
- } catch (e: java.lang.Exception) {
299
- // Handle the exception
300
- result = false
301
- }
302
- return result
303
- }
304
-
305
- /**
306
- * Log click events on react native control.
307
- *
308
- * @param target Target id of the control.
309
- * @return True if the operation was successful, false otherwise.
310
- */
311
- fun logClickEvent(target: Double): Boolean {
312
- var result = false
313
- try {
314
- val context: ReactApplicationContext = reactContext
315
- // Add UI-block so we can get a valid reference to the map-view
316
- val uiManager = context.getNativeModule(UIManagerModule::class.java)
317
-
318
- Objects.requireNonNull<UIManagerModule?>(uiManager)
319
- .addUIBlock { nvhm: NativeViewHierarchyManager ->
320
- val view = nvhm.resolveView(target.toInt())
321
- if (view == null) {
81
+ }
82
+
83
+ /**
84
+ * Sets the module's boolean configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key.
85
+ *
86
+ * @param key Map Key.
87
+ * @param value Boolean Value.
88
+ * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
89
+ * @return True if the operation was successful, false otherwise.
90
+ */
91
+ override fun setBooleanConfigItemForKey(
92
+ key: String,
93
+ value: Boolean,
94
+ moduleName: String
95
+ ): Boolean {
96
+ val result: Boolean =
97
+ EOCore.updateConfig(key, value.toString(), EOCore.getLifecycleObject(moduleName))
98
+ return result
99
+ }
100
+
101
+ /**
102
+ * Sets the module's string configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key.
103
+ *
104
+ * @param key Map Key.
105
+ * @param value String Value.
106
+ * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
107
+ * @return True if the operation was successful, false otherwise.
108
+ */
109
+ override fun setStringItemForKey(
110
+ key: String,
111
+ value: String,
112
+ moduleName: String
113
+ ): Boolean {
114
+ val result = EOCore.updateConfig(key, value, EOCore.getLifecycleObject(moduleName))
115
+ return result
116
+ }
117
+
118
+ /**
119
+ * Sets the module's number configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key.
120
+ *
121
+ * @param key Map Key.
122
+ * @param value Number Value.
123
+ * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
124
+ * @return True if the operation was successful, false otherwise.
125
+ */
126
+ override fun setNumberItemForKey(
127
+ key: String,
128
+ value: Double,
129
+ moduleName: String
130
+ ): Boolean {
131
+ val result = EOCore.updateConfig(key, value.toString(), EOCore.getLifecycleObject(moduleName))
132
+ return result
133
+ }
134
+
135
+ /**
136
+ * Sets the module's configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key.
137
+ *
138
+ * @param key Map Key.
139
+ * @param value Map Value.
140
+ * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
141
+ * @param promise Javascript Promise interface.
142
+ */
143
+ override fun setConfigItemForKey(
144
+ key: String,
145
+ value: Variant_String_Double_Boolean,
146
+ moduleName: String
147
+ ): Boolean {
148
+ val result = EOCore.updateConfig(key, value.toString(), EOCore.getLifecycleObject(moduleName))
149
+ return result
150
+ }
151
+
152
+ /**
153
+ * Gets the module's configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key as a BOOL value.
154
+ *
155
+ * @param theDefault In case no value if found, use this value as default.
156
+ * @param key Key value.
157
+ * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
158
+ * @return True if the operation was successful, false otherwise.
159
+ */
160
+ override fun getBooleanConfigItemForKey(
161
+ theDefault: Boolean,
162
+ key: String,
163
+ moduleName: String
164
+ ): Boolean {
165
+ val result = EOCore.getConfigItemBoolean(key, EOCore.getLifecycleObject(moduleName))
166
+ if (result == false) {
167
+ return theDefault
168
+ }
169
+ return result
170
+ }
171
+
172
+ /**
173
+ * Gets the module's configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key as a String value.
174
+ *
175
+ * @param theDefault In case no value if found, use this value as default.
176
+ * @param key Key value.
177
+ * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
178
+ * @return String value if the operation was successful, null otherwise.
179
+ */
180
+ override fun getStringItemForKey(theDefault: String, key: String, moduleName: String): String? {
181
+ var result = EOCore.getConfigItemString(key, EOCore.getLifecycleObject(moduleName))
182
+ if (TextUtils.isEmpty(result)) {
183
+ result = theDefault
184
+ }
185
+ return result
186
+ }
187
+
188
+ /**
189
+ * Gets the module's configuration item from AdvancedConfig.json or BasicConfig.properties that matches the specified key as a double value.
190
+ *
191
+ * @param theDefault In case no value if found, use this value as default.
192
+ * @param key Key value.
193
+ * @param moduleName The class name of the module's EOLifecycleObject for which the configuration item is referencing.
194
+ * @return Double value if the operation was successful, 0.0 otherwise.
195
+ */
196
+ override fun getNumberItemForKey(theDefault: Double, key: String, moduleName: String): Double {
197
+ var result = EOCore.getConfigItemDouble(key, EOCore.getLifecycleObject(moduleName))
198
+ if (result == -1.0) {
199
+ result = theDefault
200
+ }
201
+ return result
202
+ }
203
+
204
+ /**
205
+ * Logs a custom event with the specified name and values.
206
+ *
207
+ * @param eventName The name of the event to be logged this will appear in the posted json.
208
+ * @param values A map of values associated with the event.
209
+ * @param level Set a custom log level to the event. This will override the configured log level for that event.
210
+ * @return True if the operation was successful, false otherwise.
211
+ */
212
+ override fun logCustomEvent(
213
+ eventName: String,
214
+ values: Map<String, Variant_String_Double_Boolean>,
215
+ level: Double
216
+ ): Boolean {
217
+ val result = Connect.logCustomEvent(eventName, convertToMap(values), level.toInt())
218
+ return result
219
+ }
220
+
221
+ /**
222
+ * Logs a signal with the specified values.
223
+ *
224
+ * @param values A map of values associated with the signal.
225
+ * @param level Set a custom log level to the event. This will override the configured log level for that event.
226
+ * @return True if the operation was successful, false otherwise.
227
+ */
228
+ override fun logSignal(
229
+ values: Map<String, Variant_String_Double_Boolean>,
230
+ level: Double
231
+ ): Boolean {
232
+ val result = Connect.logSignal(convertToMapAny(values), level.toInt())
233
+ return result
234
+ }
235
+
236
+ /**
237
+ * Logs an exception event with the specified message and stack information.
238
+ *
239
+ * @param message The message associated with the exception.
240
+ * @param stackInfo The stack information associated with the exception.
241
+ * @param unhandled Indicates whether the exception is unhandled.
242
+ * @return True if the operation was successful, false otherwise.
243
+ */
244
+ override fun logExceptionEvent(message: String, stackInfo: String, unhandled: Boolean): Boolean {
245
+ val result = Connect.logExceptionEvent("React Plugin", message, stackInfo, unhandled)
246
+ return result
247
+ }
248
+
249
+ /**
250
+ * Logs the current location.
251
+ *
252
+ * @return True if the operation was successful, false otherwise.
253
+ */
254
+ override fun logLocation(): Boolean {
255
+ val result = logGeolocation(EOMonitoringLevel.kEOMonitoringLevelInfo.value)
256
+ return result
257
+ }
258
+
259
+ /**
260
+ * Logs the current location with the specified latitude and longitude.
261
+ *
262
+ * @param latitude The latitude of the location.
263
+ * @param longitude The longitude of the location.
264
+ * @param level Set a custom log level to the event. This will override the configured log level for that event.
265
+ * @return True if the operation was successful, false otherwise.
266
+ */
267
+ override fun logLocationWithLatitudeLongitude(
268
+ latitude: Double,
269
+ longitude: Double,
270
+ level: Double
271
+ ): Boolean {
272
+ val result = logLocationUpdateEventWithLatitude(latitude, longitude, level.toInt())
273
+ return result
274
+ }
275
+
276
+ /**
277
+ * Log click events on react native control.
278
+ *
279
+ * @param target Target id of the control.
280
+ * @param controlId Accessibility ID(virtual id).
281
+ * @return True if the operation was successful, false otherwise.
282
+ */
283
+ override fun logClickEvent(target: Double, controlId: String): Boolean {
284
+ var result = false
285
+ try {
286
+ val context: ReactApplicationContext = reactContext
287
+ // Add UI-block so we can get a valid reference to the map-view
288
+ val uiManager = context.getNativeModule(UIManagerModule::class.java)
289
+
290
+ Objects.requireNonNull<UIManagerModule?>(uiManager)
291
+ .addUIBlock { nvhm: NativeViewHierarchyManager ->
292
+ val view = nvhm.resolveView(target.toInt())
293
+ if (view == null) {
294
+ result = false
295
+ } else {
296
+ if (view is EditText) {
297
+ addFocusAndRegister(view, null, getCurrentActivity()!!)
298
+ } else {
299
+ if (!TextUtils.isEmpty(controlId)) {
300
+ logEvent(view, "click", controlId)
301
+ } else {
302
+ logEvent(view, "click")
303
+ }
304
+ }
305
+ result = true
306
+ }
307
+ }
308
+ } catch (e: java.lang.Exception) {
309
+ // Handle the exception
310
+ result = false
311
+ }
312
+ return result
313
+ }
314
+
315
+ /**
316
+ * Log click events on react native control.
317
+ *
318
+ * @param target Target id of the control.
319
+ * @return True if the operation was successful, false otherwise.
320
+ */
321
+ fun logClickEvent(target: Double): Boolean {
322
+ var result = false
323
+ try {
324
+ val context: ReactApplicationContext = reactContext
325
+ // Add UI-block so we can get a valid reference to the map-view
326
+ val uiManager = context.getNativeModule(UIManagerModule::class.java)
327
+
328
+ Objects.requireNonNull<UIManagerModule?>(uiManager)
329
+ .addUIBlock { nvhm: NativeViewHierarchyManager ->
330
+ val view = nvhm.resolveView(target.toInt())
331
+ if (view == null) {
332
+ result = false
333
+ } else {
334
+ if (view is EditText) {
335
+ addFocusAndRegister(view as EditText, null, getCurrentActivity()!!)
336
+ } else {
337
+ logEvent(view, "click")
338
+ }
339
+ result = true
340
+ }
341
+ }
342
+ } catch (e: java.lang.Exception) {
343
+ // Handle the exception
322
344
  result = false
323
- } else {
324
- if (view is EditText) {
325
- addFocusAndRegister(view as EditText, null, getCurrentActivity()!!)
345
+ }
346
+ return result
347
+ }
348
+
349
+ /**
350
+ * Log EditText change event.
351
+ *
352
+ * @param target A valid native View Id for lookup.
353
+ * @param controlId Accessibility ID(virtual id).
354
+ * @param text The input string.
355
+ * @return True if the operation was successful, false otherwise.
356
+ */
357
+ override fun logTextChangeEvent(target: Double, controlId: String, text: String?): Boolean {
358
+ var result = false
359
+ try {
360
+ val context: ReactApplicationContext = reactContext
361
+ // Add UI-block so we can get a valid reference to the map-view
362
+ val uiManager = context.getNativeModule(UIManagerModule::class.java)
363
+
364
+ Objects.requireNonNull<UIManagerModule?>(uiManager)
365
+ .addUIBlock { nvhm: NativeViewHierarchyManager ->
366
+ val view = nvhm.resolveView(target.toInt())
367
+ if (view == null) {
368
+ result = false
369
+ } else {
370
+ if (view is EditText && (view as EditText).onFocusChangeListener == null) {
371
+ // First time, logEvent and subsequent calls will be handled in change listener
372
+ logEvent(view, TLF_ON_FOCUS_CHANGE_IN, controlId)
373
+ addFocusAndRegister(view as EditText, controlId, getCurrentActivity()!!)
374
+ }
375
+ result = true
376
+ }
377
+ }
378
+ } catch (e: java.lang.Exception) {
379
+ // Handle the exception
380
+ result = false
381
+ }
382
+ return result
383
+ }
384
+
385
+ /**
386
+ * Requests that the framework save the current application page name.
387
+ *
388
+ * @param logicalPageName The logical page name to be set.
389
+ * @return True if the operation was successful, false otherwise.
390
+ */
391
+ override fun setCurrentScreenName(logicalPageName: String): Boolean {
392
+ val result = resumeConnect(getCurrentActivity(), logicalPageName, false)
393
+ return result
394
+ }
395
+
396
+ /**
397
+ * Requests that the framework logs an screen load event.
398
+ *
399
+ * @param logicalPageName The logical page name to be set.
400
+ * @param referrer The referrer for the screen view.
401
+ * @return True if the operation was successful, false otherwise.
402
+ */
403
+ override fun logScreenViewContextLoad(logicalPageName: String?, referrer: String?): Boolean {
404
+ val result = logScreenview(getCurrentActivity()!!, logicalPageName.toString(), ScreenviewType.LOAD, referrer);
405
+ return result
406
+ }
407
+
408
+ /**
409
+ * Requests that the framework logs an screen unload event.
410
+ *
411
+ * @param logicalPageName The logical page name to be set.
412
+ * @param referrer The referrer for the screen view.
413
+ * @return True if the operation was successful, false otherwise.
414
+ */
415
+ override fun logScreenViewContextUnload(logicalPageName: String?, referrer: String?): Boolean {
416
+ val result = logScreenview(getCurrentActivity()!!, logicalPageName.toString(), ScreenviewType.UNLOAD, referrer);
417
+ return result
418
+ }
419
+
420
+ /**
421
+ * Log Current Screen Layout using native side background thread.
422
+ *
423
+ * @param name Page name or title e.g. "Login View Controller"; Must not be empty.
424
+ * @param delay The delay in milliseconds before logging the event.
425
+ * @return True if the operation was successful, false otherwise.
426
+ */
427
+ override fun logScreenLayout(name: String, delay: Double): Boolean {
428
+ setCurrentScreenName(name)
429
+ logScreenview(getCurrentActivity(), name, ScreenviewType.LOAD)
430
+ var result = false
431
+ if (LayoutUtil.canCaptureUserEvents(null, name)) {
432
+ result = logScreenLayout(
433
+ Objects.requireNonNull<Activity?>(getCurrentActivity()),
434
+ name,
435
+ if (delay.toInt() < 0) 300 else delay.toInt(),
436
+ true
437
+ )
438
+ }
439
+ return result
440
+ }
441
+
442
+ /**
443
+ * Logs a dialog show event with the specified dialog information.
444
+ *
445
+ * @param dialogId Unique identifier for the dialog.
446
+ * @param dialogTitle The title of the dialog.
447
+ * @param dialogType The type of dialog (alert, custom, modal).
448
+ * @return True if the operation was successful, false otherwise.
449
+ */
450
+ override fun logDialogShowEvent(dialogId: String, dialogTitle: String, dialogType: String): Boolean {
451
+ var result: Boolean
452
+
453
+ try {
454
+ // Your existing logging code...
455
+ val values = HashMap<String?, String?>()
456
+ values["dialogId"] = dialogId
457
+ values["dialogTitle"] = dialogTitle
458
+ values["dialogType"] = dialogType
459
+ values["eventType"] = "dialog_show"
460
+ values["timestamp"] = System.currentTimeMillis().toString()
461
+
462
+ // For Alert dialogs, use delayed capture to ensure dialog is rendered
463
+ if (dialogType == "alert") {
464
+ // Schedule delayed capture to allow dialog to render
465
+ Handler(Looper.getMainLooper()).postDelayed({
466
+ try {
467
+ val dialog = findMostRecentDialog()
468
+ if (dialog != null) {
469
+ val activity = getCurrentActivity()
470
+ if (activity != null) {
471
+ DialogUtil.logDialog(getCurrentActivity(), dialog)
472
+ // Tealeaf.logScreenLayoutSetOnShowListener(activity, dialog, dialogId, true)
473
+ Log.v(TAG, "Delayed screenshot capture successful for dialog: $dialogId")
474
+ }
475
+ } else {
476
+ Log.v(TAG, "Warning: Could not find dialog object for $dialogId after delay")
477
+ // Fallback to regular screen layout capture
478
+ logScreenLayout(Objects.requireNonNull<Activity?>(getCurrentActivity()), dialogTitle, 0, true)
479
+ }
480
+ } catch (e: Exception) {
481
+ Log.v(TAG, "Error in delayed dialog capture: ${e.message}")
482
+ // Fallback to regular screen layout capture
483
+ logScreenLayout(Objects.requireNonNull<Activity?>(getCurrentActivity()), dialogTitle, 0, true)
484
+ }
485
+ }, DIALOG_CAPTURE_DELAY_MS) // Use configurable delay
486
+
487
+ // Return true immediately since we're handling capture asynchronously
488
+ result = true
326
489
  } else {
327
- logEvent(view, "click")
490
+ // For non-alert dialogs, try immediate capture first
491
+ val dialog = findMostRecentDialog()
492
+ if (dialog != null) {
493
+ val activity = getCurrentActivity()
494
+ if (activity != null) {
495
+ DialogUtil.logDialog(getCurrentActivity(), dialog)
496
+ return true
497
+ } else {
498
+ result = false
499
+ }
500
+ } else {
501
+ // Default fallback
502
+ result = logScreenLayout(Objects.requireNonNull<Activity?>(getCurrentActivity()), dialogTitle, 300, true)
503
+ }
328
504
  }
329
- result = true
330
- }
331
- }
332
- } catch (e: java.lang.Exception) {
333
- // Handle the exception
334
- result = false
335
- }
336
- return result
337
- }
338
-
339
- /**
340
- * Log EditText change event.
341
- *
342
- * @param target A valid native View Id for lookup.
343
- * @param controlId Accessibility ID(virtual id).
344
- * @param text The input string.
345
- * @return True if the operation was successful, false otherwise.
346
- */
347
- override fun logTextChangeEvent(target: Double, controlId: String, text: String?): Boolean {
348
- var result = false
349
- try {
350
- val context: ReactApplicationContext = reactContext
351
- // Add UI-block so we can get a valid reference to the map-view
352
- val uiManager = context.getNativeModule(UIManagerModule::class.java)
353
-
354
- Objects.requireNonNull<UIManagerModule?>(uiManager)
355
- .addUIBlock { nvhm: NativeViewHierarchyManager ->
356
- val view = nvhm.resolveView(target.toInt())
357
- if (view == null) {
505
+ } catch (e: Exception) {
506
+ Log.v(TAG, "Error logging dialog show event: ${e.message}")
358
507
  result = false
359
- } else {
360
- if (view is EditText && (view as EditText).onFocusChangeListener == null) {
361
- // First time, logEvent and subsequent calls will be handled in change listener
362
- logEvent(view, TLF_ON_FOCUS_CHANGE_IN, controlId)
363
- addFocusAndRegister(view as EditText, controlId, getCurrentActivity()!!)
508
+ }
509
+ return result
510
+ }
511
+
512
+ private fun findMostRecentDialog(): Dialog? {
513
+ val activity = getCurrentActivity() ?: return null
514
+
515
+ // Check fragments first
516
+ val fragmentDialog = findDialogFromFragments(activity)
517
+ if (fragmentDialog != null) return fragmentDialog
518
+
519
+ // Check window manager for Alert dialogs with retry logic
520
+ return findLatestDialogFromWindowManager(activity)
521
+ }
522
+
523
+ private fun findDialogFromFragments(activity: Activity): Dialog? {
524
+ try {
525
+ // Check support fragments first (more common in modern apps)
526
+ if (activity is androidx.fragment.app.FragmentActivity) {
527
+ val supportFm = activity.supportFragmentManager
528
+ for (frag in supportFm.fragments) {
529
+ if (frag is androidx.fragment.app.DialogFragment) {
530
+ val dialog = frag.dialog
531
+ if (dialog != null && dialog.isShowing) {
532
+ return dialog
533
+ }
534
+ }
535
+ }
536
+ }
537
+
538
+ // Check legacy fragments
539
+ // Note: fm.fragments is only available in API 26+, so we'll skip this for older versions
540
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
541
+ val fm = activity.fragmentManager
542
+ if (fm != null) {
543
+ for (frag in fm.fragments) {
544
+ if (frag is DialogFragment) {
545
+ val dialog = frag.dialog
546
+ if (dialog != null && dialog.isShowing) {
547
+ return dialog
548
+ }
549
+ }
550
+ }
551
+ }
364
552
  }
365
- result = true
366
- }
553
+ } catch (e: Exception) {
554
+ Log.v(TAG, "Error checking fragments: ${e.message}")
367
555
  }
368
- } catch (e: java.lang.Exception) {
369
- // Handle the exception
370
- result = false
371
- }
372
- return result
373
- }
374
-
375
- /**
376
- * Requests that the framework save the current application page name.
377
- *
378
- * @param logicalPageName The logical page name to be set.
379
- * @return True if the operation was successful, false otherwise.
380
- */
381
- override fun setCurrentScreenName(logicalPageName: String): Boolean {
382
- val result = resumeConnect(getCurrentActivity(), logicalPageName, false)
383
- return result
384
- }
385
-
386
- /**
387
- * Requests that the framework logs an screen load event.
388
- *
389
- * @param logicalPageName The logical page name to be set.
390
- * @param referrer The referrer for the screen view.
391
- * @return True if the operation was successful, false otherwise.
392
- */
393
- override fun logScreenViewContextLoad(logicalPageName: String?, referrer: String?): Boolean {
394
- val result = logScreenview(getCurrentActivity()!!, logicalPageName.toString(), ScreenviewType.LOAD, referrer);
395
- return result
396
- }
397
-
398
- /**
399
- * Requests that the framework logs an screen unload event.
400
- *
401
- * @param logicalPageName The logical page name to be set.
402
- * @param referrer The referrer for the screen view.
403
- * @return True if the operation was successful, false otherwise.
404
- */
405
- override fun logScreenViewContextUnload(logicalPageName: String?, referrer: String?): Boolean {
406
- val result = logScreenview(getCurrentActivity()!!, logicalPageName.toString(), ScreenviewType.UNLOAD, referrer);
407
- return result
408
- }
409
-
410
- /**
411
- * Log Current Screen Layout using native side background thread.
412
- *
413
- * @param name Page name or title e.g. "Login View Controller"; Must not be empty.
414
- * @param delay The delay in milliseconds before logging the event.
415
- * @return True if the operation was successful, false otherwise.
416
- */
417
- override fun logScreenLayout(name: String, delay: Double): Boolean {
418
- setCurrentScreenName(name)
419
- logScreenview(getCurrentActivity(), name, ScreenviewType.LOAD)
420
- var result = false
421
- if (LayoutUtil.canCaptureUserEvents(null, name)) {
422
- result = logScreenLayout(
423
- Objects.requireNonNull<Activity?>(getCurrentActivity()),
424
- name,
425
- if (delay.toInt() < 0) 300 else delay.toInt(),
426
- true
427
- )
428
- }
429
- return result
430
- }
431
-
432
- /**
433
- * Converts a map of Variant_String_Double_Boolean to a HashMap<String?, String?>.
434
- *
435
- * @param values The map to be converted.
436
- * @return A HashMap<String?, String?> representation of the input map which library can use.
437
- */
438
- private fun convertToMap(values: Map<String, Variant_String_Double_Boolean>): HashMap<String?, String?> {
439
- val map = HashMap<String?, String?>()
440
- for (key in values.keys) {
441
- val value = values[key]
442
- if (value != null) {
443
- map[key] = value.toString()
444
- }
445
- }
446
- return map
447
- }
448
-
449
- /**
450
- * Converts a map of Variant_String_Double_Boolean to a HashMap<String?, String?>.
451
- *
452
- * @param values The map to be converted.
453
- * @return A HashMap<String?, String?> representation of the input map which library can use.
454
- */
455
- private fun convertToMapAny(values: Map<String, Variant_String_Double_Boolean>): java.util.HashMap<String?, Any?>? {
456
- val map = HashMap<String?, Any?>()
457
- for (key in values.keys) {
458
- val value = values[key]
459
- if (value != null) {
460
- map[key] = value
461
- }
462
- }
463
- return map
464
- }
465
-
466
- /**
467
- * Gets the current activity from the ReactApplicationContext.
468
- *
469
- * @return The current activity or null if not available.
470
- */
471
- private fun getCurrentActivity(): android.app.Activity? {
472
- return reactContext.currentActivity
473
- }
474
-
475
- /**
476
- * Add focus listener to handle EditText UI control.
477
- *
478
- * @param textView Input TextView.
479
- * @param accessibilityID Accessibility ID(virtual id).
480
- * @param activity Current activity.
481
- */
482
- fun addFocusAndRegister(textView: TextView, accessibilityID: String?, activity: Activity) {
483
- textView.onFocusChangeListener = OnFocusChangeListener { v: View, hasFocus: Boolean ->
484
- if (hasFocus) {
485
- val imm = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
486
- imm.showSoftInput(v, InputMethodManager.SHOW_FORCED)
487
- val keyboardView = KeyboardView(v.context.applicationContext, null)
488
-
489
- if (TextUtils.isEmpty(accessibilityID)) {
490
- logEvent(keyboardView, TLF_UI_KEYBOARD_DID_SHOW_NOTIFICATION)
491
- logEvent(v, TLF_ON_FOCUS_CHANGE_IN)
492
- } else {
493
- logEvent(keyboardView, TLF_UI_KEYBOARD_DID_SHOW_NOTIFICATION, accessibilityID!!)
494
- logEvent(v, TLF_ON_FOCUS_CHANGE_IN, accessibilityID!!)
556
+ return null
557
+ }
558
+
559
+ private fun findLatestDialogFromWindowManager(activity: Activity): Dialog? {
560
+ try {
561
+ val wmgClass = Class.forName("android.view.WindowManagerGlobal")
562
+ val wmgInstance = wmgClass.getMethod("getInstance").invoke(null)
563
+
564
+ val viewsField = wmgClass.getDeclaredField("mViews")
565
+ viewsField.isAccessible = true
566
+ val views = viewsField.get(wmgInstance) as ArrayList<View>
567
+
568
+ val paramsField = wmgClass.getDeclaredField("mParams")
569
+ paramsField.isAccessible = true
570
+ val params = paramsField.get(wmgInstance) as ArrayList<WindowManager.LayoutParams>
571
+
572
+ // Find the most recent dialog window (last in the list)
573
+ for (i in views.indices.reversed()) {
574
+ val view = views[i]
575
+ val param = params[i]
576
+
577
+ if (isDialogWindow(param)) {
578
+ // Try to get dialog from view context
579
+ var context = view.context
580
+ while (context is ContextWrapper) {
581
+ if (context is Dialog && context.isShowing) {
582
+ Log.v(TAG, "Found dialog in WindowManager: ${context.javaClass.simpleName}")
583
+ return context
584
+ }
585
+ context = context.baseContext
586
+ }
587
+
588
+ // Additional check: look for AlertDialog specifically
589
+ if (view.javaClass.name.contains("AlertController")) {
590
+ Log.v(TAG, "Found AlertController view, attempting to get dialog")
591
+ // Try to find the dialog through the view's parent or other means
592
+ val parent = view.parent
593
+ if (parent is View) {
594
+ var parentContext = parent.context
595
+ while (parentContext is ContextWrapper) {
596
+ if (parentContext is Dialog && parentContext.isShowing) {
597
+ Log.v(TAG, "Found AlertDialog through parent context")
598
+ return parentContext
599
+ }
600
+ parentContext = parentContext.baseContext
601
+ }
602
+ }
603
+ }
604
+ }
605
+ }
606
+ } catch (e: Exception) {
607
+ Log.v(TAG, "Error accessing WindowManager: ${e.message}")
495
608
  }
496
- } else {
497
- logEvent(v, TLF_ON_FOCUS_CHANGE_OUT)
498
- val imm = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
499
- imm.hideSoftInputFromWindow(v.windowToken, 0)
609
+ return null
610
+ }
500
611
 
501
- val keyboardView = KeyboardView(v.context.applicationContext, null)
612
+ private fun isDialogWindow(params: WindowManager.LayoutParams): Boolean {
613
+ return params.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL ||
614
+ params.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL ||
615
+ (params.flags and WindowManager.LayoutParams.FLAG_DIM_BEHIND) != 0
616
+ }
502
617
 
503
- if (TextUtils.isEmpty(accessibilityID)) {
504
- logEvent(keyboardView, TLF_UI_KEYBOARD_DID_HIDE_NOTIFICATION)
505
- } else {
506
- logEvent(keyboardView, TLF_UI_KEYBOARD_DID_HIDE_NOTIFICATION, accessibilityID!!)
618
+ /**
619
+ * Logs a dialog dismiss event with the specified dialog information.
620
+ *
621
+ * @param dialogId Unique identifier for the dialog.
622
+ * @param dismissReason The reason for dismissing the dialog.
623
+ * @return True if the operation was successful, false otherwise.
624
+ */
625
+ override fun logDialogDismissEvent(dialogId: String, dismissReason: String): Boolean {
626
+ var result = false
627
+ try {
628
+ val values = HashMap<String?, String?>()
629
+ values["dialogId"] = dialogId
630
+ values["dismissReason"] = dismissReason
631
+ values["eventType"] = "dialog_dismiss"
632
+ values["timestamp"] = System.currentTimeMillis().toString()
633
+
634
+ result =
635
+ Connect.logCustomEvent("DialogDismissEvent", values, EOMonitoringLevel.kEOMonitoringLevelInfo.value)
636
+ } catch (e: Exception) {
637
+ Log.v(TAG, "Error logging dialog dismiss event: ${e.message}")
638
+ result = false
639
+ }
640
+ return result
641
+ }
642
+
643
+ /**
644
+ * Logs a dialog button click event with the specified button information.
645
+ *
646
+ * @param dialogId Unique identifier for the dialog.
647
+ * @param buttonText The text of the clicked button.
648
+ * @param buttonIndex The index of the clicked button.
649
+ * @return True if the operation was successful, false otherwise.
650
+ */
651
+ override fun logDialogButtonClickEvent(dialogId: String, buttonText: String, buttonIndex: Double): Boolean {
652
+ var result: Boolean
653
+
654
+ try {
655
+ val values = HashMap<String?, String?>()
656
+ values["dialogId"] = dialogId
657
+ values["buttonText"] = buttonText
658
+ values["buttonIndex"] = buttonIndex.toInt().toString()
659
+ values["eventType"] = "dialog_button_click"
660
+ values["timestamp"] = System.currentTimeMillis().toString()
661
+
662
+ result = Connect.logCustomEvent("DialogButtonClickEvent", values, EOMonitoringLevel.kEOMonitoringLevelInfo.value)
663
+
664
+ // TODO: JS pass button id
665
+ // Simple approach: find the most recently shown dialog
666
+ // val dialog = findMostRecentDialog()
667
+ // if (dialog != null) {
668
+ // val activity = getCurrentActivity()
669
+ // if (activity != null) {
670
+ // val view = when (dialog) {
671
+ // is AlertDialog -> dialog.getButton(id)
672
+ // is androidx.appcompat.app.AlertDialog -> dialog.getButton(id)
673
+ // else -> null
674
+ // }
675
+ //
676
+ // if (view == null) {
677
+ // return false
678
+ // }
679
+ //
680
+ // Connect.logDialogEvent(dialog, 1)
681
+ // }
682
+ // }
683
+ } catch (e: Exception) {
684
+ Log.v(TAG, "Error logging dialog button click event: ${e.message}")
685
+ result = false
686
+ }
687
+ return result
688
+ }
689
+
690
+ /**
691
+ * Logs a custom dialog event with the specified event information.
692
+ *
693
+ * @param dialogId Unique identifier for the dialog.
694
+ * @param eventName The name of the custom event.
695
+ * @param values A map of values associated with the event.
696
+ * @return True if the operation was successful, false otherwise.
697
+ */
698
+ override fun logDialogCustomEvent(
699
+ dialogId: String,
700
+ eventName: String,
701
+ values: Map<String, Variant_String_Double_Boolean>
702
+ ): Boolean {
703
+ var result = false
704
+ try {
705
+ val eventValues = HashMap<String?, String?>()
706
+ eventValues["dialogId"] = dialogId
707
+ eventValues["customEventName"] = eventName
708
+ eventValues["eventType"] = "dialog_custom_event"
709
+ eventValues["timestamp"] = System.currentTimeMillis().toString()
710
+
711
+ // Add the custom values
712
+ for (key in values.keys) {
713
+ val value = values[key]
714
+ if (value != null) {
715
+ eventValues[key] = value.toString()
716
+ }
717
+ }
718
+
719
+ result =
720
+ Connect.logCustomEvent("DialogCustomEvent", eventValues, EOMonitoringLevel.kEOMonitoringLevelInfo.value)
721
+ } catch (e: Exception) {
722
+ Log.v(TAG, "Error logging dialog custom event: ${e.message}")
723
+ result = false
724
+ }
725
+ return result
726
+ }
727
+
728
+ /**
729
+ * Converts a map of Variant_String_Double_Boolean to a HashMap<String?, String?>.
730
+ *
731
+ * @param values The map to be converted.
732
+ * @return A HashMap<String?, String?> representation of the input map which library can use.
733
+ */
734
+ private fun convertToMap(values: Map<String, Variant_String_Double_Boolean>): HashMap<String?, String?> {
735
+ val map = HashMap<String?, String?>()
736
+ for (key in values.keys) {
737
+ val value = values[key]
738
+ if (value != null) {
739
+ map[key] = value.toString()
740
+ }
507
741
  }
508
- }
742
+ return map
509
743
  }
510
744
 
511
- registerFormField(textView, activity)
512
- }
745
+ /**
746
+ * Converts a map of Variant_String_Double_Boolean to a HashMap<String?, String?>.
747
+ *
748
+ * @param values The map to be converted.
749
+ * @return A HashMap<String?, String?> representation of the input map which library can use.
750
+ */
751
+ private fun convertToMapAny(values: Map<String, Variant_String_Double_Boolean>): java.util.HashMap<String?, Any?>? {
752
+ val map = HashMap<String?, Any?>()
753
+ for (key in values.keys) {
754
+ val value = values[key]
755
+ if (value != null) {
756
+ map[key] = value
757
+ }
758
+ }
759
+ return map
760
+ }
761
+
762
+ /**
763
+ * Gets the current activity from the ReactApplicationContext.
764
+ *
765
+ * @return The current activity or null if not available.
766
+ */
767
+ private fun getCurrentActivity(): android.app.Activity? {
768
+ return applicationContext?.currentActivity
769
+ }
770
+
771
+ /**
772
+ * Add focus listener to handle EditText UI control.
773
+ *
774
+ * @param textView Input TextView.
775
+ * @param accessibilityID Accessibility ID(virtual id).
776
+ * @param activity Current activity.
777
+ */
778
+ fun addFocusAndRegister(textView: TextView, accessibilityID: String?, activity: Activity) {
779
+ textView.onFocusChangeListener = OnFocusChangeListener { v: View, hasFocus: Boolean ->
780
+ if (hasFocus) {
781
+ val imm = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
782
+ imm.showSoftInput(v, InputMethodManager.SHOW_FORCED)
783
+ val keyboardView = KeyboardView(v.context.applicationContext, null)
784
+
785
+ if (TextUtils.isEmpty(accessibilityID)) {
786
+ logEvent(keyboardView, TLF_UI_KEYBOARD_DID_SHOW_NOTIFICATION)
787
+ logEvent(v, TLF_ON_FOCUS_CHANGE_IN)
788
+ } else {
789
+ logEvent(keyboardView, TLF_UI_KEYBOARD_DID_SHOW_NOTIFICATION, accessibilityID!!)
790
+ logEvent(v, TLF_ON_FOCUS_CHANGE_IN, accessibilityID!!)
791
+ }
792
+ } else {
793
+ logEvent(v, TLF_ON_FOCUS_CHANGE_OUT)
794
+ val imm = v.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
795
+ imm.hideSoftInputFromWindow(v.windowToken, 0)
796
+
797
+ val keyboardView = KeyboardView(v.context.applicationContext, null)
798
+
799
+ if (TextUtils.isEmpty(accessibilityID)) {
800
+ logEvent(keyboardView, TLF_UI_KEYBOARD_DID_HIDE_NOTIFICATION)
801
+ } else {
802
+ logEvent(keyboardView, TLF_UI_KEYBOARD_DID_HIDE_NOTIFICATION, accessibilityID!!)
803
+ }
804
+ }
805
+ }
806
+
807
+ registerFormField(textView, activity)
808
+ }
513
809
 
514
810
  // override fun onWindowFocusChanged(hasFocus: Boolean) {
515
811
  // if (!reactContext.hasActiveCatalystInstance()) {
@@ -534,74 +830,75 @@ class HybridAcousticConnectRN(private val reactContext: ReactApplicationContext)
534
830
  // }
535
831
  // }
536
832
 
537
- fun onWindowFocusChanged(hasFocus: Boolean) {
538
- if (!reactContext.hasActiveCatalystInstance()) {
833
+ fun onWindowFocusChanged(hasFocus: Boolean) {
834
+ if (!reactContext.hasActiveCatalystInstance()) {
539
835
  // logEvent("WindowFocus", "Context is not ready. Skipping onWindowFocusChanged.")
540
- return
541
- }
836
+ return
837
+ }
542
838
 
543
- // Handle window focus change
544
- if (hasFocus) {
839
+ // Handle window focus change
840
+ if (hasFocus) {
545
841
  // logEvent("WindowFocus", "Window gained focus")
546
- } else {
842
+ } else {
547
843
  // logEvent("WindowFocus", "Window lost focus")
844
+ }
548
845
  }
549
- }
550
846
 
551
- /**
552
- * Used when host resumes.
553
- */
554
- override fun onHostResume() {
555
- // Initialize Connect library, and hook into activity lifecycle events to help detect if app is in background
556
- if (!reactContext.hasActiveCatalystInstance()) {
847
+ /**
848
+ * Used when host resumes.
849
+ */
850
+ override fun onHostResume() {
851
+ // Initialize Connect library, and hook into activity lifecycle events to help detect if app is in background
852
+ if (!reactContext.hasActiveCatalystInstance()) {
557
853
  // logEvent("Lifecycle", "onHostResume skipped: ReactContext is not ready")
558
- return
559
- }
854
+ return
855
+ }
560
856
 
561
- val activity = getCurrentActivity()
562
- if (activity == null) {
857
+ val activity = getCurrentActivity()
858
+ if (activity == null) {
563
859
  // logEvent("Lifecycle", "onHostResume skipped: Activity is null")
564
- return
565
- }
860
+ return
861
+ }
566
862
 
567
- if (!isEnabled()) {
568
- if (getApplication() == null) {
569
- init((reactContext.applicationContext as Application))
570
- }
571
- enable()
863
+ if (!isEnabled()) {
864
+ if (getApplication() == null) {
865
+ init((reactContext.applicationContext as Application))
866
+ }
867
+ enable()
868
+ }
869
+ onResume(activity, null)
572
870
  }
573
- onResume(activity, null)
574
- }
575
871
 
576
- /**
577
- * Used when host gets paused.
578
- */
579
- override fun onHostPause() {
580
- val activity = getCurrentActivity()
581
- if (activity == null) {
872
+ /**
873
+ * Used when host gets paused.
874
+ */
875
+ override fun onHostPause() {
876
+ val activity = getCurrentActivity()
877
+ if (activity == null) {
582
878
  // logEvent("Lifecycle", "onHostPause skipped: Activity is null")
583
- return
584
- }
879
+ return
880
+ }
585
881
 
586
- Connect.onPause(activity, null)
587
- }
882
+ Connect.onPause(activity, null)
883
+ }
588
884
 
589
- /**
590
- * Used when host gets destroyed.
591
- */
592
- override fun onHostDestroy() {
593
- val activity = getCurrentActivity()
594
- if (activity == null) {
885
+ /**
886
+ * Used when host gets destroyed.
887
+ */
888
+ override fun onHostDestroy() {
889
+ val activity = getCurrentActivity()
890
+ if (activity == null) {
595
891
  // logEvent("Lifecycle", "onHostDestroy skipped: Activity is null")
596
- return
597
- }
892
+ return
893
+ }
598
894
 
599
- Tealeaf.onDestroy(activity, null)
600
- // Uncomment if Connect.onDestroy is needed
601
- // Connect.onDestroy(activity, null)
602
- }
895
+ Tealeaf.onDestroy(activity, null)
896
+ // Uncomment if Connect.onDestroy is needed
897
+ // Connect.onDestroy(activity, null)
898
+ }
603
899
 
604
900
  companion object {
605
901
  const val TAG = "AcousticConnectRN"
902
+ const val DIALOG_CAPTURE_DELAY_MS = 500L // Configurable delay for dialog screenshot capture
606
903
  }
607
904
  }