react-native-frame-capture 1.0.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.
Files changed (77) hide show
  1. package/FrameCapture.podspec +21 -0
  2. package/LICENSE +20 -0
  3. package/README.md +158 -0
  4. package/android/build.gradle +77 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/src/main/AndroidManifest.xml +20 -0
  7. package/android/src/main/java/com/framecapture/CaptureManager.kt +831 -0
  8. package/android/src/main/java/com/framecapture/Constants.kt +196 -0
  9. package/android/src/main/java/com/framecapture/ErrorHandler.kt +165 -0
  10. package/android/src/main/java/com/framecapture/FrameCaptureModule.kt +653 -0
  11. package/android/src/main/java/com/framecapture/FrameCapturePackage.kt +32 -0
  12. package/android/src/main/java/com/framecapture/OverlayRenderer.kt +423 -0
  13. package/android/src/main/java/com/framecapture/PermissionHandler.kt +150 -0
  14. package/android/src/main/java/com/framecapture/ScreenCaptureService.kt +366 -0
  15. package/android/src/main/java/com/framecapture/StorageManager.kt +221 -0
  16. package/android/src/main/java/com/framecapture/capture/BitmapProcessor.kt +157 -0
  17. package/android/src/main/java/com/framecapture/capture/CaptureEventEmitter.kt +120 -0
  18. package/android/src/main/java/com/framecapture/models/CaptureModels.kt +302 -0
  19. package/android/src/main/java/com/framecapture/models/EnumsAndExtensions.kt +60 -0
  20. package/android/src/main/java/com/framecapture/models/OverlayModels.kt +154 -0
  21. package/android/src/main/java/com/framecapture/service/CaptureNotificationManager.kt +286 -0
  22. package/android/src/main/java/com/framecapture/storage/StorageStrategies.kt +317 -0
  23. package/android/src/main/java/com/framecapture/utils/ValidationUtils.kt +379 -0
  24. package/app.plugin.js +1 -0
  25. package/ios/FrameCapture.h +5 -0
  26. package/ios/FrameCapture.mm +21 -0
  27. package/lib/module/NativeFrameCapture.js +24 -0
  28. package/lib/module/NativeFrameCapture.js.map +1 -0
  29. package/lib/module/api.js +146 -0
  30. package/lib/module/api.js.map +1 -0
  31. package/lib/module/constants.js +67 -0
  32. package/lib/module/constants.js.map +1 -0
  33. package/lib/module/errors.js +19 -0
  34. package/lib/module/errors.js.map +1 -0
  35. package/lib/module/events.js +58 -0
  36. package/lib/module/events.js.map +1 -0
  37. package/lib/module/index.js +24 -0
  38. package/lib/module/index.js.map +1 -0
  39. package/lib/module/normalize.js +51 -0
  40. package/lib/module/normalize.js.map +1 -0
  41. package/lib/module/package.json +1 -0
  42. package/lib/module/types.js +165 -0
  43. package/lib/module/types.js.map +1 -0
  44. package/lib/module/validation.js +190 -0
  45. package/lib/module/validation.js.map +1 -0
  46. package/lib/typescript/package.json +1 -0
  47. package/lib/typescript/plugin/src/index.d.ts +4 -0
  48. package/lib/typescript/plugin/src/index.d.ts.map +1 -0
  49. package/lib/typescript/src/NativeFrameCapture.d.ts +75 -0
  50. package/lib/typescript/src/NativeFrameCapture.d.ts.map +1 -0
  51. package/lib/typescript/src/api.d.ts +66 -0
  52. package/lib/typescript/src/api.d.ts.map +1 -0
  53. package/lib/typescript/src/constants.d.ts +41 -0
  54. package/lib/typescript/src/constants.d.ts.map +1 -0
  55. package/lib/typescript/src/errors.d.ts +14 -0
  56. package/lib/typescript/src/errors.d.ts.map +1 -0
  57. package/lib/typescript/src/events.d.ts +30 -0
  58. package/lib/typescript/src/events.d.ts.map +1 -0
  59. package/lib/typescript/src/index.d.ts +12 -0
  60. package/lib/typescript/src/index.d.ts.map +1 -0
  61. package/lib/typescript/src/normalize.d.ts +43 -0
  62. package/lib/typescript/src/normalize.d.ts.map +1 -0
  63. package/lib/typescript/src/types.d.ts +247 -0
  64. package/lib/typescript/src/types.d.ts.map +1 -0
  65. package/lib/typescript/src/validation.d.ts +15 -0
  66. package/lib/typescript/src/validation.d.ts.map +1 -0
  67. package/package.json +196 -0
  68. package/plugin/build/index.js +48 -0
  69. package/src/NativeFrameCapture.ts +86 -0
  70. package/src/api.ts +189 -0
  71. package/src/constants.ts +69 -0
  72. package/src/errors.ts +21 -0
  73. package/src/events.ts +61 -0
  74. package/src/index.tsx +31 -0
  75. package/src/normalize.ts +81 -0
  76. package/src/types.ts +327 -0
  77. package/src/validation.ts +321 -0
