@attentive-mobile/attentive-react-native-sdk 1.0.3-beta.1 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -0
- package/android/build.gradle +4 -0
- package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.kt +384 -0
- package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveReactNativeSdkPackage.kt +36 -0
- package/android/src/main/kotlin/com/attentivereactnativesdk/debug/AttentiveDebugHelper.kt +438 -0
- package/android/src/main/kotlin/com/attentivereactnativesdk/debug/DebugEvent.kt +76 -0
- package/attentive-react-native-sdk.podspec +4 -5
- package/ios/AttentiveReactNativeSdk.h +6 -6
- package/ios/AttentiveReactNativeSdk.mm +325 -35
- package/ios/AttentiveReactNativeSdk.xcodeproj/project.pbxproj +2 -2
- package/ios/Bridging/ATTNNativeSDK.swift +1118 -3
- package/ios/Bridging/AttentiveReactNativeSdk-Bridging-Header.h +3 -0
- package/ios/Bridging/AttentiveSDKManager.swift +83 -0
- package/ios/Podfile +4 -17
- package/lib/commonjs/NativeAttentiveReactNativeSdk.js +14 -0
- package/lib/commonjs/NativeAttentiveReactNativeSdk.js.map +1 -0
- package/lib/commonjs/index.js +363 -39
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/NativeAttentiveReactNativeSdk.js +7 -0
- package/lib/module/NativeAttentiveReactNativeSdk.js.map +1 -0
- package/lib/module/index.js +346 -38
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/NativeAttentiveReactNativeSdk.d.ts +103 -0
- package/lib/typescript/NativeAttentiveReactNativeSdk.d.ts.map +1 -0
- package/lib/typescript/eventTypes.d.ts +44 -17
- package/lib/typescript/eventTypes.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +276 -33
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +22 -8
- package/src/NativeAttentiveReactNativeSdk.ts +152 -0
- package/src/eventTypes.tsx +57 -20
- package/src/index.tsx +472 -82
- package/android/src/main/java/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.java +0 -247
- package/android/src/main/java/com/attentivereactnativesdk/AttentiveReactNativeSdkPackage.java +0 -28
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
package com.attentivereactnativesdk.debug
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.graphics.Color
|
|
6
|
+
import android.graphics.Typeface
|
|
7
|
+
import android.graphics.drawable.GradientDrawable
|
|
8
|
+
import android.util.DisplayMetrics
|
|
9
|
+
import android.util.Log
|
|
10
|
+
import android.view.Gravity
|
|
11
|
+
import android.view.View
|
|
12
|
+
import android.view.ViewGroup
|
|
13
|
+
import android.widget.Button
|
|
14
|
+
import android.widget.FrameLayout
|
|
15
|
+
import android.widget.LinearLayout
|
|
16
|
+
import android.widget.ScrollView
|
|
17
|
+
import android.widget.TextView
|
|
18
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
19
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
20
|
+
import org.json.JSONObject
|
|
21
|
+
import java.text.SimpleDateFormat
|
|
22
|
+
import java.util.Date
|
|
23
|
+
import java.util.Locale
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Kotlin-based debug helper for the Attentive React Native SDK.
|
|
27
|
+
*
|
|
28
|
+
* This class provides comprehensive debugging capabilities including:
|
|
29
|
+
* - Session history tracking
|
|
30
|
+
* - Debug overlay with tabbed interface
|
|
31
|
+
* - Export and share functionality
|
|
32
|
+
* - Real-time debug information display
|
|
33
|
+
*/
|
|
34
|
+
class AttentiveDebugHelper(private val reactContext: ReactApplicationContext) {
|
|
35
|
+
|
|
36
|
+
companion object {
|
|
37
|
+
private const val TAG = "AttentiveDebugHelper"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private val debugHistory = mutableListOf<DebugEvent>()
|
|
41
|
+
private var isDebuggingEnabled = false
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returns whether debugging is currently enabled.
|
|
45
|
+
* Provides explicit getter method for Java interop.
|
|
46
|
+
*/
|
|
47
|
+
fun isDebuggingEnabled(): Boolean = isDebuggingEnabled
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Initializes debugging based on configuration and build type.
|
|
51
|
+
*
|
|
52
|
+
* @param enableDebuggerFromConfig Whether debugging is enabled in configuration
|
|
53
|
+
*/
|
|
54
|
+
fun initialize(enableDebuggerFromConfig: Boolean) {
|
|
55
|
+
val isDebugBuild = (reactContext.applicationInfo.flags and
|
|
56
|
+
android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE) != 0
|
|
57
|
+
isDebuggingEnabled = enableDebuggerFromConfig && isDebugBuild
|
|
58
|
+
|
|
59
|
+
Log.i(TAG, "Debug initialization - enableDebuggerFromConfig: $enableDebuggerFromConfig, " +
|
|
60
|
+
"isDebugBuild: $isDebugBuild, debuggingEnabled: ${isDebuggingEnabled()}")
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Shows debug information by adding to history and displaying the debug dialog.
|
|
65
|
+
*
|
|
66
|
+
* @param event The event name/type
|
|
67
|
+
* @param data The event data
|
|
68
|
+
*/
|
|
69
|
+
fun showDebugInfo(event: String, data: Map<String, Any?>) {
|
|
70
|
+
if (!isDebuggingEnabled()) return
|
|
71
|
+
|
|
72
|
+
Log.i(TAG, "showDebugInfo called for event: $event, data: $data")
|
|
73
|
+
|
|
74
|
+
// Add to debug history
|
|
75
|
+
val debugEvent = DebugEvent(event, data)
|
|
76
|
+
debugHistory.add(debugEvent)
|
|
77
|
+
|
|
78
|
+
val currentActivity = reactContext.currentActivity
|
|
79
|
+
if (currentActivity != null) {
|
|
80
|
+
Log.i(TAG, "Current activity found, showing debug dialog on UI thread")
|
|
81
|
+
UiThreadUtil.runOnUiThread {
|
|
82
|
+
showDebugDialog(currentActivity, event, data)
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
Log.w(TAG, "Current activity is null, cannot show debug dialog")
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Manually invokes the debug helper overlay.
|
|
91
|
+
* Shows existing debug session data without adding to history.
|
|
92
|
+
*/
|
|
93
|
+
fun invokeDebugHelper() {
|
|
94
|
+
Log.i(TAG, "invokeDebugHelper called - isDebuggingEnabled: ${isDebuggingEnabled()}")
|
|
95
|
+
|
|
96
|
+
if (!isDebuggingEnabled()) {
|
|
97
|
+
Log.w(TAG, "Debug helper not invoked because debugging is not enabled")
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
val currentActivity = reactContext.currentActivity
|
|
102
|
+
Log.i(TAG, "Current activity: $currentActivity")
|
|
103
|
+
|
|
104
|
+
if (currentActivity != null) {
|
|
105
|
+
Log.i(TAG, "Activity is available, running on UI thread")
|
|
106
|
+
UiThreadUtil.runOnUiThread {
|
|
107
|
+
val debugData = mapOf(
|
|
108
|
+
"action" to "manual_debug_call",
|
|
109
|
+
"session_events" to debugHistory.size.toString()
|
|
110
|
+
)
|
|
111
|
+
Log.i(TAG, "About to show debug dialog")
|
|
112
|
+
showDebugDialog(currentActivity, "Manual Debug View", debugData)
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
Log.w(TAG, "Current activity is null, cannot show debug dialog")
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Exports the current debug session logs as a formatted string.
|
|
121
|
+
* @return A comprehensive formatted string containing all debug events in the current session
|
|
122
|
+
*/
|
|
123
|
+
fun exportDebugLogs(): String {
|
|
124
|
+
if (!isDebuggingEnabled()) {
|
|
125
|
+
return "Debug logging is not enabled. Please enable debugging to export logs."
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (debugHistory.isEmpty()) {
|
|
129
|
+
return "No debug events recorded in this session."
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
|
133
|
+
val exportDate = formatter.format(Date())
|
|
134
|
+
|
|
135
|
+
return buildString {
|
|
136
|
+
appendLine("Attentive React Native SDK - Debug Session Export")
|
|
137
|
+
appendLine("Generated: $exportDate")
|
|
138
|
+
appendLine("Total Events: ${debugHistory.size}")
|
|
139
|
+
appendLine()
|
|
140
|
+
appendLine("=".repeat(60))
|
|
141
|
+
appendLine()
|
|
142
|
+
|
|
143
|
+
// Add all events in chronological order (oldest first for better readability)
|
|
144
|
+
debugHistory.forEachIndexed { index, event ->
|
|
145
|
+
appendLine("Event #${index + 1}")
|
|
146
|
+
append(event.formatForExport())
|
|
147
|
+
appendLine()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
appendLine("=".repeat(60))
|
|
151
|
+
append("End of Debug Session Export")
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Shares debug logs using the Android share intent.
|
|
157
|
+
* @param context The current activity context
|
|
158
|
+
* @param content The content to share
|
|
159
|
+
*/
|
|
160
|
+
private fun shareDebugLogs(context: Activity, content: String) {
|
|
161
|
+
val shareIntent = Intent().apply {
|
|
162
|
+
action = Intent.ACTION_SEND
|
|
163
|
+
type = "text/plain"
|
|
164
|
+
putExtra(Intent.EXTRA_TEXT, content)
|
|
165
|
+
putExtra(Intent.EXTRA_SUBJECT, "Attentive React Native SDK - Debug Session Export")
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
val chooser = Intent.createChooser(shareIntent, "Share Debug Logs")
|
|
169
|
+
if (shareIntent.resolveActivity(context.packageManager) != null) {
|
|
170
|
+
context.startActivity(chooser)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Creates and displays the comprehensive debug overlay with tabbed interface.
|
|
176
|
+
* Features:
|
|
177
|
+
* - Current event details
|
|
178
|
+
* - Session history with chronological listing
|
|
179
|
+
* - Export/share functionality
|
|
180
|
+
* - Material Design styling with rounded corners and proper spacing
|
|
181
|
+
*/
|
|
182
|
+
private fun showDebugDialog(activity: Activity, currentEvent: String, currentData: Map<String, Any?>) {
|
|
183
|
+
Log.i(TAG, "showDebugDialog called for event: $currentEvent")
|
|
184
|
+
|
|
185
|
+
// Get the root view of the current activity to add our overlay directly
|
|
186
|
+
val rootView = activity.window.decorView.rootView as ViewGroup
|
|
187
|
+
Log.i(TAG, "Root view obtained: $rootView")
|
|
188
|
+
|
|
189
|
+
// Create custom view for tabbed interface - this will be added directly to the activity
|
|
190
|
+
val mainLayout = LinearLayout(activity).apply {
|
|
191
|
+
orientation = LinearLayout.VERTICAL
|
|
192
|
+
setPadding(32, 32, 32, 32)
|
|
193
|
+
setBackgroundColor(Color.TRANSPARENT) // Completely transparent background
|
|
194
|
+
layoutParams = FrameLayout.LayoutParams(
|
|
195
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
196
|
+
FrameLayout.LayoutParams.MATCH_PARENT
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Create content container that will be positioned at bottom
|
|
201
|
+
val contentContainer = LinearLayout(activity).apply {
|
|
202
|
+
orientation = LinearLayout.VERTICAL
|
|
203
|
+
setBackgroundColor(0xFFFFFFFF.toInt())
|
|
204
|
+
setPadding(24, 24, 24, 24)
|
|
205
|
+
|
|
206
|
+
// Round corners with discrete outline border
|
|
207
|
+
val background = GradientDrawable().apply {
|
|
208
|
+
setColor(0xFFFFFFFF.toInt()) // White background
|
|
209
|
+
cornerRadius = 24f // Rounded corners
|
|
210
|
+
setStroke(2, 0xFFE0E0E0.toInt()) // Discrete light gray border (2dp width)
|
|
211
|
+
}
|
|
212
|
+
setBackground(background)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Header with title, share button, and close button
|
|
216
|
+
val headerLayout = LinearLayout(activity).apply {
|
|
217
|
+
orientation = LinearLayout.HORIZONTAL
|
|
218
|
+
gravity = Gravity.CENTER_VERTICAL
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
val titleText = TextView(activity).apply {
|
|
222
|
+
text = "🐛 Attentive Debug Session"
|
|
223
|
+
textSize = 18f
|
|
224
|
+
typeface = Typeface.DEFAULT_BOLD
|
|
225
|
+
setTextColor(Color.BLACK)
|
|
226
|
+
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
|
227
|
+
}
|
|
228
|
+
headerLayout.addView(titleText)
|
|
229
|
+
|
|
230
|
+
// Share button - Material Design share icon, circular
|
|
231
|
+
val shareButton = createCircularButton(activity, "↗", 0xFF1976D2.toInt(), 0xFFF0F0F0.toInt()).apply {
|
|
232
|
+
layoutParams = LinearLayout.LayoutParams(72, 72).apply {
|
|
233
|
+
setMargins(0, 0, 20, 0)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
headerLayout.addView(shareButton)
|
|
237
|
+
|
|
238
|
+
// Close button - circular with prominent X and destructive styling
|
|
239
|
+
val closeButton = createCircularButton(activity, "×", 0xFFD32F2F.toInt(), 0xFFFFF0F0.toInt()).apply {
|
|
240
|
+
textSize = 32f
|
|
241
|
+
layoutParams = LinearLayout.LayoutParams(72, 72)
|
|
242
|
+
}
|
|
243
|
+
headerLayout.addView(closeButton)
|
|
244
|
+
|
|
245
|
+
contentContainer.addView(headerLayout)
|
|
246
|
+
|
|
247
|
+
// Tab buttons
|
|
248
|
+
val tabLayout = LinearLayout(activity).apply {
|
|
249
|
+
orientation = LinearLayout.HORIZONTAL
|
|
250
|
+
setPadding(0, 20, 0, 0)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
val currentTab = Button(activity).apply {
|
|
254
|
+
text = "Current Event"
|
|
255
|
+
setTextColor(Color.BLACK)
|
|
256
|
+
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
val historyTab = Button(activity).apply {
|
|
260
|
+
text = "History (${debugHistory.size})"
|
|
261
|
+
setTextColor(Color.BLACK)
|
|
262
|
+
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
tabLayout.addView(currentTab)
|
|
266
|
+
tabLayout.addView(historyTab)
|
|
267
|
+
contentContainer.addView(tabLayout)
|
|
268
|
+
|
|
269
|
+
// Content area
|
|
270
|
+
val contentScroll = ScrollView(activity)
|
|
271
|
+
val contentText = TextView(activity).apply {
|
|
272
|
+
typeface = Typeface.MONOSPACE
|
|
273
|
+
textSize = 12f
|
|
274
|
+
setTextColor(0xFF333333.toInt()) // Dark gray text for better readability
|
|
275
|
+
setPadding(0, 20, 0, 0)
|
|
276
|
+
}
|
|
277
|
+
contentScroll.addView(contentText)
|
|
278
|
+
|
|
279
|
+
// Make content area larger to fill most of the screen
|
|
280
|
+
contentScroll.layoutParams = LinearLayout.LayoutParams(
|
|
281
|
+
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f
|
|
282
|
+
)
|
|
283
|
+
contentContainer.addView(contentScroll)
|
|
284
|
+
|
|
285
|
+
// Position content container at bottom with minimum height (55% of screen - slightly shorter)
|
|
286
|
+
val displayMetrics = DisplayMetrics()
|
|
287
|
+
activity.windowManager.defaultDisplay.getMetrics(displayMetrics)
|
|
288
|
+
val screenHeight = displayMetrics.heightPixels
|
|
289
|
+
val minHeight = (screenHeight * 0.55).toInt() // 55% of screen height - slightly shorter
|
|
290
|
+
|
|
291
|
+
contentContainer.apply {
|
|
292
|
+
minimumHeight = minHeight
|
|
293
|
+
layoutParams = LinearLayout.LayoutParams(
|
|
294
|
+
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
295
|
+
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
296
|
+
).apply {
|
|
297
|
+
gravity = Gravity.BOTTOM
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
Log.i(TAG, "Debug overlay minimum height set to: ${minHeight}px (55% of ${screenHeight}px)")
|
|
302
|
+
|
|
303
|
+
// Add spacer to push content to bottom
|
|
304
|
+
val spacer = View(activity).apply {
|
|
305
|
+
layoutParams = LinearLayout.LayoutParams(
|
|
306
|
+
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f
|
|
307
|
+
)
|
|
308
|
+
}
|
|
309
|
+
mainLayout.addView(spacer)
|
|
310
|
+
mainLayout.addView(contentContainer)
|
|
311
|
+
|
|
312
|
+
// Set initial content to current event
|
|
313
|
+
updateCurrentEventContent(contentText, currentEvent, currentData)
|
|
314
|
+
|
|
315
|
+
// Tab click listeners
|
|
316
|
+
currentTab.setOnClickListener {
|
|
317
|
+
updateCurrentEventContent(contentText, currentEvent, currentData)
|
|
318
|
+
currentTab.isEnabled = false
|
|
319
|
+
historyTab.isEnabled = true
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
historyTab.setOnClickListener {
|
|
323
|
+
updateHistoryContent(contentText)
|
|
324
|
+
currentTab.isEnabled = true
|
|
325
|
+
historyTab.isEnabled = false
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Set initial tab state
|
|
329
|
+
currentTab.isEnabled = false
|
|
330
|
+
historyTab.isEnabled = true
|
|
331
|
+
|
|
332
|
+
// Setup share button to export and share debug logs
|
|
333
|
+
shareButton.setOnClickListener {
|
|
334
|
+
val exportContent = exportDebugLogs()
|
|
335
|
+
shareDebugLogs(activity, exportContent)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Setup close button to remove the overlay from the root view
|
|
339
|
+
closeButton.setOnClickListener {
|
|
340
|
+
rootView.removeView(mainLayout)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Add the overlay directly to the activity's root view
|
|
344
|
+
rootView.addView(mainLayout)
|
|
345
|
+
Log.i(TAG, "Debug overlay added to root view successfully")
|
|
346
|
+
|
|
347
|
+
// Note: No auto-dismiss to match iOS behavior - overlay stays until user closes it
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Creates a circular button with specified text, colors, and styling.
|
|
352
|
+
*/
|
|
353
|
+
private fun createCircularButton(activity: Activity, text: String, textColor: Int, backgroundColor: Int): Button {
|
|
354
|
+
return Button(activity).apply {
|
|
355
|
+
this.text = text
|
|
356
|
+
textSize = 28f
|
|
357
|
+
setTextColor(textColor)
|
|
358
|
+
setBackgroundColor(backgroundColor)
|
|
359
|
+
typeface = Typeface.DEFAULT_BOLD
|
|
360
|
+
gravity = Gravity.CENTER
|
|
361
|
+
includeFontPadding = false
|
|
362
|
+
minWidth = 0
|
|
363
|
+
minHeight = 0
|
|
364
|
+
minimumWidth = 0
|
|
365
|
+
minimumHeight = 0
|
|
366
|
+
textAlignment = View.TEXT_ALIGNMENT_CENTER
|
|
367
|
+
setSingleLine(true)
|
|
368
|
+
setLines(1)
|
|
369
|
+
setPadding(0, -20, 0, 8)
|
|
370
|
+
|
|
371
|
+
val drawable = GradientDrawable().apply {
|
|
372
|
+
shape = GradientDrawable.OVAL
|
|
373
|
+
setColor(backgroundColor)
|
|
374
|
+
setStroke(3, textColor)
|
|
375
|
+
}
|
|
376
|
+
background = drawable
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Updates the content text view with current event information.
|
|
382
|
+
*/
|
|
383
|
+
private fun updateCurrentEventContent(contentText: TextView, event: String, data: Map<String, Any?>) {
|
|
384
|
+
val content = buildString {
|
|
385
|
+
appendLine("Event: $event")
|
|
386
|
+
appendLine()
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
val jsonObject = JSONObject(data)
|
|
390
|
+
append(jsonObject.toString(2))
|
|
391
|
+
} catch (e: Exception) {
|
|
392
|
+
append("Data: $data")
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
contentText.text = content
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Updates the content text view with session history information.
|
|
401
|
+
*/
|
|
402
|
+
private fun updateHistoryContent(contentText: TextView) {
|
|
403
|
+
if (debugHistory.isEmpty()) {
|
|
404
|
+
contentText.text = "No events recorded in this session yet."
|
|
405
|
+
return
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
val content = buildString {
|
|
409
|
+
appendLine("Session History (${debugHistory.size} events):")
|
|
410
|
+
appendLine()
|
|
411
|
+
|
|
412
|
+
// Show events in reverse order (newest first)
|
|
413
|
+
debugHistory.asReversed().forEach { event ->
|
|
414
|
+
appendLine("───────────────────────────────")
|
|
415
|
+
appendLine("[${event.getFormattedTime()}] ${event.eventType}")
|
|
416
|
+
|
|
417
|
+
// Add summary
|
|
418
|
+
val summary = event.getSummary()
|
|
419
|
+
if (summary.isNotEmpty()) {
|
|
420
|
+
appendLine("Summary: $summary")
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
appendLine()
|
|
424
|
+
appendLine("Payload:")
|
|
425
|
+
try {
|
|
426
|
+
val jsonObject = JSONObject(event.data)
|
|
427
|
+
appendLine(jsonObject.toString(2))
|
|
428
|
+
} catch (e: Exception) {
|
|
429
|
+
appendLine(event.data.toString())
|
|
430
|
+
}
|
|
431
|
+
appendLine()
|
|
432
|
+
appendLine()
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
contentText.text = content
|
|
437
|
+
}
|
|
438
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
package com.attentivereactnativesdk.debug
|
|
2
|
+
|
|
3
|
+
import org.json.JSONObject
|
|
4
|
+
import java.text.SimpleDateFormat
|
|
5
|
+
import java.util.Date
|
|
6
|
+
import java.util.Locale
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Data class representing a debug event with timestamp and formatting capabilities.
|
|
10
|
+
*
|
|
11
|
+
* @param eventType The type/name of the event
|
|
12
|
+
* @param data The event data as a map
|
|
13
|
+
*/
|
|
14
|
+
data class DebugEvent(
|
|
15
|
+
val eventType: String,
|
|
16
|
+
val data: Map<String, Any?>
|
|
17
|
+
) {
|
|
18
|
+
val timestamp: Long = System.currentTimeMillis()
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns formatted time string for display.
|
|
22
|
+
*/
|
|
23
|
+
fun getFormattedTime(): String {
|
|
24
|
+
val formatter = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
|
|
25
|
+
return formatter.format(Date(timestamp))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generates a summary string with key information from the event data.
|
|
30
|
+
*/
|
|
31
|
+
fun getSummary(): String {
|
|
32
|
+
val summaryParts = mutableListOf<String>()
|
|
33
|
+
|
|
34
|
+
data["items_count"]?.let { summaryParts.add("Items: $it") }
|
|
35
|
+
data["order_id"]?.let { summaryParts.add("Order: $it") }
|
|
36
|
+
data["creativeId"]?.let { summaryParts.add("Creative: $it") }
|
|
37
|
+
data["event_type"]?.let { summaryParts.add("Type: $it") }
|
|
38
|
+
|
|
39
|
+
// Always show payload size info
|
|
40
|
+
summaryParts.add("Payload: ${data.size} fields")
|
|
41
|
+
|
|
42
|
+
return summaryParts.joinToString(" • ")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Formats the debug event as a human-readable string for export.
|
|
47
|
+
* @return A formatted string containing timestamp, event type, and data
|
|
48
|
+
*/
|
|
49
|
+
fun formatForExport(): String {
|
|
50
|
+
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
|
|
51
|
+
val timeString = formatter.format(Date(timestamp))
|
|
52
|
+
|
|
53
|
+
return buildString {
|
|
54
|
+
appendLine("[$timeString] $eventType")
|
|
55
|
+
|
|
56
|
+
// Add summary information if available
|
|
57
|
+
val summary = getSummary()
|
|
58
|
+
if (summary.isNotEmpty()) {
|
|
59
|
+
appendLine("Summary: $summary")
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
appendLine("Data:")
|
|
63
|
+
|
|
64
|
+
// Format data as JSON for better readability
|
|
65
|
+
try {
|
|
66
|
+
val jsonObject = JSONObject(data)
|
|
67
|
+
appendLine(jsonObject.toString(2))
|
|
68
|
+
} catch (e: Exception) {
|
|
69
|
+
appendLine(data.toString())
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
appendLine("=".repeat(50))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
}
|
|
@@ -11,13 +11,12 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.license = package["license"]
|
|
12
12
|
s.authors = package["author"]
|
|
13
13
|
|
|
14
|
-
s.platforms = { :ios => "
|
|
14
|
+
s.platforms = { :ios => "14.0" }
|
|
15
15
|
s.source = { :git => "https://github.com/attentive-mobile/attentive-react-native-sdk.git", :tag => "#{s.version}" }
|
|
16
16
|
|
|
17
17
|
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
18
18
|
|
|
19
|
-
s.dependency 'attentive-ios-sdk', '
|
|
20
|
-
s.ios.deployment_target = '14.0'
|
|
19
|
+
s.dependency 'attentive-ios-sdk', '2.0.8'
|
|
21
20
|
s.swift_versions = ['5']
|
|
22
21
|
s.dependency "React-Core"
|
|
23
22
|
|
|
@@ -25,9 +24,9 @@ Pod::Spec.new do |s|
|
|
|
25
24
|
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
|
|
26
25
|
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
|
|
27
26
|
s.pod_target_xcconfig = {
|
|
28
|
-
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
|
27
|
+
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/Headers/Public/React-NativeModulesApple\" \"$(PODS_ROOT)/Headers/Private/React-NativeModulesApple\" \"$(PODS_ROOT)/Headers/Private/React-Codegen/react/renderer/components\" \"${PODS_CONFIGURATION_BUILD_DIR}/React-Codegen/React_Codegen.framework/Headers\"",
|
|
29
28
|
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
|
|
30
|
-
"CLANG_CXX_LANGUAGE_STANDARD" => "c++
|
|
29
|
+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20"
|
|
31
30
|
}
|
|
32
31
|
s.dependency "React-Codegen"
|
|
33
32
|
s.dependency "RCT-Folly"
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
// Created by Wyatt Davis on 2/13/23.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
9
|
+
#import "AttentiveReactNativeSdkSpec.h"
|
|
10
|
+
|
|
11
|
+
@interface AttentiveReactNativeSdk : NSObject <NativeAttentiveReactNativeSdkSpec>
|
|
12
|
+
#else
|
|
13
13
|
#import <React/RCTBridgeModule.h>
|
|
14
14
|
|
|
15
15
|
@interface AttentiveReactNativeSdk : NSObject <RCTBridgeModule>
|
|
16
|
-
|
|
16
|
+
#endif
|
|
17
17
|
|
|
18
18
|
@end
|