expo-screen-capture 7.2.0-canary-20250709-136b77f → 7.2.0-canary-20250722-599a28f

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 (44) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/android/build.gradle +2 -2
  3. package/build/ScreenCapture.d.ts +27 -2
  4. package/build/ScreenCapture.d.ts.map +1 -1
  5. package/build/ScreenCapture.js +39 -2
  6. package/build/ScreenCapture.js.map +1 -1
  7. package/expo-module.config.json +1 -1
  8. package/ios/AnimatedBlurEffectView.swift +32 -0
  9. package/ios/ScreenCaptureModule.swift +170 -7
  10. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.module → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.module} +7 -7
  11. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.module.md5 +1 -0
  12. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.module.sha1 +1 -0
  13. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.module.sha256 +1 -0
  14. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.module.sha512 +1 -0
  15. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.pom → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.pom} +1 -1
  16. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.pom.md5 +1 -0
  17. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.pom.sha1 +1 -0
  18. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.pom.sha256 +1 -0
  19. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.pom.sha512 +1 -0
  20. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/maven-metadata.xml +4 -4
  21. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/maven-metadata.xml.md5 +1 -1
  22. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/maven-metadata.xml.sha1 +1 -1
  23. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/maven-metadata.xml.sha256 +1 -1
  24. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/maven-metadata.xml.sha512 +1 -1
  25. package/package.json +3 -3
  26. package/src/ScreenCapture.ts +43 -2
  27. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.module.md5 +0 -1
  28. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.module.sha1 +0 -1
  29. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.module.sha256 +0 -1
  30. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.module.sha512 +0 -1
  31. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.pom.md5 +0 -1
  32. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.pom.sha1 +0 -1
  33. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.pom.sha256 +0 -1
  34. package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.pom.sha512 +0 -1
  35. /package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f-sources.jar → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f-sources.jar} +0 -0
  36. /package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f-sources.jar.md5 → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f-sources.jar.md5} +0 -0
  37. /package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f-sources.jar.sha1 → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f-sources.jar.sha1} +0 -0
  38. /package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f-sources.jar.sha256 → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f-sources.jar.sha256} +0 -0
  39. /package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f-sources.jar.sha512 → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f-sources.jar.sha512} +0 -0
  40. /package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.aar → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.aar} +0 -0
  41. /package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.aar.md5 → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.aar.md5} +0 -0
  42. /package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.aar.sha1 → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.aar.sha1} +0 -0
  43. /package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.aar.sha256 → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.aar.sha256} +0 -0
  44. /package/local-maven-repo/host/exp/exponent/expo.modules.screencapture/{7.2.0-canary-20250709-136b77f/expo.modules.screencapture-7.2.0-canary-20250709-136b77f.aar.sha512 → 7.2.0-canary-20250722-599a28f/expo.modules.screencapture-7.2.0-canary-20250722-599a28f.aar.sha512} +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,8 @@
7
7
  ### 🎉 New features
8
8
 