@@ -0,0 +1,196 @@
1
+ package com.framecapture
2
+
3
+ /**
4
+ * Constants for the FrameCapture library
5
+ *
6
+ * Centralized configuration values including:
7
+ * - Capture constraints (intervals, quality, formats)
8
+ * - Storage thresholds
9
+ * - Notification configuration
10
+ * - Service actions and intent extras
11
+ * - Event names for JavaScript communication
12
+ * - Resource management timeouts
13
+ */
14
+ object Constants {
15
+
16
+ // Capture interval constraints (in milliseconds)
17
+ const val MIN_INTERVAL = 100L
18
+ const val MAX_INTERVAL = 60000L
19
+ const val DEFAULT_INTERVAL = 1000L
20
+
21
+ // Image quality constraints (0-100)
22
+ const val MIN_QUALITY = 0
23
+ const val MAX_QUALITY = 100
24
+ const val DEFAULT_QUALITY = 80
25
+
26
+ // Default image format
27
+ const val DEFAULT_FORMAT = "jpeg"
28
+
29
+ // Capture mode strings
30
+ const val CAPTURE_MODE_INTERVAL = "interval"
31
+
32
+ // Storage warning threshold (100MB in bytes) - DEFAULT VALUE
33
+ const val DEFAULT_STORAGE_WARNING_THRESHOLD = 100L * 1024L * 1024L
34
+
35
+ // Storage location identifiers
36
+ const val STORAGE_LOCATION_PUBLIC = "public"
37
+ const val STORAGE_LOCATION_PRIVATE = "private"
38
+
39
+ // File naming - DEFAULT VALUES
40
+ const val DEFAULT_FILENAME_PREFIX = "capture_"
41
+ const val DEFAULT_FILENAME_DATE_FORMAT = "yyyyMMdd_HHmmss_SSS"
42
+ const val DEFAULT_FILENAME_FRAME_PADDING = 5 // Number of digits for frame number
43
+
44
+ // Image format extensions
45
+ const val FORMAT_EXTENSION_PNG = "png"
46
+ const val FORMAT_EXTENSION_JPEG = "jpg"
47
+
48
+ // MIME types
49
+ const val MIME_TYPE_PNG = "image/png"
50
+ const val MIME_TYPE_JPEG = "image/jpeg"
51
+
52
+ // Storage directories
53
+ const val TEMP_FRAMES_DIRECTORY = "captured_frames"
54
+
55
+ // Foreground service notification
56
+ const val NOTIFICATION_ID = 1001
57
+ const val CHANNEL_ID = "screen_capture_channel"
58
+ const val CHANNEL_NAME = "Screen Capture"
59
+ const val CHANNEL_DESCRIPTION = "Notifications for screen capture service"
60
+ const val CUSTOM_CHANNEL_ID_PREFIX = "screen_capture_custom_"
61
+
62
+ // Notification priority levels
63
+ const val NOTIFICATION_PRIORITY_HIGH = "high"
64
+ const val NOTIFICATION_PRIORITY_DEFAULT = "default"
65
+ const val NOTIFICATION_PRIORITY_LOW = "low"
66
+
67
+ // Notification action labels
68
+ const val NOTIFICATION_ACTION_PAUSE = "Pause"
69
+ const val NOTIFICATION_ACTION_RESUME = "Resume"
70
+ const val NOTIFICATION_ACTION_STOP = "Stop"
71
+
72
+ // Notification default values
73
+ const val NOTIFICATION_DEFAULT_TITLE = "Screen Capture Active"
74
+ const val NOTIFICATION_DEFAULT_DESCRIPTION = "Capturing frames..."
75
+ const val NOTIFICATION_DEFAULT_PAUSED_TITLE = "Screen Capture Paused"
76
+ const val NOTIFICATION_DEFAULT_PAUSED_DESCRIPTION = "{frameCount} frames captured"
77
+ const val NOTIFICATION_DEFAULT_UPDATE_INTERVAL = 10
78
+
79
+ // Notification action request codes
80
+ const val NOTIFICATION_REQUEST_CODE_STOP = 0
81
+ const val NOTIFICATION_REQUEST_CODE_PAUSE_RESUME = 1
82
+
83
+ // Notification template variable
84
+ const val NOTIFICATION_TEMPLATE_FRAME_COUNT = "{frameCount}"
85
+
86
+ // Service actions
87
+ const val ACTION_START_CAPTURE = "com.framecapture.START_CAPTURE"
88
+ const val ACTION_STOP_CAPTURE = "com.framecapture.STOP_CAPTURE"
89
+ const val ACTION_PAUSE_CAPTURE = "com.framecapture.PAUSE_CAPTURE"
90
+ const val ACTION_RESUME_CAPTURE = "com.framecapture.RESUME_CAPTURE"
91
+
92
+ // Intent extras
93
+ const val EXTRA_CAPTURE_OPTIONS = "capture_options"
94
+ const val EXTRA_PROJECTION_DATA = "projection_data"
95
+
96
+ // MediaProjection request code
97
+ const val REQUEST_MEDIA_PROJECTION = 1001
98
+
99
+ // Module name
100
+ const val MODULE_NAME = "FrameCapture"
101
+
102
+ // Event names
103
+ const val EVENT_FRAME_CAPTURED = "onFrameCaptured"
104
+ const val EVENT_CAPTURE_ERROR = "onCaptureError"
105
+ const val EVENT_CAPTURE_STOP = "onCaptureStop"
106
+ const val EVENT_CAPTURE_START = "onCaptureStart"
107
+ const val EVENT_STORAGE_WARNING = "onStorageWarning"
108
+ const val EVENT_CAPTURE_PAUSE = "onCapturePause"
109
+ const val EVENT_CAPTURE_RESUME = "onCaptureResume"
110
+ const val EVENT_OVERLAY_ERROR = "onOverlayError"
111
+
112
+ // Resource cleanup timeout (in milliseconds) - DEFAULT VALUES
113
+ const val DEFAULT_EXECUTOR_SHUTDOWN_TIMEOUT = 5000L
114
+ const val DEFAULT_EXECUTOR_FORCED_SHUTDOWN_TIMEOUT = 1000L
115
+
116
+ // ImageReader buffer count - DEFAULT VALUE
117
+ const val DEFAULT_IMAGE_READER_MAX_IMAGES = 2
118
+
119
+ // Image format constants
120
+ const val RGBA_BYTES_PER_PIXEL = 4
121
+
122
+ // Resolution scaling constraints
123
+ const val MIN_SCALE_RESOLUTION = 0.1f
124
+ const val MAX_SCALE_RESOLUTION = 1.0f
125
+
126
+ // Overlay rendering - DEFAULT VALUES
127
+ const val DEFAULT_OVERLAY_IMAGE_CACHE_SIZE = 10 * 1024 * 1024 // 10MB in bytes
128
+ const val OVERLAY_DEFAULT_PADDING = 10 // Padding from edges in pixels
129
+ const val MIN_OVERLAY_OPACITY = 0.0f
130
+ const val MAX_OVERLAY_OPACITY = 1.0f
131
+
132
+ // Overlay types
133
+ const val OVERLAY_TYPE_TEXT = "text"
134
+ const val OVERLAY_TYPE_IMAGE = "image"
135
+
136
+ // Overlay position presets
137
+ const val POSITION_TOP_LEFT = "top-left"
138
+ const val POSITION_TOP_RIGHT = "top-right"
139
+ const val POSITION_BOTTOM_LEFT = "bottom-left"
140
+ const val POSITION_BOTTOM_RIGHT = "bottom-right"
141
+ const val POSITION_CENTER = "center"
142
+
143
+ // Overlay position units
144
+ const val POSITION_UNIT_PERCENTAGE = "percentage"
145
+ const val POSITION_UNIT_PIXELS = "pixels"
146
+
147
+ // Template variables
148
+ const val TEMPLATE_VAR_FRAME_NUMBER = "{frameNumber}"
149
+ const val TEMPLATE_VAR_SESSION_ID = "{sessionId}"
150
+ const val TEMPLATE_VAR_TIMESTAMP = "{timestamp}"
151
+
152
+ // Timestamp format for overlays
153
+ const val OVERLAY_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
154
+ const val OVERLAY_TIMESTAMP_TIMEZONE = "UTC"
155
+
156
+ // Text style values
157
+ const val TEXT_WEIGHT_BOLD = "bold"
158
+ const val TEXT_WEIGHT_NORMAL = "normal"
159
+ const val TEXT_ALIGN_LEFT = "left"
160
+ const val TEXT_ALIGN_CENTER = "center"
161
+ const val TEXT_ALIGN_RIGHT = "right"
162
+
163
+ // Text style defaults
164
+ const val TEXT_DEFAULT_FONT_SIZE = 14
165
+ const val TEXT_DEFAULT_COLOR = "#FFFFFF"
166
+ const val TEXT_DEFAULT_BACKGROUND_COLOR = "#00000080"
167
+ const val TEXT_DEFAULT_PADDING = 8
168
+
169
+ // Image overlay defaults
170
+ const val IMAGE_DEFAULT_OPACITY = 1.0f
171
+
172
+ // URI schemes
173
+ const val URI_SCHEME_FILE = "file"
174
+ const val URI_SCHEME_CONTENT = "content"
175
+
176
+ // Resource types
177
+ const val RESOURCE_TYPE_DRAWABLE = "drawable"
178
+
179
+ // Hex color validation
180
+ const val HEX_COLOR_LENGTH_SHORT = 3 // #RGB
181
+ const val HEX_COLOR_LENGTH_MEDIUM = 6 // #RRGGBB
182
+ const val HEX_COLOR_LENGTH_LONG = 8 // #AARRGGBB
183
+
184
+ // Error detail keys
185
+ const val ERROR_DETAIL_PERMISSION = "permission"
186
+ const val ERROR_DETAIL_CURRENT_STATE = "currentState"
187
+ const val ERROR_DETAIL_AVAILABLE_SPACE = "availableSpace"
188
+ const val ERROR_DETAIL_HEAP_SIZE = "heapSize"
189
+ const val ERROR_DETAIL_USED_MEMORY = "usedMemory"
190
+ const val ERROR_DETAIL_ERRORS = "errors"
191
+ const val ERROR_DETAIL_ERROR_TYPE = "errorType"
192
+
193
+ // Event payload keys
194
+ const val EVENT_KEY_AVAILABLE_SPACE = "availableSpace"
195
+ const val EVENT_KEY_THRESHOLD = "threshold"
196
+ }
@@ -0,0 +1,165 @@
1
+ package com.framecapture
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.Arguments
5
+ import com.facebook.react.bridge.Promise
6
+ import com.facebook.react.bridge.WritableMap
7
+ import com.framecapture.models.CaptureState
8
+ import com.framecapture.models.ErrorCode
9
+ import java.io.IOException
10
+
11
+ /**
12
+ * Handles error classification, formatting, and reporting
13
+ *
14
+ * Provides consistent error handling across the module by:
15
+ * - Classifying exceptions into appropriate ErrorCode values
16
+ * - Formatting error details for JavaScript consumption
17
+ * - Coordinating error emission and cleanup
18
+ *
19
+ * Uses dependency injection for storage space and capture state checks
20
+ * to avoid tight coupling with other components.
21
+ */
22
+ class ErrorHandler(
23
+ private val getStorageSpace: () -> Long,
24
+ private val getCaptureState: () -> CaptureState
25
+ ) {
26
+
27
+ /**
28
+ * Classifies exception into error code, message, and details
29
+ *
30
+ * Maps common exceptions to appropriate ErrorCode values and provides
31
+ * contextual details for debugging (e.g., available storage, current state).
32
+ *
33
+ * @param error The exception to classify
34
+ * @return Triple of (ErrorCode, error message, optional details map)
35
+ */
36
+ fun classifyError(error: Exception): Triple<ErrorCode, String, Map<String, Any>?> {
37
+ return when (error) {
38
+ is SecurityException -> Triple(
39
+ ErrorCode.PERMISSION_DENIED,
40
+ "Required permission not granted: ${error.message}",
41
+ mapOf(Constants.ERROR_DETAIL_PERMISSION to "MEDIA_PROJECTION")
42
+ )
43
+
44
+ is IllegalStateException -> Triple(
45
+ ErrorCode.ALREADY_CAPTURING,
46
+ "Capture session is already active: ${error.message}",
47
+ mapOf(Constants.ERROR_DETAIL_CURRENT_STATE to getCaptureState().value)
48
+ )
49
+
50
+ is IllegalArgumentException -> Triple(
51
+ ErrorCode.INVALID_OPTIONS,
52
+ "Invalid configuration: ${error.message}",
53
+ null
54
+ )
55
+
56
+ is IOException -> Triple(
57
+ ErrorCode.STORAGE_ERROR,
58
+ "Failed to save frame: ${error.message}",
59
+ mapOf(Constants.ERROR_DETAIL_AVAILABLE_SPACE to getStorageSpace())
60
+ )
61
+
62
+ is OutOfMemoryError -> Triple(
63
+ ErrorCode.SYSTEM_ERROR,
64
+ "Out of memory during capture",
65
+ mapOf(Constants.ERROR_DETAIL_HEAP_SIZE to Runtime.getRuntime().maxMemory())
66
+ )
67
+
68
+ else -> Triple(
69
+ ErrorCode.SYSTEM_ERROR,
70
+ "Unexpected error: ${error.message ?: "Unknown error"}",
71
+ null
72
+ )
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Creates error map for promise rejection and event emission
78
+ *
79
+ * Formats error information into a WritableMap that can be sent to JavaScript.
80
+ * Handles various data types in the details map (String, Int, Long, Boolean, List).
81
+ *
82
+ * @param code Error code enum value
83
+ * @param message Human-readable error message
84
+ * @param details Optional map of additional error context
85
+ * @return WritableMap formatted for React Native bridge
86
+ */
87
+ fun createErrorMap(
88
+ code: ErrorCode,
89
+ message: String,
90
+ details: Map<String, Any>?
91
+ ): WritableMap {
92
+ return Arguments.createMap().apply {
93
+ putString("code", code.value)
94
+ putString("message", message)
95
+ details?.let { detailsMap ->
96
+ val detailsWritableMap = Arguments.createMap()
97
+ detailsMap.forEach { (key, value) ->
98
+ when (value) {
99
+ is String -> detailsWritableMap.putString(key, value)
100
+ is Int -> detailsWritableMap.putInt(key, value)
101
+ is Double -> detailsWritableMap.putDouble(key, value)
102
+ is Long -> detailsWritableMap.putDouble(key, value.toDouble())
103
+ is Boolean -> detailsWritableMap.putBoolean(key, value)
104
+ is List<*> -> {
105
+ val array = Arguments.createArray()
106
+ value.forEach { item ->
107
+ when (item) {
108
+ is String -> array.pushString(item)
109
+ else -> array.pushString(item.toString())
110
+ }
111
+ }
112
+ detailsWritableMap.putArray(key, array)
113
+ }
114
+ else -> detailsWritableMap.putString(key, value.toString())
115
+ }
116
+ }
117
+ putMap("details", detailsWritableMap)
118
+ }
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Handles error with logging, classification, and promise rejection
124
+ *
125
+ * Orchestrates the complete error handling flow:
126
+ * 1. Classifies the error
127
+ * 2. Logs for debugging
128
+ * 3. Performs cleanup via callback
129
+ * 4. Emits error event to JavaScript
130
+ * 5. Rejects promise if provided
131
+ *
132
+ * @param error The exception that occurred
133
+ * @param promise Optional promise to reject
134
+ * @param onEmitError Callback to emit error event
135
+ * @param onCleanup Callback to perform cleanup
136
+ */
137
+ fun handleError(
138
+ error: Exception,
139
+ promise: Promise?,
140
+ onEmitError: (ErrorCode, String, Map<String, Any>?) -> Unit,
141
+ onCleanup: () -> Unit
142
+ ) {
143
+ val (code, message, details) = classifyError(error)
144
+
145
+ // Log for debugging
146
+ Log.e(TAG, "Capture error: $message", error)
147
+
148
+ // Clean up resources on error
149
+ try {
150
+ onCleanup()
151
+ } catch (cleanupError: Exception) {
152
+ Log.e(TAG, "Error during cleanup: ${cleanupError.message}", cleanupError)
153
+ }
154
+
155
+ // Emit error event
156
+ onEmitError(code, message, details)
157
+
158
+ // Reject promise if provided
159
+ promise?.reject(code.value, message, createErrorMap(code, message, details))
160
+ }
161
+
162
+ companion object {
163
+ private const val TAG = "ErrorHandler"
164
+ }
165
+ }