@exodus/react-native-screenshot-detector 1.2.0 → 1.2.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/index.ios.js +48 -19
- package/ios/RNScreenshotDetector/RNScreenshotDetector.m +78 -28
- package/package.json +1 -1
package/index.ios.js
CHANGED
|
@@ -1,59 +1,88 @@
|
|
|
1
1
|
import { NativeEventEmitter, NativeModules } from 'react-native'
|
|
2
2
|
|
|
3
3
|
const { RNScreenshotDetector } = NativeModules
|
|
4
|
+
|
|
5
|
+
console.log('[TEST] RNScreenshotDetector module loaded:', !!RNScreenshotDetector)
|
|
6
|
+
console.log('[TEST] Available methods:', Object.keys(RNScreenshotDetector || {}))
|
|
7
|
+
|
|
4
8
|
const eventEmitter = new NativeEventEmitter(RNScreenshotDetector)
|
|
5
9
|
|
|
6
10
|
const SCREENSHOT_EVENT = 'ScreenshotTaken'
|
|
7
11
|
const SCREEN_RECORDING_EVENT = 'ScreenRecordingChanged'
|
|
8
12
|
|
|
9
13
|
const subscribe = (cb) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (RNScreenshotDetector.subscribeToScreenRecording) {
|
|
20
|
-
RNScreenshotDetector.subscribeToScreenRecording()
|
|
14
|
+
console.log('[TEST] subscribe called - using NEW explicit method!')
|
|
15
|
+
|
|
16
|
+
// Use the more explicit method name internally
|
|
17
|
+
if (RNScreenshotDetector && RNScreenshotDetector.subscribeToScreenshotAndScreenRecording) {
|
|
18
|
+
console.log('[TEST] Calling subscribeToScreenshotAndScreenRecording')
|
|
19
|
+
RNScreenshotDetector.subscribeToScreenshotAndScreenRecording()
|
|
20
|
+
console.log('[TEST] SUCCESS: subscribeToScreenshotAndScreenRecording called')
|
|
21
|
+
} else {
|
|
22
|
+
console.log('[TEST] ERROR: subscribeToScreenshotAndScreenRecording not available!')
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
const sub = eventEmitter.addListener(
|
|
25
|
+
const sub = eventEmitter.addListener(SCREENSHOT_EVENT, (data) => {
|
|
26
|
+
console.log('[TEST] ScreenshotTaken event received:', data)
|
|
24
27
|
cb(data)
|
|
25
28
|
})
|
|
26
29
|
return () => {
|
|
30
|
+
console.log('[TEST] Unsubscribing from screenshot events')
|
|
27
31
|
sub.remove()
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
|
|
33
|
+
// Use the more explicit method name internally
|
|
34
|
+
if (RNScreenshotDetector && RNScreenshotDetector.unsubscribeFromScreenshotAndScreenRecording) {
|
|
35
|
+
console.log('[TEST] Calling unsubscribeFromScreenshotAndScreenRecording')
|
|
36
|
+
RNScreenshotDetector.unsubscribeFromScreenshotAndScreenRecording()
|
|
37
|
+
console.log('[TEST] SUCCESS: unsubscribeFromScreenshotAndScreenRecording called')
|
|
38
|
+
} else {
|
|
39
|
+
console.log('[TEST] ERROR: unsubscribeFromScreenshotAndScreenRecording not available!')
|
|
30
40
|
}
|
|
31
41
|
}
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
const disableScreenshots = () => {
|
|
35
|
-
|
|
45
|
+
console.log('[TEST] disableScreenshots called')
|
|
46
|
+
if (RNScreenshotDetector && RNScreenshotDetector.disableScreenshots) {
|
|
47
|
+
console.log('[TEST] Calling native disableScreenshots')
|
|
36
48
|
RNScreenshotDetector.disableScreenshots()
|
|
49
|
+
console.log('[TEST] SUCCESS: disableScreenshots called')
|
|
50
|
+
} else {
|
|
51
|
+
console.log('[TEST] ERROR: disableScreenshots not available!')
|
|
37
52
|
}
|
|
38
53
|
}
|
|
39
54
|
|
|
40
55
|
const enableScreenshots = () => {
|
|
41
|
-
|
|
56
|
+
console.log('[TEST] enableScreenshots called')
|
|
57
|
+
if (RNScreenshotDetector && RNScreenshotDetector.enableScreenshots) {
|
|
58
|
+
console.log('[TEST] Calling native enableScreenshots')
|
|
42
59
|
RNScreenshotDetector.enableScreenshots()
|
|
60
|
+
console.log('[TEST] SUCCESS: enableScreenshots called')
|
|
61
|
+
} else {
|
|
62
|
+
console.log('[TEST] ERROR: enableScreenshots not available!')
|
|
43
63
|
}
|
|
44
64
|
}
|
|
45
65
|
|
|
46
66
|
const isScreenRecording = async () => {
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
console.log('[TEST] isScreenRecording called')
|
|
68
|
+
if (RNScreenshotDetector && RNScreenshotDetector.isScreenRecording) {
|
|
69
|
+
try {
|
|
70
|
+
const result = await RNScreenshotDetector.isScreenRecording()
|
|
71
|
+
console.log('[TEST] isScreenRecording result:', result)
|
|
72
|
+
return result
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.log('[TEST] ERROR in isScreenRecording:', error)
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
49
77
|
} else {
|
|
78
|
+
console.log('[TEST] ERROR: isScreenRecording not available!')
|
|
50
79
|
return false
|
|
51
80
|
}
|
|
52
81
|
}
|
|
53
82
|
|
|
83
|
+
// Main API - only expose what's actually used externally
|
|
54
84
|
const ScreenshotDetector = {
|
|
55
85
|
subscribe,
|
|
56
|
-
subscribeToScreenRecording,
|
|
57
86
|
disableScreenshots,
|
|
58
87
|
enableScreenshots,
|
|
59
88
|
isScreenRecording,
|
|
@@ -12,98 +12,138 @@
|
|
|
12
12
|
{
|
|
13
13
|
id screenshotObserver;
|
|
14
14
|
id screenRecordingObserver;
|
|
15
|
-
UIView *securityOverlay;
|
|
16
15
|
BOOL isProtectionEnabled;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
RCT_EXPORT_MODULE();
|
|
20
19
|
|
|
21
20
|
- (NSArray<NSString *> *)supportedEvents {
|
|
21
|
+
NSLog(@"[TEST] supportedEvents called");
|
|
22
22
|
return @[@"ScreenshotTaken", @"ScreenRecordingChanged"];
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
- (void)startObserving {
|
|
26
|
+
NSLog(@"[TEST] startObserving called");
|
|
26
27
|
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
|
|
27
|
-
screenshotObserver = [[NSNotificationCenter defaultCenter]
|
|
28
|
-
addObserverForName:UIApplicationUserDidTakeScreenshotNotification
|
|
29
|
-
object:nil
|
|
30
|
-
queue:mainQueue
|
|
31
|
-
usingBlock:^(NSNotification *notification) {
|
|
32
|
-
[self screenshotDetected:notification];
|
|
33
|
-
}];
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
if (screenshotObserver == nil) {
|
|
30
|
+
screenshotObserver = [[NSNotificationCenter defaultCenter]
|
|
31
|
+
addObserverForName:UIApplicationUserDidTakeScreenshotNotification
|
|
32
|
+
object:nil
|
|
33
|
+
queue:mainQueue
|
|
34
|
+
usingBlock:^(NSNotification *notification) {
|
|
35
|
+
[self screenshotDetected:notification];
|
|
36
|
+
}];
|
|
37
|
+
NSLog(@"[TEST] Screenshot observer registered successfully");
|
|
38
|
+
} else {
|
|
39
|
+
NSLog(@"[TEST] Screenshot observer already exists");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (screenRecordingObserver == nil) {
|
|
43
|
+
screenRecordingObserver = [[NSNotificationCenter defaultCenter]
|
|
44
|
+
addObserverForName:UIScreenCapturedDidChangeNotification
|
|
45
|
+
object:nil
|
|
46
|
+
queue:mainQueue
|
|
47
|
+
usingBlock:^(NSNotification *notification) {
|
|
48
|
+
[self screenRecordingChanged:notification];
|
|
49
|
+
}];
|
|
50
|
+
NSLog(@"[TEST] Screen recording observer registered successfully");
|
|
51
|
+
} else {
|
|
52
|
+
NSLog(@"[TEST] Screen recording observer already exists");
|
|
53
|
+
}
|
|
42
54
|
}
|
|
43
55
|
|
|
44
56
|
- (void)stopObserving {
|
|
57
|
+
NSLog(@"[TEST] stopObserving called");
|
|
45
58
|
if (screenshotObserver != nil) {
|
|
46
59
|
[[NSNotificationCenter defaultCenter] removeObserver:screenshotObserver];
|
|
47
60
|
screenshotObserver = nil;
|
|
61
|
+
NSLog(@"[TEST] Screenshot observer removed successfully");
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
if (screenRecordingObserver != nil) {
|
|
51
65
|
[[NSNotificationCenter defaultCenter] removeObserver:screenRecordingObserver];
|
|
52
66
|
screenRecordingObserver = nil;
|
|
67
|
+
NSLog(@"[TEST] Screen recording observer removed successfully");
|
|
53
68
|
}
|
|
54
69
|
}
|
|
55
70
|
|
|
56
71
|
- (void)screenshotDetected:(NSNotification *)notification {
|
|
72
|
+
NSLog(@"[TEST] 🚨 SCREENSHOT DETECTED! 🚨");
|
|
57
73
|
if (screenshotObserver != nil) {
|
|
74
|
+
NSLog(@"[TEST] Sending ScreenshotTaken event to JavaScript");
|
|
58
75
|
[self sendEventWithName:@"ScreenshotTaken" body:@{}];
|
|
76
|
+
NSLog(@"[TEST] ScreenshotTaken event sent successfully");
|
|
77
|
+
} else {
|
|
78
|
+
NSLog(@"[TEST] ERROR: Screenshot observer is nil, not sending event");
|
|
59
79
|
}
|
|
60
80
|
}
|
|
61
81
|
|
|
62
82
|
- (void)screenRecordingChanged:(NSNotification *)notification {
|
|
83
|
+
BOOL isRecording = [UIScreen mainScreen].isCaptured;
|
|
84
|
+
NSLog(@"[TEST] Screen recording changed: %@", isRecording ? @"STARTED" : @"STOPPED");
|
|
85
|
+
|
|
63
86
|
if (screenRecordingObserver != nil) {
|
|
64
|
-
|
|
65
|
-
|
|
87
|
+
NSLog(@"[TEST] Sending ScreenRecordingChanged event to JavaScript");
|
|
66
88
|
[self sendEventWithName:@"ScreenRecordingChanged" body:@{@"isRecording": @(isRecording)}];
|
|
89
|
+
NSLog(@"[TEST] ScreenRecordingChanged event sent successfully");
|
|
90
|
+
} else {
|
|
91
|
+
NSLog(@"[TEST] ERROR: Screen recording observer is nil, not sending event");
|
|
67
92
|
}
|
|
68
93
|
}
|
|
69
94
|
|
|
70
95
|
RCT_EXPORT_METHOD(disableScreenshots) {
|
|
96
|
+
NSLog(@"[TEST] disableScreenshots called");
|
|
71
97
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
72
98
|
isProtectionEnabled = YES;
|
|
99
|
+
NSLog(@"[TEST] Protection enabled, calling enableTrueScreenshotPrevention");
|
|
73
100
|
[self enableTrueScreenshotPrevention];
|
|
101
|
+
NSLog(@"[TEST] disableScreenshots completed");
|
|
74
102
|
});
|
|
75
103
|
}
|
|
76
104
|
|
|
77
105
|
RCT_EXPORT_METHOD(enableScreenshots) {
|
|
106
|
+
NSLog(@"[TEST] enableScreenshots called");
|
|
78
107
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
79
108
|
isProtectionEnabled = NO;
|
|
109
|
+
NSLog(@"[TEST] Protection disabled, calling disableTrueScreenshotPrevention");
|
|
80
110
|
[self disableTrueScreenshotPrevention];
|
|
111
|
+
NSLog(@"[TEST] enableScreenshots completed");
|
|
81
112
|
});
|
|
82
113
|
}
|
|
83
114
|
|
|
84
115
|
RCT_EXPORT_METHOD(isScreenRecording:(RCTPromiseResolveBlock)resolve
|
|
85
116
|
rejecter:(RCTPromiseRejectBlock)reject) {
|
|
86
117
|
BOOL isRecording = [UIScreen mainScreen].isCaptured;
|
|
118
|
+
NSLog(@"[TEST] isScreenRecording called, result: %@", isRecording ? @"YES" : @"NO");
|
|
87
119
|
resolve(@(isRecording));
|
|
88
120
|
}
|
|
89
121
|
|
|
90
|
-
|
|
122
|
+
// Clear and explicit method names
|
|
123
|
+
RCT_EXPORT_METHOD(subscribeToScreenshotAndScreenRecording) {
|
|
124
|
+
NSLog(@"[TEST] subscribeToScreenshotAndScreenRecording called - NEW EXPLICIT METHOD!");
|
|
91
125
|
[self startObserving];
|
|
126
|
+
NSLog(@"[TEST] subscribeToScreenshotAndScreenRecording completed");
|
|
92
127
|
}
|
|
93
128
|
|
|
94
|
-
RCT_EXPORT_METHOD(
|
|
129
|
+
RCT_EXPORT_METHOD(unsubscribeFromScreenshotAndScreenRecording) {
|
|
130
|
+
NSLog(@"[TEST] unsubscribeFromScreenshotAndScreenRecording called");
|
|
95
131
|
[self stopObserving];
|
|
132
|
+
NSLog(@"[TEST] unsubscribeFromScreenshotAndScreenRecording completed");
|
|
96
133
|
}
|
|
97
134
|
|
|
98
135
|
// Screenshot Prevention using Secure Text Field
|
|
99
136
|
- (void)enableTrueScreenshotPrevention {
|
|
137
|
+
NSLog(@"[TEST] enableTrueScreenshotPrevention called");
|
|
100
138
|
if (self.secureTextField == nil) {
|
|
139
|
+
NSLog(@"[TEST] Creating new secureTextField");
|
|
101
140
|
self.secureTextField = [[UITextField alloc] init];
|
|
102
141
|
self.secureTextField.userInteractionEnabled = NO;
|
|
103
142
|
self.secureTextField.secureTextEntry = YES;
|
|
104
143
|
|
|
105
144
|
UIWindow *keyWindow = [self getKeyWindow];
|
|
106
145
|
if (keyWindow != nil) {
|
|
146
|
+
NSLog(@"[TEST] Key window found, setting up secure text field");
|
|
107
147
|
[keyWindow makeKeyAndVisible];
|
|
108
148
|
|
|
109
149
|
// Make the app window a sublayer of the secure text field
|
|
@@ -113,29 +153,46 @@ RCT_EXPORT_METHOD(unsubscribeFromScreenRecording) {
|
|
|
113
153
|
NSArray *sublayers = self.secureTextField.layer.sublayers;
|
|
114
154
|
if (sublayers.count > 0) {
|
|
115
155
|
[sublayers.firstObject addSublayer:keyWindow.layer];
|
|
156
|
+
NSLog(@"[TEST] ✅ Secure text field configured successfully - Screenshot prevention ACTIVE!");
|
|
157
|
+
} else {
|
|
158
|
+
NSLog(@"[TEST] ❌ No sublayers found in secure text field");
|
|
116
159
|
}
|
|
160
|
+
} else {
|
|
161
|
+
NSLog(@"[TEST] ❌ Key window not found!");
|
|
117
162
|
}
|
|
118
163
|
} else {
|
|
164
|
+
NSLog(@"[TEST] Secure text field already exists, enabling secureTextEntry");
|
|
119
165
|
self.secureTextField.secureTextEntry = YES;
|
|
166
|
+
NSLog(@"[TEST] ✅ Secure text field re-enabled - Screenshot prevention ACTIVE!");
|
|
120
167
|
}
|
|
121
168
|
}
|
|
122
169
|
|
|
123
170
|
- (void)disableTrueScreenshotPrevention {
|
|
171
|
+
NSLog(@"[TEST] disableTrueScreenshotPrevention called");
|
|
124
172
|
if (self.secureTextField != nil) {
|
|
173
|
+
NSLog(@"[TEST] Disabling secureTextEntry");
|
|
125
174
|
self.secureTextField.secureTextEntry = NO;
|
|
175
|
+
NSLog(@"[TEST] ✅ Screenshot prevention DISABLED");
|
|
176
|
+
} else {
|
|
177
|
+
NSLog(@"[TEST] Secure text field is nil");
|
|
126
178
|
}
|
|
127
179
|
}
|
|
128
180
|
|
|
129
181
|
- (UIWindow *)getKeyWindow {
|
|
182
|
+
NSLog(@"[TEST] getKeyWindow called");
|
|
130
183
|
UIWindow *keyWindow = nil;
|
|
131
184
|
|
|
132
185
|
NSSet<UIScene *> *connectedScenes = [UIApplication sharedApplication].connectedScenes;
|
|
186
|
+
NSLog(@"[TEST] Connected scenes count: %lu", (unsigned long)connectedScenes.count);
|
|
187
|
+
|
|
133
188
|
for (UIScene *scene in connectedScenes) {
|
|
134
189
|
if ([scene isKindOfClass:[UIWindowScene class]]) {
|
|
135
190
|
UIWindowScene *windowScene = (UIWindowScene *)scene;
|
|
191
|
+
NSLog(@"[TEST] Found window scene with %lu windows", (unsigned long)windowScene.windows.count);
|
|
136
192
|
for (UIWindow *window in windowScene.windows) {
|
|
137
193
|
if (window.isKeyWindow) {
|
|
138
194
|
keyWindow = window;
|
|
195
|
+
NSLog(@"[TEST] ✅ Found key window using UIScene method");
|
|
139
196
|
break;
|
|
140
197
|
}
|
|
141
198
|
}
|
|
@@ -144,21 +201,14 @@ RCT_EXPORT_METHOD(unsubscribeFromScreenRecording) {
|
|
|
144
201
|
}
|
|
145
202
|
|
|
146
203
|
if (keyWindow == nil) {
|
|
147
|
-
|
|
148
|
-
if (keyWindow == nil) {
|
|
149
|
-
for (UIWindow *window in [UIApplication sharedApplication].windows) {
|
|
150
|
-
if (window.isKeyWindow) {
|
|
151
|
-
keyWindow = window;
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
204
|
+
NSLog(@"[TEST] ❌ Key window not found using UIScene method!");
|
|
156
205
|
}
|
|
157
206
|
|
|
158
207
|
return keyWindow;
|
|
159
208
|
}
|
|
160
209
|
|
|
161
210
|
- (void)dealloc {
|
|
211
|
+
NSLog(@"[TEST] dealloc called");
|
|
162
212
|
[self stopObserving];
|
|
163
213
|
[self disableTrueScreenshotPrevention];
|
|
164
214
|
}
|