react-native-privacy-guard-kit 0.1.0
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/LICENSE +20 -0
- package/PrivacyGuardKit.podspec +27 -0
- package/README.md +320 -0
- package/android/CMakeLists.txt +47 -0
- package/android/build.gradle +64 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/privacyguardkit/PrivacyGuardKitPackage.kt +17 -0
- package/android/src/main/java/com/privacyguardkit/Privacyguardkitmodule .kt +208 -0
- package/android/src/main/java/com/privacyguardkit/Screenshotobserver.kt +66 -0
- package/android/src/main/java/com/privacyguardkit/Secureview.kt +104 -0
- package/android/src/main/java/com/privacyguardkit/Secureviewmanager.kt +27 -0
- package/android/src/main/jni/react/renderer/components/PrivacyGuardKitViewSpec/RNSecureViewComponentDescriptor.h +25 -0
- package/ios/PrivacyGuardKit-Umbrella.h +14 -0
- package/ios/PrivacyGuardKit.m +38 -0
- package/ios/PrivacyGuardKit.swift +221 -0
- package/ios/RNSecureViewComponentView.h +16 -0
- package/ios/RNSecureViewComponentView.mm +84 -0
- package/ios/RNSecureViewManager.mm +48 -0
- package/lib/module/Hooks.js +119 -0
- package/lib/module/Hooks.js.map +1 -0
- package/lib/module/NativePrivacyGuardKit.js +12 -0
- package/lib/module/NativePrivacyGuardKit.js.map +1 -0
- package/lib/module/PrivacyGuardProvider.js +99 -0
- package/lib/module/PrivacyGuardProvider.js.map +1 -0
- package/lib/module/PrivacyGuardkitApi.js +104 -0
- package/lib/module/PrivacyGuardkitApi.js.map +1 -0
- package/lib/module/SecureView.js +24 -0
- package/lib/module/SecureView.js.map +1 -0
- package/lib/module/index.js +16 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/specs/RNSecureViewNativeComponent.ts +19 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/Hooks.d.ts +29 -0
- package/lib/typescript/src/Hooks.d.ts.map +1 -0
- package/lib/typescript/src/NativePrivacyGuardKit.d.ts +4 -0
- package/lib/typescript/src/NativePrivacyGuardKit.d.ts.map +1 -0
- package/lib/typescript/src/PrivacyGuardProvider.d.ts +30 -0
- package/lib/typescript/src/PrivacyGuardProvider.d.ts.map +1 -0
- package/lib/typescript/src/PrivacyGuardkitApi.d.ts +45 -0
- package/lib/typescript/src/PrivacyGuardkitApi.d.ts.map +1 -0
- package/lib/typescript/src/SecureView.d.ts +10 -0
- package/lib/typescript/src/SecureView.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/specs/RNSecureViewNativeComponent.d.ts +11 -0
- package/lib/typescript/src/specs/RNSecureViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +29 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +174 -0
- package/src/Hooks.ts +138 -0
- package/src/NativePrivacyGuardKit.ts +13 -0
- package/src/PrivacyGuardProvider.tsx +134 -0
- package/src/PrivacyGuardkitApi.ts +123 -0
- package/src/SecureView.tsx +29 -0
- package/src/index.tsx +37 -0
- package/src/specs/RNSecureViewNativeComponent.ts +19 -0
- package/src/types.ts +34 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// PrivacyGuardKit.swift
|
|
2
|
+
// The @objc(PrivacyGuardKit) annotation is what generates
|
|
3
|
+
// _OBJC_CLASS_$_PrivacyGuardKit that the linker needs.
|
|
4
|
+
// The class name MUST match the RCT_EXTERN_MODULE name in PrivacyGuardKit.m
|
|
5
|
+
|
|
6
|
+
import Foundation
|
|
7
|
+
import UIKit
|
|
8
|
+
import React
|
|
9
|
+
|
|
10
|
+
@objc(PrivacyGuardKit)
|
|
11
|
+
class PrivacyGuardKit: RCTEventEmitter {
|
|
12
|
+
|
|
13
|
+
static let screenshotTakenEvent = "onScreenshotTaken"
|
|
14
|
+
static let recordingStartedEvent = "onScreenRecordingStarted"
|
|
15
|
+
static let recordingStoppedEvent = "onScreenRecordingStopped"
|
|
16
|
+
|
|
17
|
+
private var isCaptureDisabled = false
|
|
18
|
+
private var isAppSwitcherGuarded = false
|
|
19
|
+
private var secureOverlay: UIView?
|
|
20
|
+
private var appSwitcherOverlay: UIView?
|
|
21
|
+
|
|
22
|
+
override static func moduleName() -> String! { "PrivacyGuardKit" }
|
|
23
|
+
override static func requiresMainQueueSetup() -> Bool { true }
|
|
24
|
+
|
|
25
|
+
override func supportedEvents() -> [String]! {
|
|
26
|
+
[
|
|
27
|
+
PrivacyGuardKit.screenshotTakenEvent,
|
|
28
|
+
PrivacyGuardKit.recordingStartedEvent,
|
|
29
|
+
PrivacyGuardKit.recordingStoppedEvent,
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// MARK: - Screen Capture
|
|
34
|
+
|
|
35
|
+
@objc(disableScreenCapture:reject:)
|
|
36
|
+
func disableScreenCapture(
|
|
37
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
38
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
39
|
+
) {
|
|
40
|
+
DispatchQueue.main.async {
|
|
41
|
+
self.addSecureOverlay()
|
|
42
|
+
self.isCaptureDisabled = true
|
|
43
|
+
resolve(true)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@objc(enableScreenCapture:reject:)
|
|
48
|
+
func enableScreenCapture(
|
|
49
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
50
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
51
|
+
) {
|
|
52
|
+
DispatchQueue.main.async {
|
|
53
|
+
self.removeSecureOverlay()
|
|
54
|
+
self.isCaptureDisabled = false
|
|
55
|
+
resolve(true)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@objc(isScreenCaptureDisabled:reject:)
|
|
60
|
+
func isScreenCaptureDisabled(
|
|
61
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
62
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
63
|
+
) {
|
|
64
|
+
resolve(isCaptureDisabled)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private func addSecureOverlay() {
|
|
68
|
+
guard let window = currentWindow(), secureOverlay == nil else { return }
|
|
69
|
+
let field = UITextField()
|
|
70
|
+
field.isSecureTextEntry = true
|
|
71
|
+
field.translatesAutoresizingMaskIntoConstraints = false
|
|
72
|
+
let container = UIView()
|
|
73
|
+
container.isUserInteractionEnabled = false
|
|
74
|
+
container.translatesAutoresizingMaskIntoConstraints = false
|
|
75
|
+
container.addSubview(field)
|
|
76
|
+
window.addSubview(container)
|
|
77
|
+
NSLayoutConstraint.activate([
|
|
78
|
+
container.leadingAnchor.constraint(equalTo: window.leadingAnchor),
|
|
79
|
+
container.trailingAnchor.constraint(equalTo: window.trailingAnchor),
|
|
80
|
+
container.topAnchor.constraint(equalTo: window.topAnchor),
|
|
81
|
+
container.bottomAnchor.constraint(equalTo: window.bottomAnchor),
|
|
82
|
+
field.widthAnchor.constraint(equalTo: container.widthAnchor),
|
|
83
|
+
field.heightAnchor.constraint(equalTo: container.heightAnchor),
|
|
84
|
+
field.centerXAnchor.constraint(equalTo: container.centerXAnchor),
|
|
85
|
+
field.centerYAnchor.constraint(equalTo: container.centerYAnchor),
|
|
86
|
+
])
|
|
87
|
+
window.sendSubviewToBack(container)
|
|
88
|
+
secureOverlay = container
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private func removeSecureOverlay() {
|
|
92
|
+
secureOverlay?.removeFromSuperview()
|
|
93
|
+
secureOverlay = nil
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// MARK: - Screen Recording
|
|
97
|
+
|
|
98
|
+
@objc(isScreenBeingRecorded:reject:)
|
|
99
|
+
func isScreenBeingRecorded(
|
|
100
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
101
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
102
|
+
) {
|
|
103
|
+
resolve(UIScreen.main.isCaptured)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@objc(startScreenshotListener:reject:)
|
|
107
|
+
func startScreenshotListener(
|
|
108
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
109
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
110
|
+
) {
|
|
111
|
+
NotificationCenter.default.removeObserver(self)
|
|
112
|
+
NotificationCenter.default.addObserver(
|
|
113
|
+
self, selector: #selector(handleScreenshot),
|
|
114
|
+
name: UIApplication.userDidTakeScreenshotNotification, object: nil)
|
|
115
|
+
NotificationCenter.default.addObserver(
|
|
116
|
+
self, selector: #selector(handleRecordingChange),
|
|
117
|
+
name: UIScreen.capturedDidChangeNotification, object: nil)
|
|
118
|
+
resolve(true)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@objc(stopScreenshotListener:reject:)
|
|
122
|
+
func stopScreenshotListener(
|
|
123
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
124
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
125
|
+
) {
|
|
126
|
+
NotificationCenter.default.removeObserver(
|
|
127
|
+
self, name: UIApplication.userDidTakeScreenshotNotification, object: nil)
|
|
128
|
+
NotificationCenter.default.removeObserver(
|
|
129
|
+
self, name: UIScreen.capturedDidChangeNotification, object: nil)
|
|
130
|
+
resolve(true)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@objc private func handleScreenshot() {
|
|
134
|
+
sendEvent(withName: PrivacyGuardKit.screenshotTakenEvent, body: nil)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@objc private func handleRecordingChange() {
|
|
138
|
+
let recording = UIScreen.main.isCaptured
|
|
139
|
+
sendEvent(
|
|
140
|
+
withName: recording
|
|
141
|
+
? PrivacyGuardKit.recordingStartedEvent
|
|
142
|
+
: PrivacyGuardKit.recordingStoppedEvent,
|
|
143
|
+
body: ["isRecording": recording])
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// MARK: - App Switcher
|
|
147
|
+
|
|
148
|
+
@objc(enableAppSwitcherProtection:reject:)
|
|
149
|
+
func enableAppSwitcherProtection(
|
|
150
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
151
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
152
|
+
) {
|
|
153
|
+
DispatchQueue.main.async {
|
|
154
|
+
NotificationCenter.default.addObserver(
|
|
155
|
+
self, selector: #selector(self.appWillResignActive),
|
|
156
|
+
name: UIApplication.willResignActiveNotification, object: nil)
|
|
157
|
+
NotificationCenter.default.addObserver(
|
|
158
|
+
self, selector: #selector(self.appDidBecomeActive),
|
|
159
|
+
name: UIApplication.didBecomeActiveNotification, object: nil)
|
|
160
|
+
self.isAppSwitcherGuarded = true
|
|
161
|
+
resolve(true)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@objc(disableAppSwitcherProtection:reject:)
|
|
166
|
+
func disableAppSwitcherProtection(
|
|
167
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
168
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
169
|
+
) {
|
|
170
|
+
DispatchQueue.main.async {
|
|
171
|
+
NotificationCenter.default.removeObserver(
|
|
172
|
+
self, name: UIApplication.willResignActiveNotification, object: nil)
|
|
173
|
+
NotificationCenter.default.removeObserver(
|
|
174
|
+
self, name: UIApplication.didBecomeActiveNotification, object: nil)
|
|
175
|
+
self.removeAppSwitcherOverlay()
|
|
176
|
+
self.isAppSwitcherGuarded = false
|
|
177
|
+
resolve(true)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@objc private func appWillResignActive() {
|
|
182
|
+
guard let window = currentWindow() else { return }
|
|
183
|
+
let overlay = UIView(frame: window.bounds)
|
|
184
|
+
overlay.backgroundColor = .systemBackground
|
|
185
|
+
overlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
186
|
+
window.addSubview(overlay)
|
|
187
|
+
appSwitcherOverlay = overlay
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@objc private func appDidBecomeActive() {
|
|
191
|
+
removeAppSwitcherOverlay()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private func removeAppSwitcherOverlay() {
|
|
195
|
+
appSwitcherOverlay?.removeFromSuperview()
|
|
196
|
+
appSwitcherOverlay = nil
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// MARK: - Clipboard
|
|
200
|
+
|
|
201
|
+
@objc(clearClipboard:reject:)
|
|
202
|
+
func clearClipboard(
|
|
203
|
+
_ resolve: @escaping RCTPromiseResolveBlock,
|
|
204
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
205
|
+
) {
|
|
206
|
+
UIPasteboard.general.items = []
|
|
207
|
+
resolve(true)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// MARK: - Helpers
|
|
211
|
+
|
|
212
|
+
private func currentWindow() -> UIWindow? {
|
|
213
|
+
if #available(iOS 13.0, *) {
|
|
214
|
+
return UIApplication.shared.connectedScenes
|
|
215
|
+
.compactMap { $0 as? UIWindowScene }
|
|
216
|
+
.flatMap { $0.windows }
|
|
217
|
+
.first { $0.isKeyWindow }
|
|
218
|
+
}
|
|
219
|
+
return UIApplication.shared.keyWindow
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// RNSecureViewComponentView.h
|
|
2
|
+
|
|
3
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
4
|
+
|
|
5
|
+
#import <React/RCTViewComponentView.h>
|
|
6
|
+
#import <UIKit/UIKit.h>
|
|
7
|
+
|
|
8
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
9
|
+
|
|
10
|
+
@interface RNSecureViewComponentView : RCTViewComponentView
|
|
11
|
+
|
|
12
|
+
@end
|
|
13
|
+
|
|
14
|
+
NS_ASSUME_NONNULL_END
|
|
15
|
+
|
|
16
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// RNSecureViewComponentView.mm
|
|
2
|
+
// Fabric ComponentView for RNSecureView.
|
|
3
|
+
//
|
|
4
|
+
// RN 0.76+ uses RCTAppDependencyProvider (generated in the example app
|
|
5
|
+
// during pod install) instead of RCTThirdPartyFabricComponentsProvider.
|
|
6
|
+
// That generated file calls NSClassFromString("RNSecureViewComponentView")
|
|
7
|
+
// to get our class — so the class name MUST match exactly.
|
|
8
|
+
//
|
|
9
|
+
// The codegenConfig.ios.componentProvider in package.json tells the
|
|
10
|
+
// code generator: "RNSecureView" → "RNSecureViewComponentView"
|
|
11
|
+
|
|
12
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
13
|
+
|
|
14
|
+
#import "RNSecureViewComponentView.h"
|
|
15
|
+
|
|
16
|
+
#import <react/renderer/components/PrivacyGuardKitViewSpec/ComponentDescriptors.h>
|
|
17
|
+
#import <react/renderer/components/PrivacyGuardKitViewSpec/Props.h>
|
|
18
|
+
#import <react/renderer/components/PrivacyGuardKitViewSpec/RCTComponentViewHelpers.h>
|
|
19
|
+
|
|
20
|
+
using namespace facebook::react;
|
|
21
|
+
|
|
22
|
+
@interface RNSecureViewComponentView () <RCTRNSecureViewViewProtocol>
|
|
23
|
+
@end
|
|
24
|
+
|
|
25
|
+
@implementation RNSecureViewComponentView
|
|
26
|
+
|
|
27
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
28
|
+
if (self = [super initWithFrame:frame]) {
|
|
29
|
+
self.backgroundColor = UIColor.clearColor;
|
|
30
|
+
}
|
|
31
|
+
return self;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
- (void)updateProps:(const Props::Shared &)props
|
|
35
|
+
oldProps:(const Props::Shared &)oldProps {
|
|
36
|
+
const auto &newProps =
|
|
37
|
+
*std::static_pointer_cast<const RNSecureViewProps>(props);
|
|
38
|
+
if (newProps.isCopyPasteDisabled) {
|
|
39
|
+
[self disableCopyPasteIn:self];
|
|
40
|
+
} else {
|
|
41
|
+
[self enableCopyPasteIn:self];
|
|
42
|
+
}
|
|
43
|
+
[super updateProps:props oldProps:oldProps];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
47
|
+
return concreteComponentDescriptorProvider<RNSecureViewComponentDescriptor>();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
|
|
51
|
+
if (_props) {
|
|
52
|
+
const auto &p = *std::static_pointer_cast<const RNSecureViewProps>(_props);
|
|
53
|
+
if (p.isCopyPasteDisabled) return NO;
|
|
54
|
+
}
|
|
55
|
+
return [super canPerformAction:action withSender:sender];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
- (void)disableCopyPasteIn:(UIView *)v {
|
|
59
|
+
for (UIView *sub in v.subviews) {
|
|
60
|
+
if ([sub isKindOfClass:UITextField.class])
|
|
61
|
+
((UITextField *)sub).userInteractionEnabled = NO;
|
|
62
|
+
if ([sub isKindOfClass:UITextView.class]) {
|
|
63
|
+
((UITextView *)sub).selectable = NO;
|
|
64
|
+
((UITextView *)sub).editable = NO;
|
|
65
|
+
}
|
|
66
|
+
[self disableCopyPasteIn:sub];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
- (void)enableCopyPasteIn:(UIView *)v {
|
|
71
|
+
for (UIView *sub in v.subviews) {
|
|
72
|
+
if ([sub isKindOfClass:UITextField.class])
|
|
73
|
+
((UITextField *)sub).userInteractionEnabled = YES;
|
|
74
|
+
if ([sub isKindOfClass:UITextView.class]) {
|
|
75
|
+
((UITextView *)sub).selectable = YES;
|
|
76
|
+
((UITextView *)sub).editable = YES;
|
|
77
|
+
}
|
|
78
|
+
[self enableCopyPasteIn:sub];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@end
|
|
83
|
+
|
|
84
|
+
#endif // RCT_NEW_ARCH_ENABLED
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// RNSecureViewManager.mm
|
|
2
|
+
// Must be .mm not .m — RCT macros expand to ObjC++ in some RN versions.
|
|
3
|
+
|
|
4
|
+
#import <React/RCTViewManager.h>
|
|
5
|
+
#import <React/RCTUIManager.h>
|
|
6
|
+
#import <UIKit/UIKit.h>
|
|
7
|
+
|
|
8
|
+
// ─────────────────────────────────────────────────────────────
|
|
9
|
+
// SecureView — a UIView that blocks copy/paste at the ObjC level.
|
|
10
|
+
// Used by the Paper (Old Architecture) path only.
|
|
11
|
+
// Fabric uses RNSecureViewComponentView.mm instead.
|
|
12
|
+
// ─────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
@interface RNSecureUIView : UIView
|
|
15
|
+
@property (nonatomic, assign) BOOL isCopyPasteDisabled;
|
|
16
|
+
@end
|
|
17
|
+
|
|
18
|
+
@implementation RNSecureUIView
|
|
19
|
+
|
|
20
|
+
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
|
|
21
|
+
if (self.isCopyPasteDisabled) {
|
|
22
|
+
return NO;
|
|
23
|
+
}
|
|
24
|
+
return [super canPerformAction:action withSender:sender];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@end
|
|
28
|
+
|
|
29
|
+
// ─────────────────────────────────────────────────────────────
|
|
30
|
+
// ViewManager — exposes RNSecureUIView to the Paper bridge
|
|
31
|
+
// ─────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
@interface RNSecureViewManager : RCTViewManager
|
|
34
|
+
@end
|
|
35
|
+
|
|
36
|
+
@implementation RNSecureViewManager
|
|
37
|
+
|
|
38
|
+
RCT_EXPORT_MODULE(RNSecureView)
|
|
39
|
+
|
|
40
|
+
- (UIView *)view {
|
|
41
|
+
return [RNSecureUIView new];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
RCT_CUSTOM_VIEW_PROPERTY(isCopyPasteDisabled, BOOL, RNSecureUIView) {
|
|
45
|
+
view.isCopyPasteDisabled = json ? [RCTConvert BOOL:json] : defaultView.isCopyPasteDisabled;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
4
|
+
import { disableScreenCapture, enableScreenCapture, isScreenBeingRecorded, enableAppSwitcherProtection, disableAppSwitcherProtection, clearClipboard, onScreenshotTaken, onScreenRecordingStarted, onScreenRecordingStopped } from "./PrivacyGuardkitApi.js";
|
|
5
|
+
// ─────────────────────────────────────────────────────────────
|
|
6
|
+
// usePrivacyGuard
|
|
7
|
+
// ─────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* One-stop hook that wires up all privacy guard features declaratively.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const { isRecording, disableScreenCapture } = usePrivacyGuard({
|
|
14
|
+
* disableScreenCapture: true,
|
|
15
|
+
* enableAppSwitcherProtection: true,
|
|
16
|
+
* });
|
|
17
|
+
*/
|
|
18
|
+
export function usePrivacyGuard(config = {}) {
|
|
19
|
+
const [captureDisabled, setCaptureDisabled] = useState(false);
|
|
20
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
21
|
+
|
|
22
|
+
// Apply config on mount
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (config.disableScreenCapture) {
|
|
25
|
+
disableScreenCapture().then(() => setCaptureDisabled(true));
|
|
26
|
+
}
|
|
27
|
+
if (config.enableAppSwitcherProtection) {
|
|
28
|
+
enableAppSwitcherProtection();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Bootstrap recording state
|
|
32
|
+
isScreenBeingRecorded().then(setIsRecording);
|
|
33
|
+
|
|
34
|
+
// Recording listeners
|
|
35
|
+
const stopStart = onScreenRecordingStarted(() => setIsRecording(true));
|
|
36
|
+
const stopStop = onScreenRecordingStopped(() => setIsRecording(false));
|
|
37
|
+
return () => {
|
|
38
|
+
if (config.disableScreenCapture) {
|
|
39
|
+
enableScreenCapture().then(() => setCaptureDisabled(false));
|
|
40
|
+
}
|
|
41
|
+
if (config.enableAppSwitcherProtection) {
|
|
42
|
+
disableAppSwitcherProtection();
|
|
43
|
+
}
|
|
44
|
+
stopStart();
|
|
45
|
+
stopStop();
|
|
46
|
+
};
|
|
47
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
48
|
+
}, []);
|
|
49
|
+
const handleDisableCapture = useCallback(async () => {
|
|
50
|
+
await disableScreenCapture();
|
|
51
|
+
setCaptureDisabled(true);
|
|
52
|
+
}, []);
|
|
53
|
+
const handleEnableCapture = useCallback(async () => {
|
|
54
|
+
await enableScreenCapture();
|
|
55
|
+
setCaptureDisabled(false);
|
|
56
|
+
}, []);
|
|
57
|
+
return {
|
|
58
|
+
isScreenCaptureDisabled: captureDisabled,
|
|
59
|
+
isRecording,
|
|
60
|
+
disableScreenCapture: handleDisableCapture,
|
|
61
|
+
enableScreenCapture: handleEnableCapture,
|
|
62
|
+
enableAppSwitcherProtection: async () => {
|
|
63
|
+
await enableAppSwitcherProtection();
|
|
64
|
+
},
|
|
65
|
+
disableAppSwitcherProtection: async () => {
|
|
66
|
+
await disableAppSwitcherProtection();
|
|
67
|
+
},
|
|
68
|
+
clearClipboard
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─────────────────────────────────────────────────────────────
|
|
73
|
+
// useScreenshotListener
|
|
74
|
+
// ─────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Calls `onTaken` every time the user takes a screenshot.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* useScreenshotListener(() => {
|
|
81
|
+
* console.log('Screenshot detected!');
|
|
82
|
+
* });
|
|
83
|
+
*/
|
|
84
|
+
export function useScreenshotListener(onTaken) {
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
const remove = onScreenshotTaken(onTaken);
|
|
87
|
+
return remove;
|
|
88
|
+
}, [onTaken]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ─────────────────────────────────────────────────────────────
|
|
92
|
+
// useScreenRecording
|
|
93
|
+
// ─────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Reactive boolean — true while screen is being recorded.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* const isRecording = useScreenRecording();
|
|
100
|
+
* if (isRecording) return <BlurredScreen />;
|
|
101
|
+
*/
|
|
102
|
+
export function useScreenRecording() {
|
|
103
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
isScreenBeingRecorded().then(setIsRecording);
|
|
106
|
+
const stopStart = onScreenRecordingStarted(({
|
|
107
|
+
isRecording: r
|
|
108
|
+
}) => setIsRecording(r));
|
|
109
|
+
const stopEnd = onScreenRecordingStopped(({
|
|
110
|
+
isRecording: r
|
|
111
|
+
}) => setIsRecording(r));
|
|
112
|
+
return () => {
|
|
113
|
+
stopStart();
|
|
114
|
+
stopEnd();
|
|
115
|
+
};
|
|
116
|
+
}, []);
|
|
117
|
+
return isRecording;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=Hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useState","useEffect","useCallback","disableScreenCapture","enableScreenCapture","isScreenBeingRecorded","enableAppSwitcherProtection","disableAppSwitcherProtection","clearClipboard","onScreenshotTaken","onScreenRecordingStarted","onScreenRecordingStopped","usePrivacyGuard","config","captureDisabled","setCaptureDisabled","isRecording","setIsRecording","then","stopStart","stopStop","handleDisableCapture","handleEnableCapture","isScreenCaptureDisabled","useScreenshotListener","onTaken","remove","useScreenRecording","r","stopEnd"],"sourceRoot":"../../src","sources":["Hooks.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { NativeModules, NativeEventEmitter } from 'react-native';
|
|
4
|
+
const {
|
|
5
|
+
PrivacyGuardKit
|
|
6
|
+
} = NativeModules;
|
|
7
|
+
if (!PrivacyGuardKit) {
|
|
8
|
+
throw new Error('[react-native-privacy-guard-kit] Native module not found. ' + 'Make sure you have linked the library and rebuilt the app.');
|
|
9
|
+
}
|
|
10
|
+
export const NativePrivacyGuardKit = PrivacyGuardKit;
|
|
11
|
+
export const PrivacyGuardKitEmitter = new NativeEventEmitter(PrivacyGuardKit);
|
|
12
|
+
//# sourceMappingURL=NativePrivacyGuardKit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NativeModules","NativeEventEmitter","PrivacyGuardKit","Error","NativePrivacyGuardKit","PrivacyGuardKitEmitter"],"sourceRoot":"../../src","sources":["NativePrivacyGuardKit.ts"],"mappings":";;AAAA,SAASA,aAAa,EAAEC,kBAAkB,QAAQ,cAAc;AAEhE,MAAM;EAAEC;AAAgB,CAAC,GAAGF,aAAa;AAEzC,IAAI,CAACE,eAAe,EAAE;EACpB,MAAM,IAAIC,KAAK,CACb,4DAA4D,GAC1D,4DACJ,CAAC;AACH;AAEA,OAAO,MAAMC,qBAAqB,GAAGF,eAAe;AACpD,OAAO,MAAMG,sBAAsB,GAAG,IAAIJ,kBAAkB,CAACC,eAAe,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, useEffect, useState, useCallback } from 'react';
|
|
4
|
+
import { disableScreenCapture, enableScreenCapture, enableAppSwitcherProtection, disableAppSwitcherProtection, isScreenBeingRecorded, clearClipboard, onScreenRecordingStarted, onScreenRecordingStopped, onScreenshotTaken } from "./PrivacyGuardkitApi.js";
|
|
5
|
+
|
|
6
|
+
// ─────────────────────────────────────────────────────────────
|
|
7
|
+
// Context
|
|
8
|
+
// ─────────────────────────────────────────────────────────────
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
const PrivacyGuardContext = /*#__PURE__*/createContext(null);
|
|
11
|
+
|
|
12
|
+
// ─────────────────────────────────────────────────────────────
|
|
13
|
+
// Provider
|
|
14
|
+
// ─────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Wrap your root (or any sensitive screen) with this provider.
|
|
18
|
+
* All children can call `usePrivacyGuardContext()` to access the API.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* <PrivacyGuardProvider
|
|
22
|
+
* config={{ disableScreenCapture: true, enableAppSwitcherProtection: true }}
|
|
23
|
+
* onScreenshot={() => Alert.alert('Screenshot blocked!')}
|
|
24
|
+
* >
|
|
25
|
+
* <App />
|
|
26
|
+
* </PrivacyGuardProvider>
|
|
27
|
+
*/
|
|
28
|
+
export function PrivacyGuardProvider({
|
|
29
|
+
children,
|
|
30
|
+
config = {},
|
|
31
|
+
onScreenshot
|
|
32
|
+
}) {
|
|
33
|
+
const [captureDisabled, setCaptureDisabled] = useState(false);
|
|
34
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
// Apply config
|
|
37
|
+
if (config.disableScreenCapture) {
|
|
38
|
+
disableScreenCapture().then(() => setCaptureDisabled(true));
|
|
39
|
+
}
|
|
40
|
+
if (config.enableAppSwitcherProtection) {
|
|
41
|
+
enableAppSwitcherProtection();
|
|
42
|
+
}
|
|
43
|
+
isScreenBeingRecorded().then(setIsRecording);
|
|
44
|
+
|
|
45
|
+
// Event subscriptions
|
|
46
|
+
const cleanups = [];
|
|
47
|
+
if (onScreenshot) {
|
|
48
|
+
cleanups.push(onScreenshotTaken(onScreenshot));
|
|
49
|
+
}
|
|
50
|
+
cleanups.push(onScreenRecordingStarted(() => setIsRecording(true)));
|
|
51
|
+
cleanups.push(onScreenRecordingStopped(() => setIsRecording(false)));
|
|
52
|
+
return () => {
|
|
53
|
+
cleanups.forEach(fn => fn());
|
|
54
|
+
if (config.disableScreenCapture) enableScreenCapture();
|
|
55
|
+
if (config.enableAppSwitcherProtection) disableAppSwitcherProtection();
|
|
56
|
+
};
|
|
57
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
58
|
+
}, []);
|
|
59
|
+
const handleDisable = useCallback(async () => {
|
|
60
|
+
await disableScreenCapture();
|
|
61
|
+
setCaptureDisabled(true);
|
|
62
|
+
}, []);
|
|
63
|
+
const handleEnable = useCallback(async () => {
|
|
64
|
+
await enableScreenCapture();
|
|
65
|
+
setCaptureDisabled(false);
|
|
66
|
+
}, []);
|
|
67
|
+
const value = {
|
|
68
|
+
isScreenCaptureDisabled: captureDisabled,
|
|
69
|
+
isRecording,
|
|
70
|
+
disableScreenCapture: handleDisable,
|
|
71
|
+
enableScreenCapture: handleEnable,
|
|
72
|
+
enableAppSwitcherProtection: async () => enableAppSwitcherProtection(),
|
|
73
|
+
disableAppSwitcherProtection: async () => disableAppSwitcherProtection(),
|
|
74
|
+
clearClipboard
|
|
75
|
+
};
|
|
76
|
+
return /*#__PURE__*/_jsx(PrivacyGuardContext.Provider, {
|
|
77
|
+
value: value,
|
|
78
|
+
children: children
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─────────────────────────────────────────────────────────────
|
|
83
|
+
// Consumer hook
|
|
84
|
+
// ─────────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Access the PrivacyGuard API from any component inside the provider.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* const { isRecording } = usePrivacyGuardContext();
|
|
91
|
+
*/
|
|
92
|
+
export function usePrivacyGuardContext() {
|
|
93
|
+
const ctx = useContext(PrivacyGuardContext);
|
|
94
|
+
if (!ctx) {
|
|
95
|
+
throw new Error('usePrivacyGuardContext must be used inside <PrivacyGuardProvider>');
|
|
96
|
+
}
|
|
97
|
+
return ctx;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=PrivacyGuardProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["createContext","useContext","useEffect","useState","useCallback","disableScreenCapture","enableScreenCapture","enableAppSwitcherProtection","disableAppSwitcherProtection","isScreenBeingRecorded","clearClipboard","onScreenRecordingStarted","onScreenRecordingStopped","onScreenshotTaken","jsx","_jsx","PrivacyGuardContext","PrivacyGuardProvider","children","config","onScreenshot","captureDisabled","setCaptureDisabled","isRecording","setIsRecording","then","cleanups","push","forEach","fn","handleDisable","handleEnable","value","isScreenCaptureDisabled","Provider","usePrivacyGuardContext","ctx","Error"],"sourceRoot":"../../src","sources":["PrivacyGuardProvider.tsx"],"mappings":"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","ignoreList":[]}
|