@goliapkg/sentori-react-native 1.0.0-rc.6 → 1.0.0-rc.8
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.
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
package com.sentori
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
|
+
import android.graphics.drawable.BitmapDrawable
|
|
4
5
|
import android.graphics.drawable.ColorDrawable
|
|
5
6
|
import android.graphics.drawable.Drawable
|
|
6
7
|
import android.graphics.drawable.GradientDrawable
|
|
7
|
-
import android.graphics.drawable.
|
|
8
|
+
import android.graphics.drawable.LayerDrawable
|
|
9
|
+
import android.graphics.drawable.StateListDrawable
|
|
8
10
|
import android.view.View
|
|
9
11
|
import android.view.ViewGroup
|
|
10
12
|
import android.widget.EditText
|
|
@@ -93,9 +95,18 @@ object SentoriReplayCapture {
|
|
|
93
95
|
totalEmptyResultTicks++
|
|
94
96
|
return null
|
|
95
97
|
}
|
|
96
|
-
|
|
98
|
+
// rc.8 — anchor the walk at android.R.id.content, NOT
|
|
99
|
+
// window.decorView. decorView includes the StatusBarBackground
|
|
100
|
+
// and NavigationBarBackground sibling views the PhoneWindow
|
|
101
|
+
// injects (full display width, positioned in absolute window
|
|
102
|
+
// coords). Insight 2026-05-18 saw those bleed into the
|
|
103
|
+
// wireframe as horizontal grey bars stretching beyond the
|
|
104
|
+
// viewport width. Anchoring at the content FrameLayout drops
|
|
105
|
+
// them while keeping the app's React tree intact.
|
|
106
|
+
val decor = activity.window?.decorView
|
|
107
|
+
val root = decor?.findViewById<View>(android.R.id.content)
|
|
97
108
|
if (root == null) {
|
|
98
|
-
lastDiagPath = "decorView.null"
|
|
109
|
+
lastDiagPath = if (decor == null) "decorView.null" else "contentView.null"
|
|
99
110
|
totalEmptyResultTicks++
|
|
100
111
|
return null
|
|
101
112
|
}
|
|
@@ -217,18 +228,21 @@ object SentoriReplayCapture {
|
|
|
217
228
|
kindEmitted = true
|
|
218
229
|
}
|
|
219
230
|
view.background != null -> {
|
|
220
|
-
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
// rc.8 — backgrounds backed by a BitmapDrawable
|
|
232
|
+
// (a View whose backgroundImage / drawable
|
|
233
|
+
// resource is a raster) emit as `image` kind so
|
|
234
|
+
// the dashboard renders them as a media region,
|
|
235
|
+
// not as a grey rect. Everything else stays
|
|
236
|
+
// `rect` and tries to extract a fill colour.
|
|
237
|
+
val bg = view.background
|
|
238
|
+
if (bg is BitmapDrawable) {
|
|
239
|
+
node.put("kind", "image")
|
|
240
|
+
} else {
|
|
241
|
+
node.put("kind", "rect")
|
|
242
|
+
val color = extractDrawableColor(bg)
|
|
243
|
+
if (color != null && (color shr 24 and 0xff) != 0) {
|
|
244
|
+
node.put("color", colorToHex(color))
|
|
245
|
+
}
|
|
232
246
|
}
|
|
233
247
|
kindEmitted = true
|
|
234
248
|
}
|
|
@@ -256,31 +270,45 @@ object SentoriReplayCapture {
|
|
|
256
270
|
return String.format("#%02X%02X%02X%02X", r, g, b, a)
|
|
257
271
|
}
|
|
258
272
|
|
|
259
|
-
/** rc.5 — best-effort fill-colour extraction for the View's
|
|
260
|
-
* background Drawable.
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
273
|
+
/** rc.5 / rc.7 — best-effort fill-colour extraction for the View's
|
|
274
|
+
* background Drawable. We hit:
|
|
275
|
+
*
|
|
276
|
+
* - `ColorDrawable` — flat `<View style={{ backgroundColor }}>`.
|
|
277
|
+
* - `GradientDrawable` — RN ≤ 0.73's path for backgroundColor +
|
|
278
|
+
* borderRadius / borderWidth.
|
|
279
|
+
* - Any `LayerDrawable` subclass — RippleDrawable (Pressable),
|
|
280
|
+
* `com.facebook.react.uimanager.drawable.CompositeBackgroundDrawable`
|
|
281
|
+
* (RN 0.74+ Fabric path, wraps the real
|
|
282
|
+
* `BackgroundDrawable` layer alongside borders / shadows), and
|
|
283
|
+
* any other future composite. Iterate layers, recurse.
|
|
284
|
+
* - **rc.7 reflective fallback** — for the BackgroundDrawable
|
|
285
|
+
* itself (RN 0.74+, Kotlin `internal class` so we can't
|
|
286
|
+
* import it from this module) and any other custom Drawable
|
|
287
|
+
* that follows the convention of exposing `getBackgroundColor()`
|
|
288
|
+
* or `getColor()`. Without this, Insight's app — which uses
|
|
289
|
+
* Pressables and rounded Views and so renders nearly every
|
|
290
|
+
* coloured surface via CompositeBackgroundDrawable —
|
|
291
|
+
* surfaced zero `node.color` fields on rc.5. */
|
|
268
292
|
private fun extractDrawableColor(drawable: Drawable?): Int? {
|
|
269
293
|
return when (drawable) {
|
|
270
294
|
null -> null
|
|
271
295
|
is ColorDrawable -> drawable.color
|
|
272
296
|
is GradientDrawable -> {
|
|
273
|
-
// API 24+ exposes the ColorStateList for the
|
|
274
|
-
// `setColor()` value. RN's typical solid-colour
|
|
275
|
-
// GradientDrawable returns a single default colour
|
|
276
|
-
// here; gradients with multiple stops still surface
|
|
277
|
-
// a reasonable representative colour.
|
|
278
297
|
val csl = drawable.color
|
|
279
298
|
csl?.defaultColor
|
|
280
299
|
}
|
|
281
|
-
is
|
|
282
|
-
//
|
|
283
|
-
//
|
|
300
|
+
is StateListDrawable -> {
|
|
301
|
+
// rc.8 — Pressable / TouchableOpacity wrap their child
|
|
302
|
+
// in a StateListDrawable (default state + pressed
|
|
303
|
+
// state). `.current` returns the currently-applied
|
|
304
|
+
// state's drawable, which during a normal capture
|
|
305
|
+
// is the unpressed visual — exactly what we want.
|
|
306
|
+
// AnimatedStateListDrawable extends StateListDrawable
|
|
307
|
+
// so it inherits this branch.
|
|
308
|
+
extractDrawableColor(drawable.current)
|
|
309
|
+
}
|
|
310
|
+
is BitmapDrawable -> null
|
|
311
|
+
is LayerDrawable -> {
|
|
284
312
|
for (i in 0 until drawable.numberOfLayers) {
|
|
285
313
|
val inner = drawable.getDrawable(i)
|
|
286
314
|
val c = extractDrawableColor(inner)
|
|
@@ -288,7 +316,31 @@ object SentoriReplayCapture {
|
|
|
288
316
|
}
|
|
289
317
|
null
|
|
290
318
|
}
|
|
291
|
-
else ->
|
|
319
|
+
else -> extractByReflection(drawable)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** rc.7 — read `getBackgroundColor()` / `getColor()` via Java
|
|
324
|
+
* reflection. Kotlin synthesizes the former from any `var
|
|
325
|
+
* backgroundColor: Int` declaration, so RN's internal
|
|
326
|
+
* `BackgroundDrawable` (which holds the actual paint colour
|
|
327
|
+
* behind any RN View with backgroundColor) exposes it
|
|
328
|
+
* automatically. Any throw / non-Int return falls through to
|
|
329
|
+
* null — fully best-effort. */
|
|
330
|
+
private fun extractByReflection(drawable: Drawable): Int? {
|
|
331
|
+
val cls = drawable.javaClass
|
|
332
|
+
for (name in arrayOf("getBackgroundColor", "getColor")) {
|
|
333
|
+
try {
|
|
334
|
+
val method = cls.getMethod(name)
|
|
335
|
+
val result = method.invoke(drawable)
|
|
336
|
+
if (result is Int) return result
|
|
337
|
+
} catch (_: NoSuchMethodException) {
|
|
338
|
+
// try next
|
|
339
|
+
} catch (_: Throwable) {
|
|
340
|
+
// some custom drawables throw on reflective access; bail
|
|
341
|
+
return null
|
|
342
|
+
}
|
|
292
343
|
}
|
|
344
|
+
return null
|
|
293
345
|
}
|
|
294
346
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goliapkg/sentori-react-native",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.8",
|
|
4
4
|
"description": "Sentori SDK for React Native \u2014 JS-layer error capture, native crash handlers (iOS / Android), batched transport, fetch + react-navigation tracing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://sentori.golia.jp",
|