expo-screen-capture 5.7.0 → 5.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,18 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 5.8.1 — 2024-01-23
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fix screenshot listener not being called on Android 34. ([#26549](https://github.com/expo/expo/pull/26549) by [@alanjhughes](https://github.com/alanjhughes))
18
+
19
+ ## 5.8.0 — 2023-12-15
20
+
21
+ ### 🎉 New features
22
+
23
+ - Added `getPermissionsAsync` and `requestPermissionsAsync` methods. ([#25849](https://github.com/expo/expo/pull/25849) by [@behenate](https://github.com/behenate))
24
+
13
25
  ## 5.7.0 — 2023-11-14
14
26
 
15
27
  ### 🛠 Breaking changes
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '5.7.0'
6
+ version = '5.8.1'
7
7
 
8
8
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
9
9
  if (expoModulesCorePlugin.exists()) {
@@ -94,7 +94,7 @@ android {
94
94
  namespace "expo.modules.screencapture"
95
95
  defaultConfig {
96
96
  versionCode 7
97
- versionName '5.7.0'
97
+ versionName '5.8.1'
98
98
  }
99
99
  }
100
100
 
@@ -1,2 +1,5 @@
1
- <manifest>
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
3
+ <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
4
+ <uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" android:maxSdkVersion="34" />
2
5
  </manifest>
@@ -1,23 +1,58 @@
1
1
  package expo.modules.screencapture
2
2
 
3
+ import android.Manifest
4
+ import android.app.Activity
3
5
  import android.content.Context
6
+ import android.os.Build
4
7
  import android.view.WindowManager
8
+ import expo.modules.interfaces.permissions.Permissions
9
+ import expo.modules.kotlin.Promise
5
10
  import expo.modules.kotlin.exception.Exceptions
6
11
  import expo.modules.kotlin.functions.Queues
7
12
  import expo.modules.kotlin.modules.Module
8
13
  import expo.modules.kotlin.modules.ModuleDefinition
9
14
 
15
+ const val eventName = "onScreenshot"
16
+
10
17
  class ScreenCaptureModule : Module() {
11
18
  private val context: Context
12
19
  get() = appContext.reactContext ?: throw Exceptions.AppContextLost()
13
20
  private val currentActivity
14
21
  get() = appContext.currentActivity ?: throw Exceptions.MissingActivity()
22
+ private var screenCaptureCallback: Activity.ScreenCaptureCallback? = null
15
23
 
16
24
  override fun definition() = ModuleDefinition {
17
25
  Name("ExpoScreenCapture")
18
26
 
27
+ Events(eventName)
28
+
19
29
  OnCreate {
20
- ScreenshotEventEmitter(context, appContext.legacyModuleRegistry)
30
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
31
+ screenCaptureCallback = Activity.ScreenCaptureCallback {
32
+ sendEvent(eventName)
33
+ }
34
+ currentActivity.registerScreenCaptureCallback(currentActivity.mainExecutor, screenCaptureCallback!!)
35
+ } else {
36
+ ScreenshotEventEmitter(context) {
37
+ sendEvent(eventName)
38
+ }
39
+ }
40
+ }
41
+
42
+ AsyncFunction("getPermissionsAsync") { promise: Promise ->
43
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
44
+ Permissions.getPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.READ_MEDIA_IMAGES)
45
+ } else {
46
+ Permissions.getPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.READ_EXTERNAL_STORAGE)
47
+ }
48
+ }
49
+
50
+ AsyncFunction("requestPermissionsAsync") { promise: Promise ->
51
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
52
+ Permissions.askForPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.READ_MEDIA_IMAGES)
53
+ } else {
54
+ Permissions.askForPermissionsWithPermissionsManager(appContext.permissions, promise, Manifest.permission.READ_EXTERNAL_STORAGE)
55
+ }
21
56
  }
22
57
 
23
58
  AsyncFunction("preventScreenCapture") {
@@ -27,5 +62,13 @@ class ScreenCaptureModule : Module() {
27
62
  AsyncFunction("allowScreenCapture") {
28
63
  currentActivity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
29
64
  }.runOnQueue(Queues.MAIN)
65
+
66
+ OnDestroy {
67
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
68
+ screenCaptureCallback?.let {
69
+ currentActivity.unregisterScreenCaptureCallback(it)
70
+ }
71
+ }
72
+ }
30
73
  }
31
74
  }