9
9
  - Implemented `useScreenshotListener` hook. ([#37411](https://github.com/expo/expo/pull/37411) by [@hryhoriiK97](https://github.com/hryhoriiK97))
10
+ - Implemented screenshot prevention on iOS. ([#37874](https://github.com/expo/expo/pull/37874) by [@hryhoriiK97](https://github.com/hryhoriiK97))
11
+ - Implement App Switcher protection on iOS. ([#38192](https://github.com/expo/expo/pull/38192) by [@hryhoriiK97](https://github.com/hryhoriiK97))
10
12
 
11
13
  ### 🐛 Bug fixes
12
14
 
@@ -4,13 +4,13 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '7.2.0-canary-20250709-136b77f'
7
+ version = '7.2.0-canary-20250722-599a28f'
8
8
 
9
9
  android {
10
10
  namespace "expo.modules.screencapture"
11
11
  defaultConfig {
12
12
  versionCode 7
13
- versionName '7.2.0-canary-20250709-136b77f'
13
+ versionName '7.2.0-canary-20250722-599a28f'
14
14
  }
15
15
  }
16
16
 
@@ -10,7 +10,7 @@ export declare function isAvailableAsync(): Promise<boolean>;
10
10
  * Prevents screenshots and screen recordings until `allowScreenCaptureAsync` is called or the app is restarted. If you are
11
11
  * already preventing screen capture, this method does nothing (unless you pass a new and unique `key`).
12
12
  *
13
- * > On iOS, this will only prevent screen recordings, and is only available on iOS 11 and newer. On older
13
+ * > On iOS, this prevents screen recordings and screenshots, and is only available on iOS 11+ (recordings) and iOS 13+ (screenshots). On older
14
14
  * iOS versions, this method does nothing.
15
15
  *
16
16
  * @param key Optional. If provided, this will help prevent multiple instances of the `preventScreenCaptureAsync`
@@ -18,7 +18,7 @@ export declare function isAvailableAsync(): Promise<boolean>;
18
18
  * When using multiple keys, you'll have to re-allow each one in order to re-enable screen capturing.
19
19
  *
20
20
  * @platform android
21
- * @platform ios 11+
21
+ * @platform ios
22
22
  */
23
23
  export declare function preventScreenCaptureAsync(key?: string): Promise<void>;
24
24
  /**
@@ -40,6 +40,31 @@ export declare function allowScreenCaptureAsync(key?: string): Promise<void>;
40
40
  * hook.
41
41
  */
42
42
  export declare function usePreventScreenCapture(key?: string): void;
43
+ /**
44
+ * Enables a privacy protection blur overlay that hides sensitive content when the app is not in focus.
45
+ * The overlay applies a customizable blur effect when the app is in the app switcher, background, or during interruptions
46
+ * (calls, Siri, Control Center, etc.), and automatically removes it when the app becomes active again.
47
+ *
48
+ * This provides visual privacy protection by preventing sensitive app content from being visible in:
49
+ * - App switcher previews
50
+ * - Background app snapshots
51
+ * - Screenshots taken during inactive states
52
+ *
53
+ * For Android, app switcher protection is automatically provided by `preventScreenCaptureAsync()`
54
+ * using the FLAG_SECURE window flag, which shows a blank screen in the recent apps preview.
55
+ *
56
+ * @param blurIntensity The intensity of the blur effect, from 0.0 (no blur) to 1.0 (maximum blur). Default is 0.5.
57
+ *
58
+ * @platform ios
59
+ *
60
+ */
61
+ export declare function enableAppSwitcherProtectionAsync(blurIntensity?: number): Promise<void>;
62
+ /**
63
+ * Disables the privacy protection overlay that was previously enabled with `enableAppSwitcherProtectionAsync`.
64
+ *
65
+ * @platform ios
66
+ */
67
+ export declare function disableAppSwitcherProtectionAsync(): Promise<void>;
43
68
  /**
44
69
  * Adds a listener that will fire whenever the user takes a screenshot while the app is foregrounded.
45
70
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ScreenCapture.d.ts","sourceRoot":"","sources":["../src/ScreenCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,kBAAkB,EAClB,gBAAgB,EAEhB,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAU3B;;;;;GAKG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAEzD;AAGD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,yBAAyB,CAAC,GAAG,GAAE,MAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAStF;AAGD;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,GAAE,MAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CASpF;AAGD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAkB,GAAG,IAAI,CAQrE;AAGD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,iBAAiB,CAE7E;AAGD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,iBAAiB,QAEvE;AAGD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,QAQzD;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAKvE;AAED;;;;KAIK;AACL,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAK3E;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,4JAGzB,CAAC;AASH,OAAO,EACL,iBAAiB,IAAI,YAAY,EACjC,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,GACtB,CAAC"}
1
+ {"version":3,"file":"ScreenCapture.d.ts","sourceRoot":"","sources":["../src/ScreenCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,kBAAkB,EAClB,gBAAgB,EAEhB,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,mBAAmB,CAAC;AAU3B;;;;;GAKG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAEzD;AAGD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,yBAAyB,CAAC,GAAG,GAAE,MAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAStF;AAGD;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,GAAE,MAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CASpF;AAGD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAkB,GAAG,IAAI,CAQrE;AAGD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,gCAAgC,CAAC,aAAa,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAMjG;AAGD;;;;GAIG;AACH,wBAAsB,iCAAiC,IAAI,OAAO,CAAC,IAAI,CAAC,CAMvE;AAGD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,iBAAiB,CAE7E;AAGD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,iBAAiB,QAEvE;AAGD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,QAQzD;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAKvE;AAED;;;;KAIK;AACL,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAK3E;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,4JAGzB,CAAC;AASH,OAAO,EACL,iBAAiB,IAAI,YAAY,EACjC,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,GACtB,CAAC"}
@@ -18,7 +18,7 @@ export async function isAvailableAsync() {
18
18
  * Prevents screenshots and screen recordings until `allowScreenCaptureAsync` is called or the app is restarted. If you are
19
19
  * already preventing screen capture, this method does nothing (unless you pass a new and unique `key`).
20
20
  *
21
- * > On iOS, this will only prevent screen recordings, and is only available on iOS 11 and newer. On older
21
+ * > On iOS, this prevents screen recordings and screenshots, and is only available on iOS 11+ (recordings) and iOS 13+ (screenshots). On older
22
22
  * iOS versions, this method does nothing.
23
23
  *
24
24
  * @param key Optional. If provided, this will help prevent multiple instances of the `preventScreenCaptureAsync`
@@ -26,7 +26,7 @@ export async function isAvailableAsync() {
26
26
  * When using multiple keys, you'll have to re-allow each one in order to re-enable screen capturing.
27
27
  *
28
28
  * @platform android
29
- * @platform ios 11+
29
+ * @platform ios
30
30
  */
31
31
  export async function preventScreenCaptureAsync(key = 'default') {
32
32
  if (!ExpoScreenCapture.preventScreenCapture) {
@@ -74,6 +74,43 @@ export function usePreventScreenCapture(key = 'default') {
74
74
  }, [key]);
75
75
  }
76
76
  // @needsAudit
77
+ /**
78
+ * Enables a privacy protection blur overlay that hides sensitive content when the app is not in focus.
79
+ * The overlay applies a customizable blur effect when the app is in the app switcher, background, or during interruptions
80
+ * (calls, Siri, Control Center, etc.), and automatically removes it when the app becomes active again.
81
+ *
82
+ * This provides visual privacy protection by preventing sensitive app content from being visible in:
83
+ * - App switcher previews
84
+ * - Background app snapshots
85
+ * - Screenshots taken during inactive states
86
+ *
87
+ * For Android, app switcher protection is automatically provided by `preventScreenCaptureAsync()`
88
+ * using the FLAG_SECURE window flag, which shows a blank screen in the recent apps preview.
89
+ *
90
+ * @param blurIntensity The intensity of the blur effect, from 0.0 (no blur) to 1.0 (maximum blur). Default is 0.5.
91
+ *
92
+ * @platform ios
93
+ *
94
+ */
95
+ export async function enableAppSwitcherProtectionAsync(blurIntensity = 0.5) {
96
+ if (!ExpoScreenCapture.enableAppSwitcherProtection) {
97
+ throw new UnavailabilityError('ScreenCapture', 'enableAppSwitcherProtectionAsync');
98
+ }
99
+ await ExpoScreenCapture.enableAppSwitcherProtection(blurIntensity);
100
+ }
101
+ // @needsAudit
102
+ /**
103
+ * Disables the privacy protection overlay that was previously enabled with `enableAppSwitcherProtectionAsync`.
104
+ *
105
+ * @platform ios
106
+ */
107
+ export async function disableAppSwitcherProtectionAsync() {
108
+ if (!ExpoScreenCapture.disableAppSwitcherProtection) {
109
+ throw new UnavailabilityError('ScreenCapture', 'disableAppSwitcherProtectionAsync');
110
+ }
111
+ await ExpoScreenCapture.disableAppSwitcherProtection();
112
+ }
113
+ // @needsAudit
77
114
  /**
78
115
  * Adds a listener that will fire whenever the user takes a screenshot while the app is foregrounded.
79
116
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ScreenCapture.js","sourceRoot":"","sources":["../src/ScreenCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EAEnB,gBAAgB,EAChB,oBAAoB,GAGrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAEpD,MAAM,UAAU,GAAgB,IAAI,GAAG,EAAE,CAAC;AAE1C,MAAM,qBAAqB,GAAG,cAAc,CAAC;AAE7C,cAAc;AACd;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,OAAO,CAAC,CAAC,iBAAiB,CAAC,oBAAoB,IAAI,CAAC,CAAC,iBAAiB,CAAC,kBAAkB,CAAC;AAC5F,CAAC;AAED,cAAc;AACd;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,MAAc,SAAS;IACrE,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,EAAE,CAAC;QAC5C,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,2BAA2B,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpB,MAAM,iBAAiB,CAAC,oBAAoB,EAAE,CAAC;IACjD,CAAC;AACH,CAAC;AAED,cAAc;AACd;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,MAAc,SAAS;IACnE,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,EAAE,CAAC;QAC5C,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,yBAAyB,CAAC,CAAC;IAC5E,CAAC;IAED,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,iBAAiB,CAAC,kBAAkB,EAAE,CAAC;IAC/C,CAAC;AACH,CAAC;AAED,cAAc;AACd;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAc,SAAS;IAC7D,SAAS,CAAC,GAAG,EAAE;QACb,yBAAyB,CAAC,GAAG,CAAC,CAAC;QAE/B,OAAO,GAAG,EAAE;YACV,uBAAuB,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;AACZ,CAAC;AAED,cAAc;AACd;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAoB;IACxD,OAAO,iBAAiB,CAAC,WAAW,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC;AACxE,CAAC;AAED,cAAc;AACd;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,wBAAwB,CAAC,YAA+B;IACtE,YAAY,CAAC,MAAM,EAAE,CAAC;AACxB,CAAC;AAED,cAAc;AACd;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAoB;IACxD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,YAAY,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QAErD,OAAO,GAAG,EAAE;YACV,wBAAwB,CAAC,YAAY,CAAC,CAAC;QACzC,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,IAAI,iBAAiB,CAAC,mBAAmB,EAAE,CAAC;QAC1C,OAAO,iBAAiB,CAAC,mBAAmB,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,0BAA0B,CAAC;AACpC,CAAC;AAED;;;;KAIK;AACL,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,IAAI,iBAAiB,CAAC,uBAAuB,EAAE,CAAC;QAC9C,OAAO,iBAAiB,CAAC,uBAAuB,EAAE,CAAC;IACrD,CAAC;IACD,OAAO,0BAA0B,CAAC;AACpC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,oBAAoB,CAAC;IACjD,SAAS,EAAE,mBAAmB;IAC9B,aAAa,EAAE,uBAAuB;CACvC,CAAC,CAAC;AAEH,MAAM,0BAA0B,GAAuB;IACrD,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,OAAO;IAChB,WAAW,EAAE,IAAI;IACjB,MAAM,EAAE,gBAAgB,CAAC,OAAO;CACjC,CAAC;AAEF,OAAO,EAGL,gBAAgB,GAEjB,CAAC","sourcesContent":["import {\n UnavailabilityError,\n PermissionResponse,\n PermissionStatus,\n createPermissionHook,\n PermissionHookOptions,\n EventSubscription,\n} from 'expo-modules-core';\nimport { useEffect } from 'react';\n\nimport ExpoScreenCapture from './ExpoScreenCapture';\n\nconst activeTags: Set<string> = new Set();\n\nconst onScreenshotEventName = 'onScreenshot';\n\n// @needsAudit\n/**\n * Returns whether the Screen Capture API is available on the current device.\n *\n * @returns A promise that resolves to a `boolean` indicating whether the Screen Capture API is available on the current\n * device.\n */\nexport async function isAvailableAsync(): Promise<boolean> {\n return !!ExpoScreenCapture.preventScreenCapture && !!ExpoScreenCapture.allowScreenCapture;\n}\n\n// @needsAudit\n/**\n * Prevents screenshots and screen recordings until `allowScreenCaptureAsync` is called or the app is restarted. If you are\n * already preventing screen capture, this method does nothing (unless you pass a new and unique `key`).\n *\n * > On iOS, this will only prevent screen recordings, and is only available on iOS 11 and newer. On older\n * iOS versions, this method does nothing.\n *\n * @param key Optional. If provided, this will help prevent multiple instances of the `preventScreenCaptureAsync`\n * and `allowScreenCaptureAsync` methods (and `usePreventScreenCapture` hook) from conflicting with each other.\n * When using multiple keys, you'll have to re-allow each one in order to re-enable screen capturing.\n *\n * @platform android\n * @platform ios 11+\n */\nexport async function preventScreenCaptureAsync(key: string = 'default'): Promise<void> {\n if (!ExpoScreenCapture.preventScreenCapture) {\n throw new UnavailabilityError('ScreenCapture', 'preventScreenCaptureAsync');\n }\n\n if (!activeTags.has(key)) {\n activeTags.add(key);\n await ExpoScreenCapture.preventScreenCapture();\n }\n}\n\n// @needsAudit\n/**\n * Re-allows the user to screen record or screenshot your app. If you haven't called\n * `preventScreenCapture()` yet, this method does nothing.\n *\n * @param key This will prevent multiple instances of the `preventScreenCaptureAsync` and\n * `allowScreenCaptureAsync` methods from conflicting with each other. If provided, the value must\n * be the same as the key passed to `preventScreenCaptureAsync` in order to re-enable screen\n * capturing.\n */\nexport async function allowScreenCaptureAsync(key: string = 'default'): Promise<void> {\n if (!ExpoScreenCapture.preventScreenCapture) {\n throw new UnavailabilityError('ScreenCapture', 'allowScreenCaptureAsync');\n }\n\n activeTags.delete(key);\n if (activeTags.size === 0) {\n await ExpoScreenCapture.allowScreenCapture();\n }\n}\n\n// @needsAudit\n/**\n * A React hook to prevent screen capturing for as long as the owner component is mounted.\n *\n * @param key If provided, this will prevent multiple instances of this hook or the\n * `preventScreenCaptureAsync` and `allowScreenCaptureAsync` methods from conflicting with each other.\n * This argument is useful if you have multiple active components using the `allowScreenCaptureAsync`\n * hook.\n */\nexport function usePreventScreenCapture(key: string = 'default'): void {\n useEffect(() => {\n preventScreenCaptureAsync(key);\n\n return () => {\n allowScreenCaptureAsync(key);\n };\n }, [key]);\n}\n\n// @needsAudit\n/**\n * Adds a listener that will fire whenever the user takes a screenshot while the app is foregrounded.\n *\n * Permission requirements for this method depend on your device’s Android version:\n * - **Before Android 13**: Requires `READ_EXTERNAL_STORAGE`.\n * - **Android 13**: Switches to `READ_MEDIA_IMAGES`.\n * - **Post-Android 13**: No additional permissions required.\n * You can request the appropriate permissions by using [`MediaLibrary.requestPermissionsAsync()`](./media-library/#medialibraryrequestpermissionsasync).\n *\n * @param listener The function that will be executed when the user takes a screenshot.\n * This function accepts no arguments.\n *\n * @return A `Subscription` object that you can use to unregister the listener, either by calling\n * `remove()` or passing it to `removeScreenshotListener`.\n */\nexport function addScreenshotListener(listener: () => void): EventSubscription {\n return ExpoScreenCapture.addListener(onScreenshotEventName, listener);\n}\n\n// @needsAudit\n/**\n * Removes the subscription you provide, so that you are no longer listening for screenshots.\n * You can also call `remove()` on that `Subscription` object.\n *\n * @param subscription Subscription returned by `addScreenshotListener`.\n *\n * @example\n * ```ts\n * let mySubscription = addScreenshotListener(() => {\n * console.log(\"You took a screenshot!\");\n * });\n * ...\n * mySubscription.remove();\n * // OR\n * removeScreenshotListener(mySubscription);\n * ```\n */\nexport function removeScreenshotListener(subscription: EventSubscription) {\n subscription.remove();\n}\n\n// @needsAudit\n/**\n * A React hook that listens for screenshots taken while the component is mounted.\n *\n * @param listener A function that will be called whenever a screenshot is detected.\n *\n * This hook automatically starts listening when the component mounts, and stops\n * listening when the component unmounts.\n */\nexport function useScreenshotListener(listener: () => void) {\n useEffect(() => {\n const subscription = addScreenshotListener(listener);\n\n return () => {\n removeScreenshotListener(subscription);\n };\n }, [listener]);\n}\n\n/**\n * Checks user's permissions for detecting when a screenshot is taken.\n * > Only Android requires additional permissions to detect screenshots. On iOS devices, this method will always resolve to a `granted` permission response.\n * @return A promise that resolves to a [`PermissionResponse`](#permissionresponse) object.\n */\nexport async function getPermissionsAsync(): Promise<PermissionResponse> {\n if (ExpoScreenCapture.getPermissionsAsync) {\n return ExpoScreenCapture.getPermissionsAsync();\n }\n return defaultPermissionsResponse;\n}\n\n/**\n * Asks the user to grant permissions necessary for detecting when a screenshot is taken.\n * > Only Android requires additional permissions to detect screenshots. On iOS devices, this method will always resolve to a `granted` permission response.\n * @return A promise that resolves to a [`PermissionResponse`](#permissionresponse) object.\n * */\nexport async function requestPermissionsAsync(): Promise<PermissionResponse> {\n if (ExpoScreenCapture.requestPermissionsAsync) {\n return ExpoScreenCapture.requestPermissionsAsync();\n }\n return defaultPermissionsResponse;\n}\n\n/**\n * Check or request permissions necessary for detecting when a screenshot is taken.\n * This uses both [`requestPermissionsAsync`](#screencapturerequestpermissionsasync) and [`getPermissionsAsync`](#screencapturegetpermissionsasync) to interact with the permissions.\n *\n * @example\n * ```js\n * const [status, requestPermission] = ScreenCapture.usePermissions();\n * ```\n */\nexport const usePermissions = createPermissionHook({\n getMethod: getPermissionsAsync,\n requestMethod: requestPermissionsAsync,\n});\n\nconst defaultPermissionsResponse: PermissionResponse = {\n granted: true,\n expires: 'never',\n canAskAgain: true,\n status: PermissionStatus.GRANTED,\n};\n\nexport {\n EventSubscription as Subscription,\n PermissionResponse,\n PermissionStatus,\n PermissionHookOptions,\n};\n"]}
1
+ {"version":3,"file":"ScreenCapture.js","sourceRoot":"","sources":["../src/ScreenCapture.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n UnavailabilityError,\n PermissionResponse,\n PermissionStatus,\n createPermissionHook,\n PermissionHookOptions,\n EventSubscription,\n} from 'expo-modules-core';\nimport { useEffect } from 'react';\n\nimport ExpoScreenCapture from './ExpoScreenCapture';\n\nconst activeTags: Set<string> = new Set();\n\nconst onScreenshotEventName = 'onScreenshot';\n\n// @needsAudit\n/**\n * Returns whether the Screen Capture API is available on the current device.\n *\n * @returns A promise that resolves to a `boolean` indicating whether the Screen Capture API is available on the current\n * device.\n */\nexport async function isAvailableAsync(): Promise<boolean> {\n return !!ExpoScreenCapture.preventScreenCapture && !!ExpoScreenCapture.allowScreenCapture;\n}\n\n// @needsAudit\n/**\n * Prevents screenshots and screen recordings until `allowScreenCaptureAsync` is called or the app is restarted. If you are\n * already preventing screen capture, this method does nothing (unless you pass a new and unique `key`).\n *\n * > On iOS, this prevents screen recordings and screenshots, and is only available on iOS 11+ (recordings) and iOS 13+ (screenshots). On older\n * iOS versions, this method does nothing.\n *\n * @param key Optional. If provided, this will help prevent multiple instances of the `preventScreenCaptureAsync`\n * and `allowScreenCaptureAsync` methods (and `usePreventScreenCapture` hook) from conflicting with each other.\n * When using multiple keys, you'll have to re-allow each one in order to re-enable screen capturing.\n *\n * @platform android\n * @platform ios\n */\nexport async function preventScreenCaptureAsync(key: string = 'default'): Promise<void> {\n if (!ExpoScreenCapture.preventScreenCapture) {\n throw new UnavailabilityError('ScreenCapture', 'preventScreenCaptureAsync');\n }\n\n if (!activeTags.has(key)) {\n activeTags.add(key);\n await ExpoScreenCapture.preventScreenCapture();\n }\n}\n\n// @needsAudit\n/**\n * Re-allows the user to screen record or screenshot your app. If you haven't called\n * `preventScreenCapture()` yet, this method does nothing.\n *\n * @param key This will prevent multiple instances of the `preventScreenCaptureAsync` and\n * `allowScreenCaptureAsync` methods from conflicting with each other. If provided, the value must\n * be the same as the key passed to `preventScreenCaptureAsync` in order to re-enable screen\n * capturing.\n */\nexport async function allowScreenCaptureAsync(key: string = 'default'): Promise<void> {\n if (!ExpoScreenCapture.preventScreenCapture) {\n throw new UnavailabilityError('ScreenCapture', 'allowScreenCaptureAsync');\n }\n\n activeTags.delete(key);\n if (activeTags.size === 0) {\n await ExpoScreenCapture.allowScreenCapture();\n }\n}\n\n// @needsAudit\n/**\n * A React hook to prevent screen capturing for as long as the owner component is mounted.\n *\n * @param key If provided, this will prevent multiple instances of this hook or the\n * `preventScreenCaptureAsync` and `allowScreenCaptureAsync` methods from conflicting with each other.\n * This argument is useful if you have multiple active components using the `allowScreenCaptureAsync`\n * hook.\n */\nexport function usePreventScreenCapture(key: string = 'default'): void {\n useEffect(() => {\n preventScreenCaptureAsync(key);\n\n return () => {\n allowScreenCaptureAsync(key);\n };\n }, [key]);\n}\n\n// @needsAudit\n/**\n * Enables a privacy protection blur overlay that hides sensitive content when the app is not in focus.\n * The overlay applies a customizable blur effect when the app is in the app switcher, background, or during interruptions\n * (calls, Siri, Control Center, etc.), and automatically removes it when the app becomes active again.\n *\n * This provides visual privacy protection by preventing sensitive app content from being visible in:\n * - App switcher previews\n * - Background app snapshots\n * - Screenshots taken during inactive states\n *\n * For Android, app switcher protection is automatically provided by `preventScreenCaptureAsync()`\n * using the FLAG_SECURE window flag, which shows a blank screen in the recent apps preview.\n *\n * @param blurIntensity The intensity of the blur effect, from 0.0 (no blur) to 1.0 (maximum blur). Default is 0.5.\n *\n * @platform ios\n *\n */\nexport async function enableAppSwitcherProtectionAsync(blurIntensity: number = 0.5): Promise<void> {\n if (!ExpoScreenCapture.enableAppSwitcherProtection) {\n throw new UnavailabilityError('ScreenCapture', 'enableAppSwitcherProtectionAsync');\n }\n\n await ExpoScreenCapture.enableAppSwitcherProtection(blurIntensity);\n}\n\n// @needsAudit\n/**\n * Disables the privacy protection overlay that was previously enabled with `enableAppSwitcherProtectionAsync`.\n *\n * @platform ios\n */\nexport async function disableAppSwitcherProtectionAsync(): Promise<void> {\n if (!ExpoScreenCapture.disableAppSwitcherProtection) {\n throw new UnavailabilityError('ScreenCapture', 'disableAppSwitcherProtectionAsync');\n }\n\n await ExpoScreenCapture.disableAppSwitcherProtection();\n}\n\n// @needsAudit\n/**\n * Adds a listener that will fire whenever the user takes a screenshot while the app is foregrounded.\n *\n * Permission requirements for this method depend on your device’s Android version:\n * - **Before Android 13**: Requires `READ_EXTERNAL_STORAGE`.\n * - **Android 13**: Switches to `READ_MEDIA_IMAGES`.\n * - **Post-Android 13**: No additional permissions required.\n * You can request the appropriate permissions by using [`MediaLibrary.requestPermissionsAsync()`](./media-library/#medialibraryrequestpermissionsasync).\n *\n * @param listener The function that will be executed when the user takes a screenshot.\n * This function accepts no arguments.\n *\n * @return A `Subscription` object that you can use to unregister the listener, either by calling\n * `remove()` or passing it to `removeScreenshotListener`.\n */\nexport function addScreenshotListener(listener: () => void): EventSubscription {\n return ExpoScreenCapture.addListener(onScreenshotEventName, listener);\n}\n\n// @needsAudit\n/**\n * Removes the subscription you provide, so that you are no longer listening for screenshots.\n * You can also call `remove()` on that `Subscription` object.\n *\n * @param subscription Subscription returned by `addScreenshotListener`.\n *\n * @example\n * ```ts\n * let mySubscription = addScreenshotListener(() => {\n * console.log(\"You took a screenshot!\");\n * });\n * ...\n * mySubscription.remove();\n * // OR\n * removeScreenshotListener(mySubscription);\n * ```\n */\nexport function removeScreenshotListener(subscription: EventSubscription) {\n subscription.remove();\n}\n\n// @needsAudit\n/**\n * A React hook that listens for screenshots taken while the component is mounted.\n *\n * @param listener A function that will be called whenever a screenshot is detected.\n *\n * This hook automatically starts listening when the component mounts, and stops\n * listening when the component unmounts.\n */\nexport function useScreenshotListener(listener: () => void) {\n useEffect(() => {\n const subscription = addScreenshotListener(listener);\n\n return () => {\n removeScreenshotListener(subscription);\n };\n }, [listener]);\n}\n\n/**\n * Checks user's permissions for detecting when a screenshot is taken.\n * > Only Android requires additional permissions to detect screenshots. On iOS devices, this method will always resolve to a `granted` permission response.\n * @return A promise that resolves to a [`PermissionResponse`](#permissionresponse) object.\n */\nexport async function getPermissionsAsync(): Promise<PermissionResponse> {\n if (ExpoScreenCapture.getPermissionsAsync) {\n return ExpoScreenCapture.getPermissionsAsync();\n }\n return defaultPermissionsResponse;\n}\n\n/**\n * Asks the user to grant permissions necessary for detecting when a screenshot is taken.\n * > Only Android requires additional permissions to detect screenshots. On iOS devices, this method will always resolve to a `granted` permission response.\n * @return A promise that resolves to a [`PermissionResponse`](#permissionresponse) object.\n * */\nexport async function requestPermissionsAsync(): Promise<PermissionResponse> {\n if (ExpoScreenCapture.requestPermissionsAsync) {\n return ExpoScreenCapture.requestPermissionsAsync();\n }\n return defaultPermissionsResponse;\n}\n\n/**\n * Check or request permissions necessary for detecting when a screenshot is taken.\n * This uses both [`requestPermissionsAsync`](#screencapturerequestpermissionsasync) and [`getPermissionsAsync`](#screencapturegetpermissionsasync) to interact with the permissions.\n *\n * @example\n * ```js\n * const [status, requestPermission] = ScreenCapture.usePermissions();\n * ```\n */\nexport const usePermissions = createPermissionHook({\n getMethod: getPermissionsAsync,\n requestMethod: requestPermissionsAsync,\n});\n\nconst defaultPermissionsResponse: PermissionResponse = {\n granted: true,\n expires: 'never',\n canAskAgain: true,\n status: PermissionStatus.GRANTED,\n};\n\nexport {\n EventSubscription as Subscription,\n PermissionResponse,\n PermissionStatus,\n PermissionHookOptions,\n};\n"]}
@@ -8,7 +8,7 @@
8
8
  "publication": {
9
9
  "groupId": "host.exp.exponent",
10
10
  "artifactId": "expo.modules.screencapture",
11
- "version": "7.2.0-canary-20250709-136b77f",
11
+ "version": "7.2.0-canary-20250722-599a28f",
12
12
  "repository": "local-maven-repo"
13
13
  }
14
14
  }
@@ -0,0 +1,32 @@
1
+ class AnimatedBlurEffectView: UIVisualEffectView {
2
+ private let blurIntensity: CGFloat
3
+ private let blurStyle: UIBlurEffect.Style
4
+ private var animator = UIViewPropertyAnimator(duration: 1, curve: .linear)
5
+
6
+ private lazy var blurEffect = UIBlurEffect(style: blurStyle)
7
+
8
+ init(style: UIBlurEffect.Style = .light, intensity: CGFloat) {
9
+ self.blurStyle = style
10
+ self.blurIntensity = intensity
11
+ super.init(effect: nil)
12
+ self.backgroundColor = .clear
13
+ }
14
+
15
+ required init?(coder: NSCoder) {
16
+ self.blurStyle = .light
17
+ self.blurIntensity = 0.5
18
+ super.init(coder: coder)
19
+ self.backgroundColor = .clear
20
+ }
21
+
22
+ func setupBlur() {
23
+ animator.stopAnimation(true)
24
+ animator.addAnimations { [weak self] in
25
+ guard let self else {
26
+ return
27
+ }
28
+ self.effect = self.blurEffect
29
+ }
30
+ animator.fractionComplete = blurIntensity
31
+ }
32
+ }
@@ -6,6 +6,10 @@ public final class ScreenCaptureModule: Module {
6
6
  private var isBeingObserved = false
7
7
  private var isListening = false
8
8
  private var blockView = UIView()
9
+ private var protectionTextField: UITextField?
10
+ private var originalParent: CALayer?
11
+ private var blurEffectView: AnimatedBlurEffectView?
12
+ private var blurIntensity: CGFloat = 0.5
9
13
 
10
14
  public func definition() -> ModuleDefinition {
11
15
  Name("ExpoScreenCapture")
@@ -18,6 +22,11 @@ public final class ScreenCaptureModule: Module {
18
22
  blockView.backgroundColor = .black
19
23
  }
20
24
 
25
+ OnDestroy {
26
+ allowScreenshots()
27
+ disableAppSwitcherProtection()
28
+ }
29
+
21
30
  OnStartObserving {
22
31
  self.setIsBeing(observed: true)
23
32
  }
@@ -27,15 +36,35 @@ public final class ScreenCaptureModule: Module {
27
36
  }
28
37
 
29
38
  AsyncFunction("preventScreenCapture") {
30
- // If already recording, block it
31
39
  self.preventScreenRecording()
40
+ self.preventScreenshots()
32
41
 
33
- NotificationCenter.default.addObserver(self, selector: #selector(self.preventScreenRecording), name: UIScreen.capturedDidChangeNotification, object: nil)
42
+ NotificationCenter.default.addObserver(
43
+ self,
44
+ selector: #selector(self.preventScreenRecording),
45
+ name: UIScreen.capturedDidChangeNotification,
46
+ object: nil
47
+ )
34
48
  }.runOnQueue(.main)
35
49
 
36
50
  AsyncFunction("allowScreenCapture") {
37
- NotificationCenter.default.removeObserver(self, name: UIScreen.capturedDidChangeNotification, object: nil)
38
- }
51
+ self.allowScreenshots()
52
+
53
+ NotificationCenter.default.removeObserver(
54
+ self,
55
+ name: UIScreen.capturedDidChangeNotification,
56
+ object: nil
57
+ )
58
+ }.runOnQueue(.main)
59
+
60
+ AsyncFunction("enableAppSwitcherProtection") { (blurIntensity: CGFloat) in
61
+ self.blurIntensity = blurIntensity
62
+ enableAppSwitcherProtection()
63
+ }.runOnQueue(.main)
64
+
65
+ AsyncFunction("disableAppSwitcherProtection") {
66
+ disableAppSwitcherProtection()
67
+ }.runOnQueue(.main)
39
68
  }
40
69
 
41
70
  private func setIsBeing(observed: Bool) {
@@ -43,11 +72,19 @@ public final class ScreenCaptureModule: Module {
43
72
  let shouldListen = self.isBeingObserved
44
73
 
45
74
  if shouldListen && !isListening {
46
- // swiftlint:disable:next line_length
47
- NotificationCenter.default.addObserver(self, selector: #selector(self.listenForScreenCapture), name: UIApplication.userDidTakeScreenshotNotification, object: nil)
75
+ NotificationCenter.default.addObserver(
76
+ self,
77
+ selector: #selector(self.listenForScreenCapture),
78
+ name: UIApplication.userDidTakeScreenshotNotification,
79
+ object: nil
80
+ )
48
81
  isListening = true
49
82
  } else if !shouldListen && isListening {
50
- NotificationCenter.default.removeObserver(self, name: UIApplication.userDidTakeScreenshotNotification, object: nil)
83
+ NotificationCenter.default.removeObserver(
84
+ self,
85
+ name: UIApplication.userDidTakeScreenshotNotification,
86
+ object: nil
87
+ )
51
88
  isListening = false
52
89
  }
53
90
  }
@@ -69,4 +106,130 @@ public final class ScreenCaptureModule: Module {
69
106
  "body": nil
70
107
  ])
71
108
  }
109
+
110
+ private func preventScreenshots() {
111
+ guard let keyWindow = UIApplication.shared.keyWindow,
112
+ let visibleView = keyWindow.subviews.first else { return }
113
+
114
+ let textField = UITextField()
115
+ textField.isSecureTextEntry = true
116
+ textField.isUserInteractionEnabled = false
117
+ textField.backgroundColor = UIColor.black
118
+
119
+ originalParent = visibleView.layer.superlayer
120
+
121
+ if let viewSuperlayer = visibleView.layer.superlayer {
122
+ viewSuperlayer.addSublayer(textField.layer)
123
+
124
+ if let firstTextFieldSublayer = textField.layer.sublayers?.first {
125
+ visibleView.layer.removeFromSuperlayer()
126
+ visibleView.layer.position = CGPoint(x: visibleView.bounds.width / 2, y: visibleView.bounds.height / 2)
127
+ firstTextFieldSublayer.addSublayer(visibleView.layer)
128
+ }
129
+ }
130
+
131
+ protectionTextField = textField
132
+ }
133
+
134
+ private func allowScreenshots() {
135
+ guard let textField = protectionTextField else {
136
+ return
137
+ }
138
+
139
+ if let protectedLayer = textField.layer.sublayers?.first?.sublayers?.first {
140
+ protectedLayer.removeFromSuperlayer()
141
+ if let parent = originalParent {
142
+ parent.addSublayer(protectedLayer)
143
+ }
144
+ }
145
+
146
+ textField.layer.removeFromSuperlayer()
147
+
148
+ protectionTextField = nil
149
+ originalParent = nil
150
+ }
151
+
152
+ private func enableAppSwitcherProtection() {
153
+ NotificationCenter.default.addObserver(
154
+ self,
155
+ selector: #selector(appWillResignActive),
156
+ name: UIApplication.willResignActiveNotification,
157
+ object: nil
158
+ )
159
+
160
+ NotificationCenter.default.addObserver(
161
+ self,
162
+ selector: #selector(appDidBecomeActive),
163
+ name: UIApplication.didBecomeActiveNotification,
164
+ object: nil
165
+ )
166
+ }
167
+
168
+ private func disableAppSwitcherProtection() {
169
+ NotificationCenter.default.removeObserver(
170
+ self,
171
+ name: UIApplication.willResignActiveNotification,
172
+ object: nil
173
+ )
174
+
175
+ NotificationCenter.default.removeObserver(
176
+ self,
177
+ name: UIApplication.didBecomeActiveNotification,
178
+ object: nil
179
+ )
180
+
181
+ removePrivacyOverlay()
182
+ }
183
+
184
+ @objc
185
+ private func appWillResignActive() {
186
+ showPrivacyOverlay()
187
+ }
188
+
189
+ @objc
190
+ private func appDidBecomeActive() {
191
+ removePrivacyOverlay()
192
+ }
193
+
194
+ private func showPrivacyOverlay() {
195
+ if let keyWindow = UIApplication.shared.keyWindow,
196
+ let rootView = keyWindow.subviews.first {
197
+ let blurEffectView = AnimatedBlurEffectView(style: .light, intensity: self.blurIntensity)
198
+ blurEffectView.frame = rootView.bounds
199
+ blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
200
+ blurEffectView.alpha = 0
201
+
202
+ rootView.addSubview(blurEffectView)
203
+ self.blurEffectView = blurEffectView
204
+
205
+ blurEffectView.setupBlur()
206
+
207
+ UIView.animate(
208
+ withDuration: 0.3,
209
+ delay: 0,
210
+ options: [.curveEaseOut],
211
+ animations: {
212
+ blurEffectView.alpha = 1.0
213
+ }
214
+ )
215
+ }
216
+ }
217
+
218
+ private func removePrivacyOverlay() {
219
+ guard let blurEffectView = self.blurEffectView else {
220
+ return
221
+ }
222
+ UIView.animate(
223
+ withDuration: 0.25,
224
+ delay: 0,
225
+ options: [.curveEaseIn],
226
+ animations: {
227
+ blurEffectView.alpha = 0
228
+ },
229
+ completion: { _ in
230
+ blurEffectView.removeFromSuperview()
231
+ self.blurEffectView = nil
232
+ }
233
+ )
234
+ }
72
235
  }
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "host.exp.exponent",
5
5
  "module": "expo.modules.screencapture",
6
- "version": "7.2.0-canary-20250709-136b77f",
6
+ "version": "7.2.0-canary-20250722-599a28f",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -24,8 +24,8 @@
24
24
  },
25
25
  "files": [
26
26
  {
27
- "name": "expo.modules.screencapture-7.2.0-canary-20250709-136b77f.aar",
28
- "url": "expo.modules.screencapture-7.2.0-canary-20250709-136b77f.aar",
27
+ "name": "expo.modules.screencapture-7.2.0-canary-20250722-599a28f.aar",
28
+ "url": "expo.modules.screencapture-7.2.0-canary-20250722-599a28f.aar",
29
29
  "size": 28245,
30
30
  "sha512": "5d693f6a83cc09074f42ae55d49f7cccfc0c2e1f024e43d866c285e18f0e37648ff966eb7507567538355f6d9f1b6c7d51f6eb45456dbe191ad41f3ae896f930",
31
31
  "sha256": "8a9e3dca21a096209a62e8ce26c3733e57135f9d59a41eb237636ae81c744345",
@@ -60,8 +60,8 @@
60
60
  ],
61
61
  "files": [
62
62
  {
63
- "name": "expo.modules.screencapture-7.2.0-canary-20250709-136b77f.aar",
64
- "url": "expo.modules.screencapture-7.2.0-canary-20250709-136b77f.aar",
63
+ "name": "expo.modules.screencapture-7.2.0-canary-20250722-599a28f.aar",
64
+ "url": "expo.modules.screencapture-7.2.0-canary-20250722-599a28f.aar",
65
65
  "size": 28245,
66
66
  "sha512": "5d693f6a83cc09074f42ae55d49f7cccfc0c2e1f024e43d866c285e18f0e37648ff966eb7507567538355f6d9f1b6c7d51f6eb45456dbe191ad41f3ae896f930",
67
67
  "sha256": "8a9e3dca21a096209a62e8ce26c3733e57135f9d59a41eb237636ae81c744345",
@@ -80,8 +80,8 @@
80
80
  },
81
81
  "files": [
82
82
  {
83
- "name": "expo.modules.screencapture-7.2.0-canary-20250709-136b77f-sources.jar",
84
- "url": "expo.modules.screencapture-7.2.0-canary-20250709-136b77f-sources.jar",
83
+ "name": "expo.modules.screencapture-7.2.0-canary-20250722-599a28f-sources.jar",
84
+ "url": "expo.modules.screencapture-7.2.0-canary-20250722-599a28f-sources.jar",
85
85
  "size": 3601,
86
86
  "sha512": "080676e6818f1d063d27b7b460c49e34d7ebeae5e6a6e4c071840008b10a6beaafe0022467b6f556a1a190b22ae6b04ef87de7e7c2a74364b5af845efa1a6b05",
87
87
  "sha256": "d2c5b19e0bb1ad883b35525cdce5c665213505bea5b758f0c0c44b735fc960eb",
@@ -0,0 +1 @@
1
+ 358690e39ee0274e1da5c3b40e7da761463d6d599e91837a7bd25246105dcbc989de64d2d7f0433f968f03815051a77b407a1514758cfe4a43e774ac0270c262
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>host.exp.exponent</groupId>
11
11
  <artifactId>expo.modules.screencapture</artifactId>
12
- <version>7.2.0-canary-20250709-136b77f</version>
12
+ <version>7.2.0-canary-20250722-599a28f</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.screencapture</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -0,0 +1 @@
1
+ deaee5bbac4268d967772e3ab52e83e4f23980a0b33897e89e5835f8744be88fe7501c34fc46b4009a1713f2a601b37d54167a060972ec2fc44413b45cc9f808
@@ -3,11 +3,11 @@
3
3
  <groupId>host.exp.exponent</groupId>
4
4
  <artifactId>expo.modules.screencapture</artifactId>
5
5
  <versioning>
6
- <latest>7.2.0-canary-20250709-136b77f</latest>
7
- <release>7.2.0-canary-20250709-136b77f</release>
6
+ <latest>7.2.0-canary-20250722-599a28f</latest>
7
+ <release>7.2.0-canary-20250722-599a28f</release>
8
8
  <versions>
9
- <version>7.2.0-canary-20250709-136b77f</version>
9
+ <version>7.2.0-canary-20250722-599a28f</version>
10
10
  </versions>
11
- <lastUpdated>20250709123207</lastUpdated>
11
+ <lastUpdated>20250722134452</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 27fb1b034898ccc1c9a9563740be7f07
1
+ 0bf60de34206bb4c83f26febbd46864d
@@ -1 +1 @@
1
- 450b60b32f5ade042ef6c4e32879c142b1e75982
1
+ 6f033e68e618c827836a01e287c066dde3430018
@@ -1 +1 @@
1
- d522a1dcdaea46d41f54b00a414171f193f41b0f1b807160a835aba4f3773e4d
1
+ 476fae8c495282b12a6abf678e7e4028d0d50b3edf016724fa306fe64863a946
@@ -1 +1 @@
1
- 0b5498a4b41234692c3dcc29aa6be0540f9fa46be16d69ca90bc8ca8e936159363d964df46f8623ddf0859d4533ccefb1800e76cd06316ea33f597bd32b57d59
1
+ dbb573146266309135287c92ab9b026c96af4c66857996a4bfdf7bbc3841ba74cd74a0e60abc6de7d8ead85b38f61513bd201c2a4e1a77c6fd087612c36d7baf
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-screen-capture",
3
- "version": "7.2.0-canary-20250709-136b77f",
3
+ "version": "7.2.0-canary-20250722-599a28f",
4
4
  "description": "Protects screens in your app from being captured or recorded, and notifies if a screenshot is taken.",
5
5
  "main": "build/ScreenCapture.js",
6
6
  "types": "build/ScreenCapture.d.ts",
@@ -35,10 +35,10 @@
35
35
  "dependencies": {},
36
36
  "devDependencies": {
37
37
  "@testing-library/react-native": "^13.1.0",
38
- "expo-module-scripts": "4.1.10-canary-20250709-136b77f"
38
+ "expo-module-scripts": "4.1.10-canary-20250722-599a28f"
39
39
  },
40
40
  "peerDependencies": {
41
- "expo": "54.0.0-canary-20250709-136b77f",
41
+ "expo": "54.0.0-canary-20250722-599a28f",
42
42
  "react": "*"
43
43
  }
44
44
  }
@@ -30,7 +30,7 @@ export async function isAvailableAsync(): Promise<boolean> {
30
30
  * Prevents screenshots and screen recordings until `allowScreenCaptureAsync` is called or the app is restarted. If you are
31
31
  * already preventing screen capture, this method does nothing (unless you pass a new and unique `key`).
32
32
  *
33
- * > On iOS, this will only prevent screen recordings, and is only available on iOS 11 and newer. On older
33
+ * > On iOS, this prevents screen recordings and screenshots, and is only available on iOS 11+ (recordings) and iOS 13+ (screenshots). On older
34
34
  * iOS versions, this method does nothing.
35
35
  *
36
36
  * @param key Optional. If provided, this will help prevent multiple instances of the `preventScreenCaptureAsync`
@@ -38,7 +38,7 @@ export async function isAvailableAsync(): Promise<boolean> {
38
38
  * When using multiple keys, you'll have to re-allow each one in order to re-enable screen capturing.
39
39
  *
40
40
  * @platform android
41
- * @platform ios 11+
41
+ * @platform ios
42
42
  */
43
43
  export async function preventScreenCaptureAsync(key: string = 'default'): Promise<void> {
44
44
  if (!ExpoScreenCapture.preventScreenCapture) {
@@ -91,6 +91,47 @@ export function usePreventScreenCapture(key: string = 'default'): void {
91
91
  }, [key]);
92
92
  }
93
93
 
94
+ // @needsAudit
95
+ /**
96
+ * Enables a privacy protection blur overlay that hides sensitive content when the app is not in focus.
97
+ * The overlay applies a customizable blur effect when the app is in the app switcher, background, or during interruptions
98
+ * (calls, Siri, Control Center, etc.), and automatically removes it when the app becomes active again.
99
+ *
100
+ * This provides visual privacy protection by preventing sensitive app content from being visible in:
101
+ * - App switcher previews
102
+ * - Background app snapshots
103
+ * - Screenshots taken during inactive states
104
+ *
105
+ * For Android, app switcher protection is automatically provided by `preventScreenCaptureAsync()`
106
+ * using the FLAG_SECURE window flag, which shows a blank screen in the recent apps preview.
107
+ *
108
+ * @param blurIntensity The intensity of the blur effect, from 0.0 (no blur) to 1.0 (maximum blur). Default is 0.5.
109
+ *
110
+ * @platform ios
111
+ *
112
+ */
113
+ export async function enableAppSwitcherProtectionAsync(blurIntensity: number = 0.5): Promise<void> {
114
+ if (!ExpoScreenCapture.enableAppSwitcherProtection) {
115
+ throw new UnavailabilityError('ScreenCapture', 'enableAppSwitcherProtectionAsync');
116
+ }
117
+
118
+ await ExpoScreenCapture.enableAppSwitcherProtection(blurIntensity);
119
+ }
120
+
121
+ // @needsAudit
122
+ /**
123
+ * Disables the privacy protection overlay that was previously enabled with `enableAppSwitcherProtectionAsync`.
124
+ *
125
+ * @platform ios
126
+ */
127
+ export async function disableAppSwitcherProtectionAsync(): Promise<void> {
128
+ if (!ExpoScreenCapture.disableAppSwitcherProtection) {
129
+ throw new UnavailabilityError('ScreenCapture', 'disableAppSwitcherProtectionAsync');
130
+ }
131
+
132
+ await ExpoScreenCapture.disableAppSwitcherProtection();
133
+ }
134
+
94
135
  // @needsAudit
95
136
  /**
96
137
  * Adds a listener that will fire whenever the user takes a screenshot while the app is foregrounded.
@@ -1 +0,0 @@
1
- 263b8c01c8c27de688f1162dca410830339977c676e21132fab1b04d5c783d956da7828ec6bf406a0c604c760794d0ed170ee5808cb90ccaf1e6976878e6b6ce
@@ -1 +0,0 @@
1
- 0c875ef54e1ead0ded3e9704353700a9211ccf2496ed456a231da4ac12893d5f76bede5b66f5dfd1e103aafdd34ef9eca6bf38fabc72d2c3a776b36d8268d22d