react-native-bug-reporter 1.0.2 → 1.0.4
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,9 +3,14 @@
|
|
|
3
3
|
<!-- Official screenshot detection on Android 14+ (no runtime prompt). -->
|
|
4
4
|
<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
|
|
5
5
|
|
|
6
|
-
<!-- MediaStore fallback for screenshot detection on Android 13
|
|
6
|
+
<!-- MediaStore fallback for screenshot detection on Android 13. -->
|
|
7
7
|
<uses-permission
|
|
8
8
|
android:name="android.permission.READ_MEDIA_IMAGES"
|
|
9
9
|
android:maxSdkVersion="33" />
|
|
10
10
|
|
|
11
|
+
<!-- MediaStore fallback for Android 12L and below (older devices). -->
|
|
12
|
+
<uses-permission
|
|
13
|
+
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
|
14
|
+
android:maxSdkVersion="32" />
|
|
15
|
+
|
|
11
16
|
</manifest>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
package com.bugreporter
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.os.Handler
|
|
5
|
+
import java.util.concurrent.Executor
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Isolated wrapper around the API 34+ `Activity.ScreenCaptureCallback`.
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT: this class is the ONLY place that references
|
|
11
|
+
* `Activity.ScreenCaptureCallback`. The main module must never reference that
|
|
12
|
+
* type directly — otherwise, on Android 13 and below where the class does not
|
|
13
|
+
* exist, the runtime's reflection over the module's methods throws
|
|
14
|
+
* `NoClassDefFoundError` and the whole native module fails to load.
|
|
15
|
+
*
|
|
16
|
+
* This class is only ever instantiated inside an `SDK_INT >= 34` guard, so it
|
|
17
|
+
* (and the missing framework class) is never loaded on older devices.
|
|
18
|
+
*/
|
|
19
|
+
class ScreenCaptureCallbackApi34(
|
|
20
|
+
private val activity: Activity,
|
|
21
|
+
private val handler: Handler,
|
|
22
|
+
private val onCapture: () -> Unit,
|
|
23
|
+
) {
|
|
24
|
+
private val callback = Activity.ScreenCaptureCallback { onCapture() }
|
|
25
|
+
|
|
26
|
+
fun register() {
|
|
27
|
+
val executor = Executor { command -> handler.post(command) }
|
|
28
|
+
activity.registerScreenCaptureCallback(executor, callback)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fun unregister() {
|
|
32
|
+
activity.unregisterScreenCaptureCallback(callback)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package com.bugreporter
|
|
2
2
|
|
|
3
|
+
import android.Manifest
|
|
3
4
|
import android.app.Activity
|
|
5
|
+
import android.content.pm.PackageManager
|
|
4
6
|
import android.database.ContentObserver
|
|
5
7
|
import android.graphics.Bitmap
|
|
6
8
|
import android.net.Uri
|
|
@@ -18,7 +20,6 @@ import com.facebook.react.bridge.WritableMap
|
|
|
18
20
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
19
21
|
import java.io.File
|
|
20
22
|
import java.io.FileOutputStream
|
|
21
|
-
import java.util.concurrent.Executor
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Native screenshot detector for Android.
|
|
@@ -38,7 +39,8 @@ class ScreenshotDetectorModule(private val reactContext: ReactApplicationContext
|
|
|
38
39
|
|
|
39
40
|
private val mainHandler = Handler(Looper.getMainLooper())
|
|
40
41
|
private var contentObserver: ContentObserver? = null
|
|
41
|
-
|
|
42
|
+
// Typed as Any? so this class never references the API-34 ScreenCaptureCallback.
|
|
43
|
+
private var screenCapture: ScreenCaptureCallbackApi34? = null
|
|
42
44
|
private var isListening = false
|
|
43
45
|
|
|
44
46
|
override fun getName(): String = NAME
|
|
@@ -50,10 +52,32 @@ class ScreenshotDetectorModule(private val reactContext: ReactApplicationContext
|
|
|
50
52
|
if (Build.VERSION.SDK_INT >= 34) {
|
|
51
53
|
registerScreenCaptureCallback()
|
|
52
54
|
} else {
|
|
55
|
+
// Older devices detect screenshots via MediaStore, which needs the media
|
|
56
|
+
// read permission granted at runtime.
|
|
57
|
+
requestStoragePermissionIfNeeded()
|
|
53
58
|
registerContentObserver()
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Requests the media-read permission on Android 13- so the MediaStore
|
|
64
|
+
* ContentObserver reliably receives screenshot notifications. No-op on
|
|
65
|
+
* Android 14+ (the official ScreenCaptureCallback needs no permission).
|
|
66
|
+
*/
|
|
67
|
+
private fun requestStoragePermissionIfNeeded() {
|
|
68
|
+
val activity = reactContext.currentActivity ?: return
|
|
69
|
+
val perm =
|
|
70
|
+
if (Build.VERSION.SDK_INT >= 33) Manifest.permission.READ_MEDIA_IMAGES
|
|
71
|
+
else Manifest.permission.READ_EXTERNAL_STORAGE
|
|
72
|
+
try {
|
|
73
|
+
if (activity.checkSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
|
|
74
|
+
activity.requestPermissions(arrayOf(perm), PERMISSION_REQUEST_CODE)
|
|
75
|
+
}
|
|
76
|
+
} catch (_: Exception) {
|
|
77
|
+
// Ignore — observer is still registered; some OEMs notify without it.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
57
81
|
@ReactMethod
|
|
58
82
|
fun stop() {
|
|
59
83
|
isListening = false
|
|
@@ -78,13 +102,14 @@ class ScreenshotDetectorModule(private val reactContext: ReactApplicationContext
|
|
|
78
102
|
private fun registerScreenCaptureCallback() {
|
|
79
103
|
val activity = reactContext.currentActivity ?: return
|
|
80
104
|
mainHandler.post {
|
|
81
|
-
val executor = Executor { command -> mainHandler.post(command) }
|
|
82
|
-
val callback = Activity.ScreenCaptureCallback {
|
|
83
|
-
onScreenshotDetected()
|
|
84
|
-
}
|
|
85
|
-
screenCaptureCallback = callback
|
|
86
105
|
try {
|
|
87
|
-
|
|
106
|
+
// All ScreenCaptureCallback usage is isolated in this helper so the
|
|
107
|
+
// missing class never loads on older Android.
|
|
108
|
+
val helper = ScreenCaptureCallbackApi34(activity, mainHandler) {
|
|
109
|
+
onScreenshotDetected()
|
|
110
|
+
}
|
|
111
|
+
helper.register()
|
|
112
|
+
screenCapture = helper
|
|
88
113
|
} catch (e: Exception) {
|
|
89
114
|
// Fall back to the content observer if the callback can't be registered.
|
|
90
115
|
registerContentObserver()
|
|
@@ -93,14 +118,13 @@ class ScreenshotDetectorModule(private val reactContext: ReactApplicationContext
|
|
|
93
118
|
}
|
|
94
119
|
|
|
95
120
|
private fun unregisterScreenCaptureCallback() {
|
|
96
|
-
val
|
|
97
|
-
val callback = screenCaptureCallback as? Activity.ScreenCaptureCallback ?: return
|
|
121
|
+
val helper = screenCapture ?: return
|
|
98
122
|
mainHandler.post {
|
|
99
123
|
try {
|
|
100
|
-
|
|
124
|
+
helper.unregister()
|
|
101
125
|
} catch (_: Exception) {
|
|
102
126
|
}
|
|
103
|
-
|
|
127
|
+
screenCapture = null
|
|
104
128
|
}
|
|
105
129
|
}
|
|
106
130
|
|
|
@@ -223,5 +247,6 @@ class ScreenshotDetectorModule(private val reactContext: ReactApplicationContext
|
|
|
223
247
|
|
|
224
248
|
companion object {
|
|
225
249
|
const val NAME = "RNBugReporterScreenshot"
|
|
250
|
+
private const val PERMISSION_REQUEST_CODE = 7322
|
|
226
251
|
}
|
|
227
252
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-bug-reporter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Universal React Native bug reporter: auto-detect screenshots, annotate, collect device/app/user context, and ship to Supabase (Postgres + Storage) with Edge Function email.",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|