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