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.
Files changed (60) hide show
  1. package/LICENSE +20 -0
  2. package/PrivacyGuardKit.podspec +27 -0
  3. package/README.md +320 -0
  4. package/android/CMakeLists.txt +47 -0
  5. package/android/build.gradle +64 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/com/privacyguardkit/PrivacyGuardKitPackage.kt +17 -0
  8. package/android/src/main/java/com/privacyguardkit/Privacyguardkitmodule .kt +208 -0
  9. package/android/src/main/java/com/privacyguardkit/Screenshotobserver.kt +66 -0
  10. package/android/src/main/java/com/privacyguardkit/Secureview.kt +104 -0
  11. package/android/src/main/java/com/privacyguardkit/Secureviewmanager.kt +27 -0
  12. package/android/src/main/jni/react/renderer/components/PrivacyGuardKitViewSpec/RNSecureViewComponentDescriptor.h +25 -0
  13. package/ios/PrivacyGuardKit-Umbrella.h +14 -0
  14. package/ios/PrivacyGuardKit.m +38 -0
  15. package/ios/PrivacyGuardKit.swift +221 -0
  16. package/ios/RNSecureViewComponentView.h +16 -0
  17. package/ios/RNSecureViewComponentView.mm +84 -0
  18. package/ios/RNSecureViewManager.mm +48 -0
  19. package/lib/module/Hooks.js +119 -0
  20. package/lib/module/Hooks.js.map +1 -0
  21. package/lib/module/NativePrivacyGuardKit.js +12 -0
  22. package/lib/module/NativePrivacyGuardKit.js.map +1 -0
  23. package/lib/module/PrivacyGuardProvider.js +99 -0
  24. package/lib/module/PrivacyGuardProvider.js.map +1 -0
  25. package/lib/module/PrivacyGuardkitApi.js +104 -0
  26. package/lib/module/PrivacyGuardkitApi.js.map +1 -0
  27. package/lib/module/SecureView.js +24 -0
  28. package/lib/module/SecureView.js.map +1 -0
  29. package/lib/module/index.js +16 -0
  30. package/lib/module/index.js.map +1 -0
  31. package/lib/module/package.json +1 -0
  32. package/lib/module/specs/RNSecureViewNativeComponent.ts +19 -0
  33. package/lib/module/types.js +2 -0
  34. package/lib/module/types.js.map +1 -0
  35. package/lib/typescript/package.json +1 -0
  36. package/lib/typescript/src/Hooks.d.ts +29 -0
  37. package/lib/typescript/src/Hooks.d.ts.map +1 -0
  38. package/lib/typescript/src/NativePrivacyGuardKit.d.ts +4 -0
  39. package/lib/typescript/src/NativePrivacyGuardKit.d.ts.map +1 -0
  40. package/lib/typescript/src/PrivacyGuardProvider.d.ts +30 -0
  41. package/lib/typescript/src/PrivacyGuardProvider.d.ts.map +1 -0
  42. package/lib/typescript/src/PrivacyGuardkitApi.d.ts +45 -0
  43. package/lib/typescript/src/PrivacyGuardkitApi.d.ts.map +1 -0
  44. package/lib/typescript/src/SecureView.d.ts +10 -0
  45. package/lib/typescript/src/SecureView.d.ts.map +1 -0
  46. package/lib/typescript/src/index.d.ts +6 -0
  47. package/lib/typescript/src/index.d.ts.map +1 -0
  48. package/lib/typescript/src/specs/RNSecureViewNativeComponent.d.ts +11 -0
  49. package/lib/typescript/src/specs/RNSecureViewNativeComponent.d.ts.map +1 -0
  50. package/lib/typescript/src/types.d.ts +29 -0
  51. package/lib/typescript/src/types.d.ts.map +1 -0
  52. package/package.json +174 -0
  53. package/src/Hooks.ts +138 -0
  54. package/src/NativePrivacyGuardKit.ts +13 -0
  55. package/src/PrivacyGuardProvider.tsx +134 -0
  56. package/src/PrivacyGuardkitApi.ts +123 -0
  57. package/src/SecureView.tsx +29 -0
  58. package/src/index.tsx +37 -0
  59. package/src/specs/RNSecureViewNativeComponent.ts +19 -0
  60. 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
+ }