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
package/src/Hooks.ts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
disableScreenCapture,
|
|
4
|
+
enableScreenCapture,
|
|
5
|
+
isScreenBeingRecorded,
|
|
6
|
+
enableAppSwitcherProtection,
|
|
7
|
+
disableAppSwitcherProtection,
|
|
8
|
+
clearClipboard,
|
|
9
|
+
onScreenshotTaken,
|
|
10
|
+
onScreenRecordingStarted,
|
|
11
|
+
onScreenRecordingStopped,
|
|
12
|
+
} from './PrivacyGuardkitApi';
|
|
13
|
+
import type { UsePrivacyGuardReturn, PrivacyGuardKitConfig } from './types';
|
|
14
|
+
|
|
15
|
+
// ─────────────────────────────────────────────────────────────
|
|
16
|
+
// usePrivacyGuard
|
|
17
|
+
// ─────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* One-stop hook that wires up all privacy guard features declaratively.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const { isRecording, disableScreenCapture } = usePrivacyGuard({
|
|
24
|
+
* disableScreenCapture: true,
|
|
25
|
+
* enableAppSwitcherProtection: true,
|
|
26
|
+
* });
|
|
27
|
+
*/
|
|
28
|
+
export function usePrivacyGuard(
|
|
29
|
+
config: PrivacyGuardKitConfig = {}
|
|
30
|
+
): UsePrivacyGuardReturn {
|
|
31
|
+
const [captureDisabled, setCaptureDisabled] = useState(false);
|
|
32
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
33
|
+
|
|
34
|
+
// Apply config on mount
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (config.disableScreenCapture) {
|
|
37
|
+
disableScreenCapture().then(() => setCaptureDisabled(true));
|
|
38
|
+
}
|
|
39
|
+
if (config.enableAppSwitcherProtection) {
|
|
40
|
+
enableAppSwitcherProtection();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Bootstrap recording state
|
|
44
|
+
isScreenBeingRecorded().then(setIsRecording);
|
|
45
|
+
|
|
46
|
+
// Recording listeners
|
|
47
|
+
const stopStart = onScreenRecordingStarted(() => setIsRecording(true));
|
|
48
|
+
const stopStop = onScreenRecordingStopped(() => setIsRecording(false));
|
|
49
|
+
|
|
50
|
+
return () => {
|
|
51
|
+
if (config.disableScreenCapture) {
|
|
52
|
+
enableScreenCapture().then(() => setCaptureDisabled(false));
|
|
53
|
+
}
|
|
54
|
+
if (config.enableAppSwitcherProtection) {
|
|
55
|
+
disableAppSwitcherProtection();
|
|
56
|
+
}
|
|
57
|
+
stopStart();
|
|
58
|
+
stopStop();
|
|
59
|
+
};
|
|
60
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
61
|
+
}, []);
|
|
62
|
+
|
|
63
|
+
const handleDisableCapture = useCallback(async () => {
|
|
64
|
+
await disableScreenCapture();
|
|
65
|
+
setCaptureDisabled(true);
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
const handleEnableCapture = useCallback(async () => {
|
|
69
|
+
await enableScreenCapture();
|
|
70
|
+
setCaptureDisabled(false);
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
isScreenCaptureDisabled: captureDisabled,
|
|
75
|
+
isRecording,
|
|
76
|
+
disableScreenCapture: handleDisableCapture,
|
|
77
|
+
enableScreenCapture: handleEnableCapture,
|
|
78
|
+
enableAppSwitcherProtection: async () => {
|
|
79
|
+
await enableAppSwitcherProtection();
|
|
80
|
+
},
|
|
81
|
+
disableAppSwitcherProtection: async () => {
|
|
82
|
+
await disableAppSwitcherProtection();
|
|
83
|
+
},
|
|
84
|
+
clearClipboard,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ─────────────────────────────────────────────────────────────
|
|
89
|
+
// useScreenshotListener
|
|
90
|
+
// ─────────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Calls `onTaken` every time the user takes a screenshot.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* useScreenshotListener(() => {
|
|
97
|
+
* console.log('Screenshot detected!');
|
|
98
|
+
* });
|
|
99
|
+
*/
|
|
100
|
+
export function useScreenshotListener(onTaken: () => void): void {
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
const remove = onScreenshotTaken(onTaken);
|
|
103
|
+
return remove;
|
|
104
|
+
}, [onTaken]);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ─────────────────────────────────────────────────────────────
|
|
108
|
+
// useScreenRecording
|
|
109
|
+
// ─────────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Reactive boolean — true while screen is being recorded.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* const isRecording = useScreenRecording();
|
|
116
|
+
* if (isRecording) return <BlurredScreen />;
|
|
117
|
+
*/
|
|
118
|
+
export function useScreenRecording(): boolean {
|
|
119
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
isScreenBeingRecorded().then(setIsRecording);
|
|
123
|
+
|
|
124
|
+
const stopStart = onScreenRecordingStarted(({ isRecording: r }) =>
|
|
125
|
+
setIsRecording(r)
|
|
126
|
+
);
|
|
127
|
+
const stopEnd = onScreenRecordingStopped(({ isRecording: r }) =>
|
|
128
|
+
setIsRecording(r)
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return () => {
|
|
132
|
+
stopStart();
|
|
133
|
+
stopEnd();
|
|
134
|
+
};
|
|
135
|
+
}, []);
|
|
136
|
+
|
|
137
|
+
return isRecording;
|
|
138
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NativeModules, NativeEventEmitter } from 'react-native';
|
|
2
|
+
|
|
3
|
+
const { PrivacyGuardKit } = NativeModules;
|
|
4
|
+
|
|
5
|
+
if (!PrivacyGuardKit) {
|
|
6
|
+
throw new Error(
|
|
7
|
+
'[react-native-privacy-guard-kit] Native module not found. ' +
|
|
8
|
+
'Make sure you have linked the library and rebuilt the app.'
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const NativePrivacyGuardKit = PrivacyGuardKit;
|
|
13
|
+
export const PrivacyGuardKitEmitter = new NativeEventEmitter(PrivacyGuardKit);
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useEffect,
|
|
5
|
+
useState,
|
|
6
|
+
useCallback,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
} from 'react';
|
|
9
|
+
import {
|
|
10
|
+
disableScreenCapture,
|
|
11
|
+
enableScreenCapture,
|
|
12
|
+
enableAppSwitcherProtection,
|
|
13
|
+
disableAppSwitcherProtection,
|
|
14
|
+
isScreenBeingRecorded,
|
|
15
|
+
clearClipboard,
|
|
16
|
+
onScreenRecordingStarted,
|
|
17
|
+
onScreenRecordingStopped,
|
|
18
|
+
onScreenshotTaken,
|
|
19
|
+
} from './PrivacyGuardkitApi';
|
|
20
|
+
import type { PrivacyGuardKitConfig, UsePrivacyGuardReturn } from './types';
|
|
21
|
+
|
|
22
|
+
// ─────────────────────────────────────────────────────────────
|
|
23
|
+
// Context
|
|
24
|
+
// ─────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
type PrivacyGuardContextType = UsePrivacyGuardReturn | null;
|
|
27
|
+
const PrivacyGuardContext = createContext<PrivacyGuardContextType>(null);
|
|
28
|
+
|
|
29
|
+
// ─────────────────────────────────────────────────────────────
|
|
30
|
+
// Provider
|
|
31
|
+
// ─────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
interface PrivacyGuardProviderProps {
|
|
34
|
+
children: ReactNode;
|
|
35
|
+
config?: PrivacyGuardKitConfig;
|
|
36
|
+
/** Called every time a screenshot is detected */
|
|
37
|
+
onScreenshot?: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Wrap your root (or any sensitive screen) with this provider.
|
|
42
|
+
* All children can call `usePrivacyGuardContext()` to access the API.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* <PrivacyGuardProvider
|
|
46
|
+
* config={{ disableScreenCapture: true, enableAppSwitcherProtection: true }}
|
|
47
|
+
* onScreenshot={() => Alert.alert('Screenshot blocked!')}
|
|
48
|
+
* >
|
|
49
|
+
* <App />
|
|
50
|
+
* </PrivacyGuardProvider>
|
|
51
|
+
*/
|
|
52
|
+
export function PrivacyGuardProvider({
|
|
53
|
+
children,
|
|
54
|
+
config = {},
|
|
55
|
+
onScreenshot,
|
|
56
|
+
}: PrivacyGuardProviderProps) {
|
|
57
|
+
const [captureDisabled, setCaptureDisabled] = useState(false);
|
|
58
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
// Apply config
|
|
62
|
+
if (config.disableScreenCapture) {
|
|
63
|
+
disableScreenCapture().then(() => setCaptureDisabled(true));
|
|
64
|
+
}
|
|
65
|
+
if (config.enableAppSwitcherProtection) {
|
|
66
|
+
enableAppSwitcherProtection();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
isScreenBeingRecorded().then(setIsRecording);
|
|
70
|
+
|
|
71
|
+
// Event subscriptions
|
|
72
|
+
const cleanups: Array<() => void> = [];
|
|
73
|
+
|
|
74
|
+
if (onScreenshot) {
|
|
75
|
+
cleanups.push(onScreenshotTaken(onScreenshot));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
cleanups.push(onScreenRecordingStarted(() => setIsRecording(true)));
|
|
79
|
+
cleanups.push(onScreenRecordingStopped(() => setIsRecording(false)));
|
|
80
|
+
|
|
81
|
+
return () => {
|
|
82
|
+
cleanups.forEach((fn) => fn());
|
|
83
|
+
if (config.disableScreenCapture) enableScreenCapture();
|
|
84
|
+
if (config.enableAppSwitcherProtection) disableAppSwitcherProtection();
|
|
85
|
+
};
|
|
86
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
87
|
+
}, []);
|
|
88
|
+
|
|
89
|
+
const handleDisable = useCallback(async () => {
|
|
90
|
+
await disableScreenCapture();
|
|
91
|
+
setCaptureDisabled(true);
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
const handleEnable = useCallback(async () => {
|
|
95
|
+
await enableScreenCapture();
|
|
96
|
+
setCaptureDisabled(false);
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
99
|
+
const value: UsePrivacyGuardReturn = {
|
|
100
|
+
isScreenCaptureDisabled: captureDisabled,
|
|
101
|
+
isRecording,
|
|
102
|
+
disableScreenCapture: handleDisable,
|
|
103
|
+
enableScreenCapture: handleEnable,
|
|
104
|
+
enableAppSwitcherProtection: async () => enableAppSwitcherProtection(),
|
|
105
|
+
disableAppSwitcherProtection: async () => disableAppSwitcherProtection(),
|
|
106
|
+
clearClipboard,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<PrivacyGuardContext.Provider value={value}>
|
|
111
|
+
{children}
|
|
112
|
+
</PrivacyGuardContext.Provider>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ─────────────────────────────────────────────────────────────
|
|
117
|
+
// Consumer hook
|
|
118
|
+
// ─────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Access the PrivacyGuard API from any component inside the provider.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* const { isRecording } = usePrivacyGuardContext();
|
|
125
|
+
*/
|
|
126
|
+
export function usePrivacyGuardContext(): UsePrivacyGuardReturn {
|
|
127
|
+
const ctx = useContext<PrivacyGuardContextType>(PrivacyGuardContext);
|
|
128
|
+
if (!ctx) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
'usePrivacyGuardContext must be used inside <PrivacyGuardProvider>'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return ctx;
|
|
134
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NativePrivacyGuardKit,
|
|
3
|
+
PrivacyGuardKitEmitter,
|
|
4
|
+
} from './NativePrivacyGuardKit';
|
|
5
|
+
import type { ScreenRecordingEventPayload } from './types';
|
|
6
|
+
|
|
7
|
+
// ─────────────────────────────────────────────────────────────
|
|
8
|
+
// Screen Capture
|
|
9
|
+
// ─────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Prevents screenshots and blurs screen-recording output.
|
|
13
|
+
* On Android uses FLAG_SECURE; on iOS uses a secure UITextField overlay.
|
|
14
|
+
*/
|
|
15
|
+
export async function disableScreenCapture(): Promise<void> {
|
|
16
|
+
await NativePrivacyGuardKit.disableScreenCapture();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Re-enables screenshots and screen-recording. */
|
|
20
|
+
export async function enableScreenCapture(): Promise<void> {
|
|
21
|
+
await NativePrivacyGuardKit.enableScreenCapture();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Returns true if screen capture is currently disabled. */
|
|
25
|
+
export async function isScreenCaptureDisabled(): Promise<boolean> {
|
|
26
|
+
return NativePrivacyGuardKit.isScreenCaptureDisabled();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ─────────────────────────────────────────────────────────────
|
|
30
|
+
// Screen Recording Detection
|
|
31
|
+
// ─────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
/** Returns true if the screen is currently being recorded/mirrored. */
|
|
34
|
+
export async function isScreenBeingRecorded(): Promise<boolean> {
|
|
35
|
+
return NativePrivacyGuardKit.isScreenBeingRecorded();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─────────────────────────────────────────────────────────────
|
|
39
|
+
// App Switcher Protection
|
|
40
|
+
// ─────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Covers the app preview shown in the OS task switcher with a
|
|
44
|
+
* blank overlay, preventing sensitive content from being visible.
|
|
45
|
+
*/
|
|
46
|
+
export async function enableAppSwitcherProtection(): Promise<void> {
|
|
47
|
+
await NativePrivacyGuardKit.enableAppSwitcherProtection();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Removes the app switcher overlay. */
|
|
51
|
+
export async function disableAppSwitcherProtection(): Promise<void> {
|
|
52
|
+
await NativePrivacyGuardKit.disableAppSwitcherProtection();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ─────────────────────────────────────────────────────────────
|
|
56
|
+
// Clipboard
|
|
57
|
+
// ─────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
/** Clears the system clipboard immediately. */
|
|
60
|
+
export async function clearClipboard(): Promise<void> {
|
|
61
|
+
await NativePrivacyGuardKit.clearClipboard();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ─────────────────────────────────────────────────────────────
|
|
65
|
+
// Event Listeners
|
|
66
|
+
// ─────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
type ScreenshotListener = () => void;
|
|
69
|
+
type RecordingListener = (payload: ScreenRecordingEventPayload) => void;
|
|
70
|
+
|
|
71
|
+
// NativeEventEmitter expects (...args: readonly Object[]) => unknown
|
|
72
|
+
// We wrap the typed callback to satisfy that constraint safely.
|
|
73
|
+
type NativeListener = (...args: readonly object[]) => unknown;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Starts the native screenshot observer and registers your callback.
|
|
77
|
+
* Returns a cleanup function — call it to unsubscribe.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* const remove = onScreenshotTaken(() => Alert.alert('Screenshot detected!'));
|
|
81
|
+
* // later:
|
|
82
|
+
* remove();
|
|
83
|
+
*/
|
|
84
|
+
export function onScreenshotTaken(callback: ScreenshotListener): () => void {
|
|
85
|
+
NativePrivacyGuardKit.startScreenshotListener();
|
|
86
|
+
const sub = PrivacyGuardKitEmitter.addListener(
|
|
87
|
+
'onScreenshotTaken',
|
|
88
|
+
callback as NativeListener
|
|
89
|
+
);
|
|
90
|
+
return () => {
|
|
91
|
+
sub.remove();
|
|
92
|
+
NativePrivacyGuardKit.stopScreenshotListener();
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Fires when screen recording begins.
|
|
98
|
+
* Returns a cleanup function.
|
|
99
|
+
*/
|
|
100
|
+
export function onScreenRecordingStarted(
|
|
101
|
+
callback: RecordingListener
|
|
102
|
+
): () => void {
|
|
103
|
+
NativePrivacyGuardKit.startScreenshotListener(); // same listener start
|
|
104
|
+
const sub = PrivacyGuardKitEmitter.addListener(
|
|
105
|
+
'onScreenRecordingStarted',
|
|
106
|
+
callback as NativeListener
|
|
107
|
+
);
|
|
108
|
+
return () => sub.remove();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Fires when screen recording stops.
|
|
113
|
+
* Returns a cleanup function.
|
|
114
|
+
*/
|
|
115
|
+
export function onScreenRecordingStopped(
|
|
116
|
+
callback: RecordingListener
|
|
117
|
+
): () => void {
|
|
118
|
+
const sub = PrivacyGuardKitEmitter.addListener(
|
|
119
|
+
'onScreenRecordingStopped',
|
|
120
|
+
callback as NativeListener
|
|
121
|
+
);
|
|
122
|
+
return () => sub.remove();
|
|
123
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// SecureView.tsx
|
|
2
|
+
import { type ReactNode } from 'react';
|
|
3
|
+
import { StyleSheet, type ViewStyle, type StyleProp } from 'react-native';
|
|
4
|
+
import RNSecureViewSpec from './specs/RNSecureViewNativeComponent';
|
|
5
|
+
|
|
6
|
+
type SecureViewProps = {
|
|
7
|
+
disableCopyPaste?: boolean;
|
|
8
|
+
style?: StyleProp<ViewStyle>;
|
|
9
|
+
children?: ReactNode;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function SecureView({
|
|
13
|
+
disableCopyPaste = true,
|
|
14
|
+
style,
|
|
15
|
+
children,
|
|
16
|
+
}: SecureViewProps) {
|
|
17
|
+
return (
|
|
18
|
+
<RNSecureViewSpec
|
|
19
|
+
isCopyPasteDisabled={disableCopyPaste}
|
|
20
|
+
style={[styles.container, style]}
|
|
21
|
+
>
|
|
22
|
+
{children}
|
|
23
|
+
</RNSecureViewSpec>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const styles = StyleSheet.create({
|
|
28
|
+
container: { flex: 1 },
|
|
29
|
+
});
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// API
|
|
2
|
+
export {
|
|
3
|
+
disableScreenCapture,
|
|
4
|
+
enableScreenCapture,
|
|
5
|
+
isScreenCaptureDisabled,
|
|
6
|
+
isScreenBeingRecorded,
|
|
7
|
+
enableAppSwitcherProtection,
|
|
8
|
+
disableAppSwitcherProtection,
|
|
9
|
+
clearClipboard,
|
|
10
|
+
onScreenshotTaken,
|
|
11
|
+
onScreenRecordingStarted,
|
|
12
|
+
onScreenRecordingStopped,
|
|
13
|
+
} from './PrivacyGuardkitApi';
|
|
14
|
+
|
|
15
|
+
// Hooks
|
|
16
|
+
export {
|
|
17
|
+
usePrivacyGuard,
|
|
18
|
+
useScreenshotListener,
|
|
19
|
+
useScreenRecording,
|
|
20
|
+
} from './Hooks';
|
|
21
|
+
|
|
22
|
+
// Provider + context hook
|
|
23
|
+
export {
|
|
24
|
+
PrivacyGuardProvider,
|
|
25
|
+
usePrivacyGuardContext,
|
|
26
|
+
} from './PrivacyGuardProvider';
|
|
27
|
+
|
|
28
|
+
// Components
|
|
29
|
+
export { SecureView } from './SecureView';
|
|
30
|
+
|
|
31
|
+
// Types
|
|
32
|
+
export type {
|
|
33
|
+
PrivacyGuardEvent,
|
|
34
|
+
PrivacyGuardKitConfig,
|
|
35
|
+
UsePrivacyGuardReturn,
|
|
36
|
+
ScreenRecordingEventPayload,
|
|
37
|
+
} from './types';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @format
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// RN 0.73+ exports codegenNativeComponent from the top-level package.
|
|
6
|
+
// Deep imports like 'react-native/Libraries/Utilities/codegenNativeComponent'
|
|
7
|
+
// are deprecated and broken in RN 0.83. Use the top-level export instead.
|
|
8
|
+
|
|
9
|
+
import type { ViewProps } from 'react-native';
|
|
10
|
+
import { codegenNativeComponent } from 'react-native';
|
|
11
|
+
import type { HostComponent } from 'react-native';
|
|
12
|
+
|
|
13
|
+
export interface NativeProps extends ViewProps {
|
|
14
|
+
isCopyPasteDisabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default codegenNativeComponent<NativeProps>(
|
|
18
|
+
'RNSecureView'
|
|
19
|
+
) as HostComponent<NativeProps>;
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type PrivacyGuardEvent =
|
|
2
|
+
| 'onScreenshotTaken'
|
|
3
|
+
| 'onScreenRecordingStarted'
|
|
4
|
+
| 'onScreenRecordingStopped';
|
|
5
|
+
|
|
6
|
+
export interface ScreenRecordingEventPayload {
|
|
7
|
+
isRecording: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PrivacyGuardKitConfig {
|
|
11
|
+
/** Disable screen capture (screenshot + screen recording blur) */
|
|
12
|
+
disableScreenCapture?: boolean;
|
|
13
|
+
/** Show a blank overlay in the app switcher */
|
|
14
|
+
enableAppSwitcherProtection?: boolean;
|
|
15
|
+
/** Prevent copy/paste within SecureView children */
|
|
16
|
+
disableCopyPaste?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface UsePrivacyGuardReturn {
|
|
20
|
+
/** Whether screen capture is currently disabled */
|
|
21
|
+
isScreenCaptureDisabled: boolean;
|
|
22
|
+
/** Whether the screen is actively being recorded */
|
|
23
|
+
isRecording: boolean;
|
|
24
|
+
/** Disable screenshots and screen-recording preview */
|
|
25
|
+
disableScreenCapture: () => Promise<void>;
|
|
26
|
+
/** Re-enable screenshots */
|
|
27
|
+
enableScreenCapture: () => Promise<void>;
|
|
28
|
+
/** Enable app switcher blur overlay */
|
|
29
|
+
enableAppSwitcherProtection: () => Promise<void>;
|
|
30
|
+
/** Disable app switcher blur overlay */
|
|
31
|
+
disableAppSwitcherProtection: () => Promise<void>;
|
|
32
|
+
/** Clears the system clipboard */
|
|
33
|
+
clearClipboard: () => Promise<void>;
|
|
34
|
+
}
|