@multiplayer-app/session-recorder-react-native 0.0.1-beta.7 → 0.0.1-beta.9
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/app.plugin.js +42 -0
- package/docs/NATIVE_MODULE_SETUP.md +177 -0
- package/ios/SessionRecorderNative.podspec +5 -0
- package/package.json +10 -1
- package/plugin/package.json +20 -0
- package/plugin/src/index.js +42 -0
- package/android/src/main/AndroidManifest.xml +0 -2
- package/android/src/main/java/com/multiplayer/sessionrecorder/ScreenMaskingModule.kt +0 -202
- package/android/src/main/java/com/multiplayer/sessionrecorder/ScreenMaskingPackage.kt +0 -16
- package/android/src/main/java/com/multiplayer/sessionrecorder/SessionRecorderModule.kt +0 -202
- package/android/src/main/java/com/multiplayer/sessionrecorder/SessionRecorderPackage.kt +0 -16
- package/babel.config.js +0 -13
- package/docs/AUTO_METADATA_DETECTION.md +0 -108
- package/docs/TROUBLESHOOTING.md +0 -168
- package/ios/ScreenMasking.m +0 -12
- package/ios/ScreenMasking.podspec +0 -21
- package/ios/ScreenMasking.swift +0 -205
- package/ios/SessionRecorder.podspec +0 -21
- package/scripts/generate-app-metadata.js +0 -173
- package/src/components/GestureCaptureWrapper/GestureCaptureWrapper.tsx +0 -86
- package/src/components/GestureCaptureWrapper/index.ts +0 -1
- package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +0 -72
- package/src/components/ScreenRecorderView/index.ts +0 -1
- package/src/components/SessionRecorderWidget/FinalPopover.tsx +0 -62
- package/src/components/SessionRecorderWidget/FloatingButton.tsx +0 -136
- package/src/components/SessionRecorderWidget/InitialPopover.tsx +0 -89
- package/src/components/SessionRecorderWidget/ModalContainer.tsx +0 -128
- package/src/components/SessionRecorderWidget/ModalHeader.tsx +0 -24
- package/src/components/SessionRecorderWidget/SessionRecorderWidget.tsx +0 -109
- package/src/components/SessionRecorderWidget/icons.tsx +0 -52
- package/src/components/SessionRecorderWidget/index.ts +0 -3
- package/src/components/SessionRecorderWidget/styles.ts +0 -150
- package/src/components/index.ts +0 -3
- package/src/config/constants.ts +0 -60
- package/src/config/defaults.ts +0 -83
- package/src/config/index.ts +0 -6
- package/src/config/masking.ts +0 -28
- package/src/config/session-recorder.ts +0 -55
- package/src/config/validators.ts +0 -31
- package/src/context/SessionRecorderContext.tsx +0 -53
- package/src/index.ts +0 -9
- package/src/native/ScreenMasking.ts +0 -34
- package/src/native/SessionRecorderNative.ts +0 -34
- package/src/otel/helpers.ts +0 -275
- package/src/otel/index.ts +0 -138
- package/src/otel/instrumentations/index.ts +0 -115
- package/src/patch/index.ts +0 -1
- package/src/patch/xhr.ts +0 -141
- package/src/recorder/eventExporter.ts +0 -141
- package/src/recorder/gestureRecorder.ts +0 -498
- package/src/recorder/index.ts +0 -179
- package/src/recorder/navigationTracker.ts +0 -449
- package/src/recorder/screenRecorder.ts +0 -527
- package/src/services/api.service.ts +0 -203
- package/src/services/screenMaskingService.ts +0 -118
- package/src/services/storage.service.ts +0 -199
- package/src/session-recorder.ts +0 -606
- package/src/types/expo.d.ts +0 -23
- package/src/types/index.ts +0 -28
- package/src/types/session-recorder.ts +0 -429
- package/src/types/session.ts +0 -65
- package/src/utils/app-metadata.ts +0 -31
- package/src/utils/index.ts +0 -8
- package/src/utils/logger.ts +0 -225
- package/src/utils/nativeModuleTest.ts +0 -60
- package/src/utils/platform.ts +0 -384
- package/src/utils/request-utils.ts +0 -61
- package/src/utils/rrweb-events.ts +0 -309
- package/src/utils/session.ts +0 -18
- package/src/utils/time.ts +0 -17
- package/src/utils/type-utils.ts +0 -75
- package/src/version.ts +0 -1
- package/tsconfig.json +0 -24
- /package/ios/{SessionRecorder.m → SessionRecorderNative.m} +0 -0
- /package/ios/{SessionRecorder.swift → SessionRecorderNative.swift} +0 -0
package/app.plugin.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const { withDangerousMod } = require('@expo/config-plugins')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
|
|
5
|
+
const withSessionRecorder = (config) => {
|
|
6
|
+
return withDangerousMod(config, [
|
|
7
|
+
'ios',
|
|
8
|
+
async (config) => {
|
|
9
|
+
const projectRoot = config.modRequest.projectRoot
|
|
10
|
+
const platformRoot = config.modRequest.platformProjectRoot
|
|
11
|
+
|
|
12
|
+
// Ensure the Podfile includes our native module
|
|
13
|
+
const podfilePath = path.join(platformRoot, 'Podfile')
|
|
14
|
+
|
|
15
|
+
if (fs.existsSync(podfilePath)) {
|
|
16
|
+
let podfileContent = fs.readFileSync(podfilePath, 'utf8')
|
|
17
|
+
|
|
18
|
+
// Check if our pod is already included
|
|
19
|
+
if (!podfileContent.includes('SessionRecorderNative')) {
|
|
20
|
+
// Add our pod to the Podfile
|
|
21
|
+
const podEntry = ` pod 'SessionRecorderNative', :path => '../node_modules/@multiplayer-app/session-recorder-react-native/ios'\n`
|
|
22
|
+
|
|
23
|
+
// Find the target section and add our pod
|
|
24
|
+
const targetRegex = /(target ['"][^'"]+['"] do)/
|
|
25
|
+
if (targetRegex.test(podfileContent)) {
|
|
26
|
+
podfileContent = podfileContent.replace(targetRegex, `$1\n${podEntry}`)
|
|
27
|
+
} else {
|
|
28
|
+
// If no target found, add at the end before the end statement
|
|
29
|
+
podfileContent = podfileContent.replace(/(end\s*$)/, `${podEntry}$1`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fs.writeFileSync(podfilePath, podfileContent)
|
|
33
|
+
console.log('✅ Added SessionRecorderNative to Podfile')
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return config
|
|
38
|
+
}
|
|
39
|
+
])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = withSessionRecorder
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Native Module Setup Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to properly set up the Session Recorder React Native library with native modules in both React Native and Expo projects.
|
|
4
|
+
|
|
5
|
+
## Issues Fixed
|
|
6
|
+
|
|
7
|
+
### 1. Missing iOS Native Files in NPM Package
|
|
8
|
+
|
|
9
|
+
**Problem**: The iOS native implementation files (`SessionRecorderNative.m` and `SessionRecorderNative.swift`) were not being included in the published npm package.
|
|
10
|
+
|
|
11
|
+
**Solution**: Updated `.npmignore` to exclude source files but include the compiled native modules.
|
|
12
|
+
|
|
13
|
+
### 2. Auto-linking Configuration
|
|
14
|
+
|
|
15
|
+
**Problem**: The library wasn't properly configured for auto-linking in Expo projects.
|
|
16
|
+
|
|
17
|
+
**Solution**:
|
|
18
|
+
|
|
19
|
+
- Added `expo` configuration section to `package.json`
|
|
20
|
+
- Created an Expo plugin for automatic Podfile configuration
|
|
21
|
+
- Updated podspec with proper Expo compatibility settings
|
|
22
|
+
|
|
23
|
+
### 3. Podspec Configuration
|
|
24
|
+
|
|
25
|
+
**Problem**: The podspec wasn't optimized for Expo compatibility.
|
|
26
|
+
|
|
27
|
+
**Solution**: Added proper header search paths and Expo-specific configurations.
|
|
28
|
+
|
|
29
|
+
## Setup Instructions
|
|
30
|
+
|
|
31
|
+
### For Expo Projects
|
|
32
|
+
|
|
33
|
+
1. **Install the package**:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @multiplayer-app/session-recorder-react-native
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
2. **Add to app.json/app.config.js**:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"expo": {
|
|
44
|
+
"plugins": ["@multiplayer-app/session-recorder-react-native"]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The plugin will automatically be detected via the `app.plugin.js` file in the package root.
|
|
50
|
+
|
|
51
|
+
3. **Run prebuild** (if using development build):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx expo prebuild --clean
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
4. **Install iOS dependencies**:
|
|
58
|
+
```bash
|
|
59
|
+
cd ios && pod install && cd ..
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### For React Native CLI Projects
|
|
63
|
+
|
|
64
|
+
1. **Install the package**:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install @multiplayer-app/session-recorder-react-native
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
2. **iOS Setup**:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
cd ios && pod install && cd ..
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
3. **Android Setup**: No additional steps required (auto-linking handles it)
|
|
77
|
+
|
|
78
|
+
## Troubleshooting
|
|
79
|
+
|
|
80
|
+
### "Pod SessionRecorderNative files are missing"
|
|
81
|
+
|
|
82
|
+
This error occurs when the iOS native files aren't properly included in the package or auto-linking fails.
|
|
83
|
+
|
|
84
|
+
**Solutions**:
|
|
85
|
+
|
|
86
|
+
1. **Check package installation**:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
ls node_modules/@multiplayer-app/session-recorder-react-native/ios/
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Should show: `SessionRecorderNative.m`, `SessionRecorderNative.podspec`, `SessionRecorderNative.swift`
|
|
93
|
+
|
|
94
|
+
2. **Clear and reinstall**:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
rm -rf node_modules
|
|
98
|
+
npm install
|
|
99
|
+
cd ios && pod install --repo-update && cd ..
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
3. **For Expo projects, ensure plugin is configured**:
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"expo": {
|
|
106
|
+
"plugins": ["@multiplayer-app/session-recorder-react-native"]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Auto-linking Issues
|
|
112
|
+
|
|
113
|
+
If auto-linking doesn't work:
|
|
114
|
+
|
|
115
|
+
1. **Check react-native.config.js** (should be automatically created):
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
module.exports = {
|
|
119
|
+
dependencies: {
|
|
120
|
+
'@multiplayer-app/session-recorder-react-native': {
|
|
121
|
+
platforms: {
|
|
122
|
+
android: {
|
|
123
|
+
sourceDir: '../android',
|
|
124
|
+
packageImportPath: 'import com.multiplayer.sessionrecorder.SessionRecorderPackage;'
|
|
125
|
+
},
|
|
126
|
+
ios: {
|
|
127
|
+
podspecPath: '../ios/SessionRecorderNative.podspec'
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
2. **Manual linking** (if auto-linking fails):
|
|
136
|
+
- iOS: Add to Podfile manually
|
|
137
|
+
- Android: Add to MainApplication.java manually
|
|
138
|
+
|
|
139
|
+
## File Structure
|
|
140
|
+
|
|
141
|
+
The package includes these native files:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
ios/
|
|
145
|
+
├── SessionRecorderNative.m # Objective-C bridge
|
|
146
|
+
├── SessionRecorderNative.podspec # CocoaPods specification
|
|
147
|
+
└── SessionRecorderNative.swift # Swift implementation
|
|
148
|
+
|
|
149
|
+
android/
|
|
150
|
+
├── build.gradle # Android build configuration
|
|
151
|
+
└── src/main/java/com/multiplayer/sessionrecorder/
|
|
152
|
+
├── SessionRecorderModule.kt # Main Android module
|
|
153
|
+
├── SessionRecorderPackage.kt # Package registration
|
|
154
|
+
├── ScreenMaskingModule.kt # Screen masking module
|
|
155
|
+
└── ScreenMaskingPackage.kt # Screen masking package
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Verification
|
|
159
|
+
|
|
160
|
+
To verify the setup is working:
|
|
161
|
+
|
|
162
|
+
1. **Check iOS**:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
cd ios && pod list | grep SessionRecorderNative
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
2. **Check Android**: Look for the package in `android/app/src/main/java/`
|
|
169
|
+
|
|
170
|
+
3. **Test in code**:
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
import { SessionRecorderNative } from '@multiplayer-app/session-recorder-react-native'
|
|
174
|
+
|
|
175
|
+
// This should not throw an error
|
|
176
|
+
console.log('Native module loaded:', SessionRecorderNative)
|
|
177
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplayer-app/session-recorder-react-native",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.9",
|
|
4
4
|
"description": "Multiplayer Fullstack Session Recorder for React Native",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Multiplayer Software, Inc.",
|
|
@@ -97,5 +97,14 @@
|
|
|
97
97
|
"sourceDir": "./android",
|
|
98
98
|
"packageImportPath": "import com.multiplayer.sessionrecorder.SessionRecorderPackage;"
|
|
99
99
|
}
|
|
100
|
+
},
|
|
101
|
+
"expo": {
|
|
102
|
+
"ios": {
|
|
103
|
+
"podspecPath": "./ios/SessionRecorderNative.podspec"
|
|
104
|
+
},
|
|
105
|
+
"android": {
|
|
106
|
+
"sourceDir": "./android",
|
|
107
|
+
"packageImportPath": "import com.multiplayer.sessionrecorder.SessionRecorderPackage;"
|
|
108
|
+
}
|
|
100
109
|
}
|
|
101
110
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@multiplayer-app/session-recorder-react-native-plugin",
|
|
3
|
+
"version": "0.0.1-beta.9",
|
|
4
|
+
"description": "Expo plugin for Session Recorder React Native",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"expo",
|
|
8
|
+
"plugin",
|
|
9
|
+
"session-recorder",
|
|
10
|
+
"react-native"
|
|
11
|
+
],
|
|
12
|
+
"author": "Multiplayer Software, Inc.",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@expo/config-plugins": "^7.0.0"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"expo": ">=49.0.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const { withDangerousMod } = require('@expo/config-plugins')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
|
|
5
|
+
const withSessionRecorder = (config) => {
|
|
6
|
+
return withDangerousMod(config, [
|
|
7
|
+
'ios',
|
|
8
|
+
async (config) => {
|
|
9
|
+
const projectRoot = config.modRequest.projectRoot
|
|
10
|
+
const platformRoot = config.modRequest.platformProjectRoot
|
|
11
|
+
|
|
12
|
+
// Ensure the Podfile includes our native module
|
|
13
|
+
const podfilePath = path.join(platformRoot, 'Podfile')
|
|
14
|
+
|
|
15
|
+
if (fs.existsSync(podfilePath)) {
|
|
16
|
+
let podfileContent = fs.readFileSync(podfilePath, 'utf8')
|
|
17
|
+
|
|
18
|
+
// Check if our pod is already included
|
|
19
|
+
if (!podfileContent.includes('SessionRecorderNative')) {
|
|
20
|
+
// Add our pod to the Podfile
|
|
21
|
+
const podEntry = ` pod 'SessionRecorderNative', :path => '../node_modules/@multiplayer-app/session-recorder-react-native/ios'\n`
|
|
22
|
+
|
|
23
|
+
// Find the target section and add our pod
|
|
24
|
+
const targetRegex = /(target ['"][^'"]+['"] do)/
|
|
25
|
+
if (targetRegex.test(podfileContent)) {
|
|
26
|
+
podfileContent = podfileContent.replace(targetRegex, `$1\n${podEntry}`)
|
|
27
|
+
} else {
|
|
28
|
+
// If no target found, add at the end before the end statement
|
|
29
|
+
podfileContent = podfileContent.replace(/(end\s*$)/, `${podEntry}$1`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fs.writeFileSync(podfilePath, podfileContent)
|
|
33
|
+
console.log('✅ Added SessionRecorderNative to Podfile')
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return config
|
|
38
|
+
}
|
|
39
|
+
])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = withSessionRecorder
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
package com.multiplayer.sessionrecorder
|
|
2
|
-
|
|
3
|
-
import android.app.Activity
|
|
4
|
-
import android.graphics.*
|
|
5
|
-
import android.util.Base64
|
|
6
|
-
import android.view.View
|
|
7
|
-
import android.widget.EditText
|
|
8
|
-
import android.widget.TextView
|
|
9
|
-
import com.facebook.react.bridge.*
|
|
10
|
-
import java.io.ByteArrayOutputStream
|
|
11
|
-
|
|
12
|
-
class SessionRecorderModule(reactContext: ReactApplicationContext) :
|
|
13
|
-
ReactContextBaseJavaModule(reactContext) {
|
|
14
|
-
|
|
15
|
-
override fun getName() = "SessionRecorder"
|
|
16
|
-
|
|
17
|
-
@ReactMethod
|
|
18
|
-
fun captureAndMask(promise: Promise) {
|
|
19
|
-
val activity = currentActivity ?: return promise.reject("NO_ACTIVITY", "No activity found")
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
val maskedImage = captureAndMaskScreen(activity, null)
|
|
23
|
-
promise.resolve(maskedImage)
|
|
24
|
-
} catch (e: Exception) {
|
|
25
|
-
promise.reject("CAPTURE_FAILED", "Failed to capture and mask screen", e)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
@ReactMethod
|
|
30
|
-
fun captureAndMaskWithOptions(options: ReadableMap, promise: Promise) {
|
|
31
|
-
val activity = currentActivity ?: return promise.reject("NO_ACTIVITY", "No activity found")
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
val maskedImage = captureAndMaskScreen(activity, options)
|
|
35
|
-
promise.resolve(maskedImage)
|
|
36
|
-
} catch (e: Exception) {
|
|
37
|
-
promise.reject("CAPTURE_FAILED", "Failed to capture and mask screen", e)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
private fun captureAndMaskScreen(activity: Activity, options: ReadableMap?): String {
|
|
42
|
-
val rootView = activity.window.decorView.rootView
|
|
43
|
-
|
|
44
|
-
// Enable drawing cache
|
|
45
|
-
rootView.isDrawingCacheEnabled = true
|
|
46
|
-
rootView.buildDrawingCache()
|
|
47
|
-
|
|
48
|
-
val bitmap = Bitmap.createBitmap(rootView.drawingCache)
|
|
49
|
-
rootView.isDrawingCacheEnabled = false
|
|
50
|
-
|
|
51
|
-
// Apply masking
|
|
52
|
-
val maskedBitmap = applyMasking(bitmap, rootView, options)
|
|
53
|
-
|
|
54
|
-
// Convert to base64
|
|
55
|
-
val output = ByteArrayOutputStream()
|
|
56
|
-
maskedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, output)
|
|
57
|
-
val base64 = Base64.encodeToString(output.toByteArray(), Base64.DEFAULT)
|
|
58
|
-
|
|
59
|
-
return base64
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private fun applyMasking(bitmap: Bitmap, rootView: View, options: ReadableMap?): Bitmap {
|
|
63
|
-
val maskedBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true)
|
|
64
|
-
val canvas = Canvas(maskedBitmap)
|
|
65
|
-
|
|
66
|
-
// Find sensitive elements
|
|
67
|
-
val sensitiveElements = findSensitiveElements(rootView)
|
|
68
|
-
|
|
69
|
-
for (element in sensitiveElements) {
|
|
70
|
-
val location = IntArray(2)
|
|
71
|
-
element.getLocationOnScreen(location)
|
|
72
|
-
|
|
73
|
-
val frame = Rect(
|
|
74
|
-
location[0],
|
|
75
|
-
location[1],
|
|
76
|
-
location[0] + element.width,
|
|
77
|
-
location[1] + element.height
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
val maskingType = getMaskingType(element)
|
|
81
|
-
|
|
82
|
-
when (maskingType) {
|
|
83
|
-
MaskingType.BLUR -> applyBlurMask(canvas, frame)
|
|
84
|
-
MaskingType.RECTANGLE -> applyRectangleMask(canvas, frame)
|
|
85
|
-
MaskingType.PIXELATE -> applyPixelateMask(canvas, frame)
|
|
86
|
-
MaskingType.NONE -> { /* No masking */ }
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return maskedBitmap
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
private fun findSensitiveElements(view: View): List<View> {
|
|
94
|
-
val sensitiveElements = mutableListOf<View>()
|
|
95
|
-
|
|
96
|
-
fun traverseView(currentView: View) {
|
|
97
|
-
if (shouldMaskView(currentView)) {
|
|
98
|
-
sensitiveElements.add(currentView)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (currentView is ViewGroup) {
|
|
102
|
-
for (i in 0 until currentView.childCount) {
|
|
103
|
-
traverseView(currentView.getChildAt(i))
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
traverseView(view)
|
|
109
|
-
return sensitiveElements
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private fun shouldMaskView(view: View): Boolean {
|
|
113
|
-
// Check for EditText - mask all text fields when inputMasking is enabled
|
|
114
|
-
if (view is EditText) {
|
|
115
|
-
return true
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Check for TextView - mask all text views when inputMasking is enabled
|
|
119
|
-
if (view is TextView) {
|
|
120
|
-
return true
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return false
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private fun getMaskingType(view: View): MaskingType {
|
|
127
|
-
// Default masking type for all text inputs
|
|
128
|
-
return MaskingType.RECTANGLE
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private fun applyBlurMask(canvas: Canvas, frame: Rect) {
|
|
132
|
-
val paint = Paint().apply {
|
|
133
|
-
color = Color.BLACK
|
|
134
|
-
alpha = 200 // Semi-transparent
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
canvas.drawRect(frame, paint)
|
|
138
|
-
|
|
139
|
-
// Add some noise to make it look blurred
|
|
140
|
-
paint.color = Color.WHITE
|
|
141
|
-
paint.alpha = 80
|
|
142
|
-
|
|
143
|
-
for (i in 0..20) {
|
|
144
|
-
val randomX = frame.left + (Math.random() * frame.width()).toFloat()
|
|
145
|
-
val randomY = frame.top + (Math.random() * frame.height()).toFloat()
|
|
146
|
-
val randomSize = (Math.random() * 6 + 2).toFloat()
|
|
147
|
-
canvas.drawCircle(randomX, randomY, randomSize, paint)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
private fun applyRectangleMask(canvas: Canvas, frame: Rect) {
|
|
152
|
-
val paint = Paint().apply {
|
|
153
|
-
color = Color.GRAY
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
canvas.drawRect(frame, paint)
|
|
157
|
-
|
|
158
|
-
// Add some text-like pattern
|
|
159
|
-
paint.color = Color.DKGRAY
|
|
160
|
-
val lineHeight = 4f
|
|
161
|
-
val spacing = 8f
|
|
162
|
-
|
|
163
|
-
var y = frame.top + spacing
|
|
164
|
-
while (y < frame.bottom - spacing) {
|
|
165
|
-
val lineWidth = (Math.random() * frame.width() * 0.5 + frame.width() * 0.3).toFloat()
|
|
166
|
-
val x = frame.left + (Math.random() * (frame.width() - lineWidth)).toFloat()
|
|
167
|
-
canvas.drawRect(x, y, x + lineWidth, y + lineHeight, paint)
|
|
168
|
-
y += lineHeight + spacing
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
private fun applyPixelateMask(canvas: Canvas, frame: Rect) {
|
|
173
|
-
val pixelSize = 8f
|
|
174
|
-
val pixelCountX = (frame.width() / pixelSize).toInt()
|
|
175
|
-
val pixelCountY = (frame.height() / pixelSize).toInt()
|
|
176
|
-
|
|
177
|
-
val paint = Paint()
|
|
178
|
-
|
|
179
|
-
for (x in 0 until pixelCountX) {
|
|
180
|
-
for (y in 0 until pixelCountY) {
|
|
181
|
-
val pixelFrame = RectF(
|
|
182
|
-
frame.left + x * pixelSize,
|
|
183
|
-
frame.top + y * pixelSize,
|
|
184
|
-
frame.left + (x + 1) * pixelSize,
|
|
185
|
-
frame.top + (y + 1) * pixelSize
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
// Use a random color for each pixel
|
|
189
|
-
paint.color = Color.rgb(
|
|
190
|
-
(Math.random() * 255).toInt(),
|
|
191
|
-
(Math.random() * 255).toInt(),
|
|
192
|
-
(Math.random() * 255).toInt()
|
|
193
|
-
)
|
|
194
|
-
canvas.drawRect(pixelFrame, paint)
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
private enum class MaskingType {
|
|
200
|
-
BLUR, RECTANGLE, PIXELATE, NONE
|
|
201
|
-
}
|
|
202
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
package com.multiplayer.sessionrecorder
|
|
2
|
-
|
|
3
|
-
import com.facebook.react.ReactPackage
|
|
4
|
-
import com.facebook.react.bridge.NativeModule
|
|
5
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
-
import com.facebook.react.uimanager.ViewManager
|
|
7
|
-
|
|
8
|
-
class SessionRecorderPackage : ReactPackage {
|
|
9
|
-
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
10
|
-
return listOf(SessionRecorderModule(reactContext))
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
14
|
-
return emptyList()
|
|
15
|
-
}
|
|
16
|
-
}
|