@@ -5,44 +5,32 @@ import android.content.Context
5
5
  import android.content.pm.PackageManager
6
6
  import android.database.ContentObserver
7
7
  import android.net.Uri
8
- import android.os.Bundle
8
+ import android.os.Build
9
9
  import android.os.Handler
10
10
  import android.os.Looper
11
11
  import android.provider.MediaStore
12
12
  import android.util.Log
13
-
14
- import androidx.core.content.ContextCompat
15
13
  import androidx.annotation.Nullable
16
-
17
- import expo.modules.core.ModuleRegistry
14
+ import androidx.core.content.ContextCompat
18
15
  import expo.modules.core.interfaces.LifecycleEventListener
19
- import expo.modules.core.interfaces.services.EventEmitter
20
- import expo.modules.core.interfaces.services.UIManager
21
16
 
22
- import java.lang.Exception
23
-
24
- class ScreenshotEventEmitter(val context: Context, moduleRegistry: ModuleRegistry) : LifecycleEventListener {
25
- private val onScreenshotEventName: String = "onScreenshot"
17
+ class ScreenshotEventEmitter(val context: Context, onCapture: () -> Unit) : LifecycleEventListener {
26
18
  private var isListening: Boolean = true
27
- private var eventEmitter: EventEmitter
28
19
  private var previousPath: String = ""
29
20
 
30
21
  init {
31
- moduleRegistry.getModule(UIManager::class.java).registerLifecycleEventListener(this)
32
- eventEmitter = moduleRegistry.getModule(EventEmitter::class.java)
33
-
34
22
  val contentObserver: ContentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
35
23
  override fun onChange(selfChange: Boolean, uri: Uri?) {
36
24
  super.onChange(selfChange, uri)
37
25
  if (isListening) {
38
- if (!hasReadExternalStoragePermission(context)) {
26
+ if (!hasPermissions(context)) {
39
27
  Log.e("expo-screen-capture", "Could not listen for screenshots, do not have READ_EXTERNAL_STORAGE permission.")
40
28
  return
41
29
  }
42
30
  val path = getFilePathFromContentResolver(context, uri)
43
31
  if (path != null && isPathOfNewScreenshot(path)) {
44
32
  previousPath = path
45
- eventEmitter.emit(onScreenshotEventName, Bundle())
33
+ onCapture()
46
34
  }
47
35
  }
48
36
  }
@@ -62,7 +50,12 @@ class ScreenshotEventEmitter(val context: Context, moduleRegistry: ModuleRegistr
62
50
  // Do nothing
63
51
  }
64
52
 
65
- private fun hasReadExternalStoragePermission(context: Context): Boolean {
53
+ private fun hasPermissions(context: Context): Boolean {
54
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
55
+ return ContextCompat.checkSelfPermission(context, permission.DETECT_SCREEN_CAPTURE) == PackageManager.PERMISSION_GRANTED
56
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
57
+ return ContextCompat.checkSelfPermission(context, permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED
58
+ }
66
59
  return ContextCompat.checkSelfPermission(context, permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
67
60
  }
68
61
 
@@ -86,7 +79,8 @@ class ScreenshotEventEmitter(val context: Context, moduleRegistry: ModuleRegistr
86
79
  }
87
80
 
88
81
  private fun isPathOfNewScreenshot(path: String): Boolean {
89
- if (!path.lowercase().contains("screenshot")) {
82
+ // Ignore paths that are not screenshots and pending screenshots
83
+ if (!path.lowercase().contains("screenshot") || path.lowercase().contains(".pending")) {
90
84
  return false
91
85
  }
92
86
  // Cannot check that the onChange event is for an insert operation until API level 30
@@ -1,4 +1,4 @@
1
- import { Subscription } from 'expo-modules-core';
1
+ import { Subscription, PermissionResponse, PermissionStatus, PermissionHookOptions } from 'expo-modules-core';
2
2
  /**
3
3
  * Returns whether the Screen Capture API is available on the current device.
4
4
  *
@@ -68,5 +68,27 @@ export declare function addScreenshotListener(listener: () => void): Subscriptio
68
68
  * @param subscription Subscription returned by `addScreenshotListener`.
69
69
  */
70
70
  export declare function removeScreenshotListener(subscription: Subscription): void;
71
- export { Subscription };
71
+ /**
72
+ * Checks user's permissions for detecting when a screenshot is taken.
73
+ * > Only Android requires additional permissions to detect screenshots. On iOS devices, this method will always resolve to a `granted` permission response.
74
+ * @return A promise that resolves to a [PermissionResponse](#permissionresponse) object.
75
+ */
76
+ export declare function getPermissionsAsync(): Promise<PermissionResponse>;
77
+ /**
78
+ * Asks the user to grant permissions necessary for detecting when a screenshot is taken.
79
+ * > Only Android requires additional permissions to detect screenshots. On iOS devices, this method will always resolve to a `granted` permission response.
80
+ * @return A promise that resolves to a [PermissionResponse](#permissionresponse) object.
81
+ * */
82
+ export declare function requestPermissionsAsync(): Promise<PermissionResponse>;
83
+ /**
84
+ * Check or request permissions necessary for detecting when a screenshot is taken.
85
+ * This uses both [`requestPermissionsAsync`](#screencapturerequestpermissionsasync) and [`getPermissionsAsync`](#screencapturegetpermissionsasync) to interact with the permissions.
86
+ *
87
+ * @example
88
+ * ```js
89
+ * const [status, requestPermission] = ScreenCapture.useScreenCapturePermissions();
90
+ * ```
91
+ */
92
+ export declare const usePermissions: (options?: PermissionHookOptions<object> | undefined) => [PermissionResponse | null, () => Promise<PermissionResponse>, () => Promise<PermissionResponse>];
93
+ export { Subscription, PermissionResponse, PermissionStatus, PermissionHookOptions };
72
94
  //# sourceMappingURL=ScreenCapture.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ScreenCapture.d.ts","sourceRoot":"","sources":["../src/ScreenCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAWpF;;;;;GAKG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAEzD;AAGD;;;;;;;;;;;GAWG;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;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,YAAY,CAExE;AAGD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,YAAY,QAElE;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"ScreenCapture.d.ts","sourceRoot":"","sources":["../src/ScreenCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,YAAY,EAEZ,kBAAkB,EAClB,gBAAgB,EAEhB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAW3B;;;;;GAKG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAEzD;AAGD;;;;;;;;;;;GAWG;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;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,YAAY,CAExE;AAGD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,YAAY,QAElE;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,EAAE,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,CAAC"}
@@ -1,4 +1,4 @@
1
- import { EventEmitter, UnavailabilityError } from 'expo-modules-core';
1
+ import { EventEmitter, UnavailabilityError, PermissionStatus, createPermissionHook, } from 'expo-modules-core';
2
2
  import { useEffect } from 'react';
3
3
  import ExpoScreenCapture from './ExpoScreenCapture';
4
4
  const activeTags = new Set();
@@ -108,4 +108,46 @@ export function addScreenshotListener(listener) {
108
108
  export function removeScreenshotListener(subscription) {
109
109
  emitter.removeSubscription(subscription);
110
110
  }
111
+ /**
112
+ * Checks user's permissions for detecting when a screenshot is taken.
113
+ * > Only Android requires additional permissions to detect screenshots. On iOS devices, this method will always resolve to a `granted` permission response.
114
+ * @return A promise that resolves to a [PermissionResponse](#permissionresponse) object.
115
+ */
116
+ export async function getPermissionsAsync() {
117
+ if (ExpoScreenCapture.getPermissionsAsync) {
118
+ return ExpoScreenCapture.getPermissionsAsync();
119
+ }
120
+ return defaultPermissionsResponse;
121
+ }
122
+ /**
123
+ * Asks the user to grant permissions necessary for detecting when a screenshot is taken.
124
+ * > Only Android requires additional permissions to detect screenshots. On iOS devices, this method will always resolve to a `granted` permission response.
125
+ * @return A promise that resolves to a [PermissionResponse](#permissionresponse) object.
126
+ * */
127
+ export async function requestPermissionsAsync() {
128
+ if (ExpoScreenCapture.requestPermissionsAsync) {
129
+ return ExpoScreenCapture.requestPermissionsAsync();
130
+ }
131
+ return defaultPermissionsResponse;
132
+ }
133
+ /**
134
+ * Check or request permissions necessary for detecting when a screenshot is taken.
135
+ * This uses both [`requestPermissionsAsync`](#screencapturerequestpermissionsasync) and [`getPermissionsAsync`](#screencapturegetpermissionsasync) to interact with the permissions.
136
+ *
137
+ * @example
138
+ * ```js
139
+ * const [status, requestPermission] = ScreenCapture.useScreenCapturePermissions();
140
+ * ```
141
+ */
142
+ export const usePermissions = createPermissionHook({
143
+ getMethod: getPermissionsAsync,
144
+ requestMethod: requestPermissionsAsync,
145
+ });
146
+ const defaultPermissionsResponse = {
147
+ granted: true,
148
+ expires: 'never',
149
+ canAskAgain: true,
150
+ status: PermissionStatus.GRANTED,
151
+ };
152
+ export { PermissionStatus };
111
153
  //# sourceMappingURL=ScreenCapture.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ScreenCapture.js","sourceRoot":"","sources":["../src/ScreenCapture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAEpD,MAAM,UAAU,GAAgB,IAAI,GAAG,EAAE,CAAC;AAC1C,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,iBAAiB,CAAC,CAAC;AAEpD,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;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,MAAc,SAAS;IACrE,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,EAAE;QAC3C,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,2BAA2B,CAAC,CAAC;KAC7E;IAED,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QACxB,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpB,MAAM,iBAAiB,CAAC,oBAAoB,EAAE,CAAC;KAChD;AACH,CAAC;AAED,cAAc;AACd;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,MAAc,SAAS;IACnE,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,EAAE;QAC3C,MAAM,IAAI,mBAAmB,CAAC,eAAe,EAAE,yBAAyB,CAAC,CAAC;KAC3E;IAED,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,EAAE;QACzB,MAAM,iBAAiB,CAAC,kBAAkB,EAAE,CAAC;KAC9C;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;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAoB;IACxD,OAAO,OAAO,CAAC,WAAW,CAAO,qBAAqB,EAAE,QAAQ,CAAC,CAAC;AACpE,CAAC;AAED,cAAc;AACd;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,wBAAwB,CAAC,YAA0B;IACjE,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;AAC3C,CAAC","sourcesContent":["import { EventEmitter, Subscription, UnavailabilityError } from 'expo-modules-core';\nimport { useEffect } from 'react';\n\nimport ExpoScreenCapture from './ExpoScreenCapture';\n\nconst activeTags: Set<string> = new Set();\nconst emitter = new EventEmitter(ExpoScreenCapture);\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. Currently, this resolves to `true` on Android and iOS only.\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 * > Please note that on iOS, this will only prevent screen recordings, and is only available on\n * iOS 11 and newer. On older 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 * Defaults to `'default'`.\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. Defaults to 'default'.\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. Defaults to `'default'`.\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 * On Android, this method requires the `READ_EXTERNAL_STORAGE` permission. You can request this\n * with [`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): Subscription {\n return emitter.addListener<void>(onScreenshotEventName, listener);\n}\n\n// @needsAudit\n/**\n * Removes the subscription you provide, so that you are no longer listening for screenshots.\n *\n * If you prefer, you can also call `remove()` on that `Subscription` object, for example:\n *\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 *\n * @param subscription Subscription returned by `addScreenshotListener`.\n */\nexport function removeScreenshotListener(subscription: Subscription) {\n emitter.removeSubscription(subscription);\n}\n\nexport { Subscription };\n"]}
1
+ {"version":3,"file":"ScreenCapture.js","sourceRoot":"","sources":["../src/ScreenCapture.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n EventEmitter,\n Subscription,\n UnavailabilityError,\n PermissionResponse,\n PermissionStatus,\n createPermissionHook,\n PermissionHookOptions,\n} from 'expo-modules-core';\nimport { useEffect } from 'react';\n\nimport ExpoScreenCapture from './ExpoScreenCapture';\n\nconst activeTags: Set<string> = new Set();\nconst emitter = new EventEmitter(ExpoScreenCapture);\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. Currently, this resolves to `true` on Android and iOS only.\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 * > Please note that on iOS, this will only prevent screen recordings, and is only available on\n * iOS 11 and newer. On older 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 * Defaults to `'default'`.\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. Defaults to 'default'.\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. Defaults to `'default'`.\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 * On Android, this method requires the `READ_EXTERNAL_STORAGE` permission. You can request this\n * with [`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): Subscription {\n return emitter.addListener<void>(onScreenshotEventName, listener);\n}\n\n// @needsAudit\n/**\n * Removes the subscription you provide, so that you are no longer listening for screenshots.\n *\n * If you prefer, you can also call `remove()` on that `Subscription` object, for example:\n *\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 *\n * @param subscription Subscription returned by `addScreenshotListener`.\n */\nexport function removeScreenshotListener(subscription: Subscription) {\n emitter.removeSubscription(subscription);\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.useScreenCapturePermissions();\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 { Subscription, PermissionResponse, PermissionStatus, PermissionHookOptions };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-screen-capture",
3
- "version": "5.7.0",
3
+ "version": "5.8.1",
4
4
  "description": "ExpoScreenCapture standalone module",
5
5
  "main": "build/ScreenCapture.js",
6
6
  "types": "build/ScreenCapture.d.ts",
@@ -39,5 +39,5 @@
39
39
  "peerDependencies": {
40
40
  "expo": "*"
41
41
  },
42
- "gitHead": "3142a086578deffd8704a8f1b6f0f661527d836c"
42
+ "gitHead": "7fb94e3c0598d0e2d428184b16eec5ec67d80388"
43
43
  }
@@ -1,4 +1,12 @@
1
- import { EventEmitter, Subscription, UnavailabilityError } from 'expo-modules-core';
1
+ import {
2
+ EventEmitter,
3
+ Subscription,
4
+ UnavailabilityError,
5
+ PermissionResponse,
6
+ PermissionStatus,
7
+ createPermissionHook,
8
+ PermissionHookOptions,
9
+ } from 'expo-modules-core';
2
10
  import { useEffect } from 'react';
3
11
 
4
12
  import ExpoScreenCapture from './ExpoScreenCapture';
@@ -121,4 +129,49 @@ export function removeScreenshotListener(subscription: Subscription) {
121
129
  emitter.removeSubscription(subscription);
122
130
  }
123
131
 
124
- export { Subscription };
132
+ /**
133
+ * Checks user's permissions for detecting when a screenshot is taken.
134
+ * > Only Android requires additional permissions to detect screenshots. On iOS devices, this method will always resolve to a `granted` permission response.
135
+ * @return A promise that resolves to a [PermissionResponse](#permissionresponse) object.
136
+ */
137
+ export async function getPermissionsAsync(): Promise<PermissionResponse> {
138
+ if (ExpoScreenCapture.getPermissionsAsync) {
139
+ return ExpoScreenCapture.getPermissionsAsync();
140
+ }
141
+ return defaultPermissionsResponse;
142
+ }
143
+
144
+ /**
145
+ * Asks the user to grant permissions necessary for detecting when a screenshot is taken.
146
+ * > Only Android requires additional permissions to detect screenshots. On iOS devices, this method will always resolve to a `granted` permission response.
147
+ * @return A promise that resolves to a [PermissionResponse](#permissionresponse) object.
148
+ * */
149
+ export async function requestPermissionsAsync(): Promise<PermissionResponse> {
150
+ if (ExpoScreenCapture.requestPermissionsAsync) {
151
+ return ExpoScreenCapture.requestPermissionsAsync();
152
+ }
153
+ return defaultPermissionsResponse;
154
+ }
155
+
156
+ /**
157
+ * Check or request permissions necessary for detecting when a screenshot is taken.
158
+ * This uses both [`requestPermissionsAsync`](#screencapturerequestpermissionsasync) and [`getPermissionsAsync`](#screencapturegetpermissionsasync) to interact with the permissions.
159
+ *
160
+ * @example
161
+ * ```js
162
+ * const [status, requestPermission] = ScreenCapture.useScreenCapturePermissions();
163
+ * ```
164
+ */
165
+ export const usePermissions = createPermissionHook({
166
+ getMethod: getPermissionsAsync,
167
+ requestMethod: requestPermissionsAsync,
168
+ });
169
+
170
+ const defaultPermissionsResponse: PermissionResponse = {
171
+ granted: true,
172
+ expires: 'never',
173
+ canAskAgain: true,
174
+ status: PermissionStatus.GRANTED,
175
+ };
176
+
177
+ export { Subscription, PermissionResponse, PermissionStatus, PermissionHookOptions };