expo-sharing 9.1.2 → 10.0.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 +33 -0
- package/android/build.gradle +28 -15
- package/android/src/main/java/expo/modules/sharing/SharingFileProvider.kt +5 -0
- package/android/src/main/java/expo/modules/sharing/SharingModule.kt +126 -0
- package/android/src/main/java/expo/modules/sharing/SharingPackage.kt +9 -0
- package/build/ExpoSharing.d.ts +1 -1
- package/build/ExpoSharing.js +1 -1
- package/build/ExpoSharing.js.map +1 -1
- package/build/ExpoSharing.web.js +1 -1
- package/build/ExpoSharing.web.js.map +1 -1
- package/build/Sharing.d.ts +19 -0
- package/build/Sharing.js +12 -1
- package/build/Sharing.js.map +1 -1
- package/ios/EXSharing/EXSharingModule.h +3 -3
- package/ios/EXSharing/EXSharingModule.m +17 -17
- package/ios/EXSharing.podspec +3 -3
- package/package.json +5 -7
- package/src/ExpoSharing.ts +1 -1
- package/src/ExpoSharing.web.ts +1 -1
- package/src/Sharing.ts +23 -1
- package/android/src/main/java/expo/modules/sharing/SharingFileProvider.java +0 -5
- package/android/src/main/java/expo/modules/sharing/SharingModule.java +0 -154
- package/android/src/main/java/expo/modules/sharing/SharingPackage.java +0 -16
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,39 @@
|
|
|
8
8
|
|
|
9
9
|
### 🐛 Bug fixes
|
|
10
10
|
|
|
11
|
+
### 💡 Others
|
|
12
|
+
|
|
13
|
+
## 10.0.1 — 2021-10-01
|
|
14
|
+
|
|
15
|
+
_This version does not introduce any user-facing changes._
|
|
16
|
+
|
|
17
|
+
## 10.0.0 — 2021-09-28
|
|
18
|
+
|
|
19
|
+
### 🛠 Breaking changes
|
|
20
|
+
|
|
21
|
+
- Dropped support for iOS 11.0 ([#14383](https://github.com/expo/expo/pull/14383) by [@cruzach](https://github.com/cruzach))
|
|
22
|
+
|
|
23
|
+
### 🐛 Bug fixes
|
|
24
|
+
|
|
25
|
+
- Fix building errors from use_frameworks! in Podfile. ([#14523](https://github.com/expo/expo/pull/14523) by [@kudo](https://github.com/kudo))
|
|
26
|
+
|
|
27
|
+
### 💡 Others
|
|
28
|
+
|
|
29
|
+
- Migrated from `@unimodules/core` to `expo-modules-core`. ([#13757](https://github.com/expo/expo/pull/13757) by [@tsapeta](https://github.com/tsapeta))
|
|
30
|
+
- Rewrote Android part from Java to Kotlin ([#14010](https://github.com/expo/expo/pull/14010) by [@m1st4ke](https://github.com/m1st4ke))
|
|
31
|
+
- Migrated from `AsyncTask` to Kotlin coroutines. ([#14029](https://github.com/expo/expo/pull/14029) by [@m1st4ke](https://github.com/m1st4ke))
|
|
32
|
+
|
|
33
|
+
## 9.2.0 — 2021-06-16
|
|
34
|
+
|
|
35
|
+
### 🐛 Bug fixes
|
|
36
|
+
|
|
37
|
+
- Enable kotlin in all modules. ([#12716](https://github.com/expo/expo/pull/12716) by [@wschurman](https://github.com/wschurman))
|
|
38
|
+
|
|
39
|
+
### 💡 Others
|
|
40
|
+
|
|
41
|
+
- Migrated from `unimodules-file-system-interface` to `expo-modules-core`.
|
|
42
|
+
- Build Android code using Java 8 to fix Android instrumented test build error. ([#12939](https://github.com/expo/expo/pull/12939) by [@kudo](https://github.com/kudo))
|
|
43
|
+
|
|
11
44
|
## 9.1.2 — 2021-04-13
|
|
12
45
|
|
|
13
46
|
_This version does not introduce any user-facing changes._
|
package/android/build.gradle
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
|
+
apply plugin: 'kotlin-android'
|
|
2
3
|
apply plugin: 'maven'
|
|
3
4
|
|
|
4
5
|
group = 'host.exp.exponent'
|
|
5
|
-
version = '
|
|
6
|
+
version = '10.0.1'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
buildscript {
|
|
9
|
+
// Simple helper that allows the root project to override versions declared by this library.
|
|
10
|
+
ext.safeExtGet = { prop, fallback ->
|
|
11
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
repositories {
|
|
15
|
+
mavenCentral()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
dependencies {
|
|
19
|
+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', '1.4.21')}")
|
|
20
|
+
}
|
|
10
21
|
}
|
|
11
22
|
|
|
12
23
|
// Upload android library to maven with javadoc and android sources
|
|
@@ -37,28 +48,30 @@ uploadArchives {
|
|
|
37
48
|
android {
|
|
38
49
|
compileSdkVersion safeExtGet("compileSdkVersion", 30)
|
|
39
50
|
|
|
51
|
+
compileOptions {
|
|
52
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
53
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
kotlinOptions {
|
|
57
|
+
jvmTarget = JavaVersion.VERSION_1_8
|
|
58
|
+
}
|
|
59
|
+
|
|
40
60
|
defaultConfig {
|
|
41
61
|
minSdkVersion safeExtGet("minSdkVersion", 21)
|
|
42
62
|
targetSdkVersion safeExtGet("targetSdkVersion", 30)
|
|
43
63
|
versionCode 16
|
|
44
|
-
versionName '
|
|
64
|
+
versionName '10.0.1'
|
|
45
65
|
}
|
|
46
66
|
lintOptions {
|
|
47
67
|
abortOnError false
|
|
48
68
|
}
|
|
49
69
|
}
|
|
50
70
|
|
|
51
|
-
if (new File(rootProject.projectDir.parentFile, 'package.json').exists()) {
|
|
52
|
-
apply from: project(":unimodules-core").file("../unimodules-core.gradle")
|
|
53
|
-
} else {
|
|
54
|
-
throw new GradleException(
|
|
55
|
-
"'unimodules-core.gradle' was not found in the usual React Native dependency location. " +
|
|
56
|
-
"This package can only be used in such projects. Are you sure you've installed the dependencies properly?")
|
|
57
|
-
}
|
|
58
|
-
|
|
59
71
|
dependencies {
|
|
60
|
-
|
|
61
|
-
unimodule 'unimodules-file-system-interface'
|
|
72
|
+
implementation project(':expo-modules-core')
|
|
62
73
|
|
|
63
74
|
api "androidx.legacy:legacy-support-v4:1.0.0"
|
|
75
|
+
|
|
76
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${safeExtGet('kotlinVersion', '1.4.21')}"
|
|
64
77
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
package expo.modules.sharing
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.Intent
|
|
6
|
+
import android.content.pm.PackageManager
|
|
7
|
+
import android.net.Uri
|
|
8
|
+
import android.os.Bundle
|
|
9
|
+
import androidx.core.content.FileProvider
|
|
10
|
+
import expo.modules.core.ExportedModule
|
|
11
|
+
import expo.modules.core.ModuleRegistry
|
|
12
|
+
import expo.modules.core.ModuleRegistryDelegate
|
|
13
|
+
import expo.modules.core.Promise
|
|
14
|
+
import expo.modules.core.arguments.ReadableArguments
|
|
15
|
+
import expo.modules.core.errors.InvalidArgumentException
|
|
16
|
+
import expo.modules.core.interfaces.ActivityEventListener
|
|
17
|
+
import expo.modules.core.interfaces.ActivityProvider
|
|
18
|
+
import expo.modules.core.interfaces.ExpoMethod
|
|
19
|
+
import expo.modules.core.interfaces.services.UIManager
|
|
20
|
+
import expo.modules.interfaces.filesystem.FilePermissionModuleInterface
|
|
21
|
+
import expo.modules.interfaces.filesystem.Permission
|
|
22
|
+
import java.io.File
|
|
23
|
+
import java.net.URLConnection
|
|
24
|
+
|
|
25
|
+
class SharingModule(
|
|
26
|
+
context: Context,
|
|
27
|
+
private val moduleRegistryDelegate: ModuleRegistryDelegate = ModuleRegistryDelegate()
|
|
28
|
+
) : ExportedModule(context), ActivityEventListener {
|
|
29
|
+
private var pendingPromise: Promise? = null
|
|
30
|
+
private val uiManager: UIManager by moduleRegistry()
|
|
31
|
+
override fun getName() = "ExpoSharing"
|
|
32
|
+
|
|
33
|
+
private inline fun <reified T> moduleRegistry() =
|
|
34
|
+
moduleRegistryDelegate.getFromModuleRegistry<T>()
|
|
35
|
+
|
|
36
|
+
override fun onCreate(moduleRegistry: ModuleRegistry) {
|
|
37
|
+
moduleRegistryDelegate.onCreate(moduleRegistry)
|
|
38
|
+
uiManager.registerActivityEventListener(this)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override fun onDestroy() {
|
|
42
|
+
uiManager.unregisterActivityEventListener(this)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@ExpoMethod
|
|
46
|
+
fun shareAsync(url: String?, params: ReadableArguments, promise: Promise) {
|
|
47
|
+
if (pendingPromise != null) {
|
|
48
|
+
promise.reject("ERR_SHARING_MUL", "Another share request is being processed now.")
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
val fileToShare = getLocalFileFoUrl(url)
|
|
53
|
+
val contentUri = FileProvider.getUriForFile(
|
|
54
|
+
context,
|
|
55
|
+
context.applicationInfo.packageName + ".SharingFileProvider",
|
|
56
|
+
fileToShare
|
|
57
|
+
)
|
|
58
|
+
val mimeType = params.getString(MIME_TYPE_OPTIONS_KEY)
|
|
59
|
+
?: URLConnection.guessContentTypeFromName(fileToShare.name)
|
|
60
|
+
?: "*/*"
|
|
61
|
+
val intent = Intent.createChooser(
|
|
62
|
+
createSharingIntent(contentUri, mimeType),
|
|
63
|
+
params.getString(DIALOG_TITLE_OPTIONS_KEY)
|
|
64
|
+
)
|
|
65
|
+
val resInfoList = context.packageManager.queryIntentActivities(
|
|
66
|
+
intent,
|
|
67
|
+
PackageManager.MATCH_DEFAULT_ONLY
|
|
68
|
+
)
|
|
69
|
+
resInfoList.forEach {
|
|
70
|
+
val packageName = it.activityInfo.packageName
|
|
71
|
+
context.grantUriPermission(packageName, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
72
|
+
}
|
|
73
|
+
val activityProvider: ActivityProvider by moduleRegistry()
|
|
74
|
+
activityProvider.currentActivity.startActivityForResult(intent, REQUEST_CODE)
|
|
75
|
+
pendingPromise = promise
|
|
76
|
+
} catch (e: InvalidArgumentException) {
|
|
77
|
+
promise.reject("ERR_SHARING_URL", e.message, e)
|
|
78
|
+
} catch (e: Exception) {
|
|
79
|
+
promise.reject("ERR_SHARING", "Failed to share the file: " + e.message, e)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@Throws(InvalidArgumentException::class)
|
|
84
|
+
private fun getLocalFileFoUrl(url: String?): File {
|
|
85
|
+
if (url == null) {
|
|
86
|
+
throw InvalidArgumentException("URL to share cannot be null.")
|
|
87
|
+
}
|
|
88
|
+
val uri = Uri.parse(url)
|
|
89
|
+
if ("file" != uri.scheme) {
|
|
90
|
+
throw InvalidArgumentException("Only local file URLs are supported (expected scheme to be 'file', got '" + uri.scheme + "'.")
|
|
91
|
+
}
|
|
92
|
+
val path = uri.path
|
|
93
|
+
?: throw InvalidArgumentException("Path component of the URL to share cannot be null.")
|
|
94
|
+
if (!isAllowedToRead(path)) {
|
|
95
|
+
throw InvalidArgumentException("Not allowed to read file under given URL.")
|
|
96
|
+
}
|
|
97
|
+
return File(path)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private fun isAllowedToRead(url: String?): Boolean {
|
|
101
|
+
val permissionModuleInterface: FilePermissionModuleInterface by moduleRegistry()
|
|
102
|
+
return permissionModuleInterface.getPathPermissions(context, url).contains(Permission.READ)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private fun createSharingIntent(uri: Uri, mimeType: String?) =
|
|
106
|
+
Intent(Intent.ACTION_SEND).apply {
|
|
107
|
+
putExtra(Intent.EXTRA_STREAM, uri)
|
|
108
|
+
setTypeAndNormalize(mimeType)
|
|
109
|
+
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
|
|
113
|
+
if (requestCode == REQUEST_CODE && pendingPromise != null) {
|
|
114
|
+
pendingPromise?.resolve(Bundle.EMPTY)
|
|
115
|
+
pendingPromise = null
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
override fun onNewIntent(intent: Intent) = Unit
|
|
120
|
+
|
|
121
|
+
companion object {
|
|
122
|
+
private const val REQUEST_CODE = 8524
|
|
123
|
+
private const val MIME_TYPE_OPTIONS_KEY = "mimeType"
|
|
124
|
+
private const val DIALOG_TITLE_OPTIONS_KEY = "dialogTitle"
|
|
125
|
+
}
|
|
126
|
+
}
|
package/build/ExpoSharing.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("
|
|
1
|
+
declare const _default: import("expo-modules-core").ProxyNativeModule;
|
|
2
2
|
export default _default;
|
package/build/ExpoSharing.js
CHANGED
package/build/ExpoSharing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoSharing.js","sourceRoot":"","sources":["../src/ExpoSharing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"ExpoSharing.js","sourceRoot":"","sources":["../src/ExpoSharing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,eAAe,kBAAkB,CAAC,WAAW,CAAC","sourcesContent":["import { NativeModulesProxy } from 'expo-modules-core';\n\nexport default NativeModulesProxy.ExpoSharing;\n"]}
|
package/build/ExpoSharing.web.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoSharing.web.js","sourceRoot":"","sources":["../src/ExpoSharing.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"ExpoSharing.web.js","sourceRoot":"","sources":["../src/ExpoSharing.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAIxD,eAAe;IACb,IAAI,IAAI;QACN,OAAO,aAAa,CAAC;IACvB,CAAC;IACD,KAAK,CAAC,gBAAgB;QACpB,IAAI,OAAO,SAAS,KAAK,WAAW,EAAE;YACpC,OAAO,KAAK,CAAC;SACd;QAED,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC;IAC3B,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,GAAW,EAAE,UAAwB,EAAE;QACtD,sDAAsD;QACtD,IAAI,SAAS,CAAC,KAAK,EAAE;YACnB,MAAM,SAAS,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;SAC5C;aAAM;YACL,MAAM,IAAI,mBAAmB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;SACrD;IACH,CAAC;CACF,CAAC","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\n\ntype ShareOptions = { title?: string; text?: string; url?: string };\n\nexport default {\n get name(): string {\n return 'ExpoSharing';\n },\n async isAvailableAsync(): Promise<boolean> {\n if (typeof navigator === 'undefined') {\n return false;\n }\n\n return !!navigator.share;\n },\n async shareAsync(url: string, options: ShareOptions = {}): Promise<void> {\n // NOTE: `navigator.share` is only available via HTTPS\n if (navigator.share) {\n await navigator.share({ ...options, url });\n } else {\n throw new UnavailabilityError('navigator', 'share');\n }\n },\n};\n"]}
|
package/build/Sharing.d.ts
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
export declare type SharingOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* Sets `mimeType` for `Intent` *(Android only)*
|
|
4
|
+
*/
|
|
2
5
|
mimeType?: string;
|
|
6
|
+
/**
|
|
7
|
+
* ([Uniform Type Identifier](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_conc/understand_utis_conc.html))
|
|
8
|
+
* the type of the target file *(iOS only)*
|
|
9
|
+
*/
|
|
3
10
|
UTI?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Sets share dialog title *(Android and Web only)*
|
|
13
|
+
*/
|
|
4
14
|
dialogTitle?: string;
|
|
5
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Determine if the sharing API can be used in this app.
|
|
18
|
+
* @return A promise that fulfills with `true` if the sharing API can be used, and `false` otherwise.
|
|
19
|
+
*/
|
|
6
20
|
export declare function isAvailableAsync(): Promise<boolean>;
|
|
21
|
+
/**
|
|
22
|
+
* Opens action sheet to share file to different applications which can handle this type of file.
|
|
23
|
+
* @param url Local file URL to share.
|
|
24
|
+
* @param options A map of share options.
|
|
25
|
+
*/
|
|
7
26
|
export declare function shareAsync(url: string, options?: SharingOptions): Promise<object>;
|
package/build/Sharing.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { UnavailabilityError } from '
|
|
1
|
+
import { UnavailabilityError } from 'expo-modules-core';
|
|
2
2
|
import Sharing from './ExpoSharing';
|
|
3
|
+
// @needsAudit
|
|
4
|
+
/**
|
|
5
|
+
* Determine if the sharing API can be used in this app.
|
|
6
|
+
* @return A promise that fulfills with `true` if the sharing API can be used, and `false` otherwise.
|
|
7
|
+
*/
|
|
3
8
|
export async function isAvailableAsync() {
|
|
4
9
|
if (Sharing) {
|
|
5
10
|
if (Sharing.isAvailableAsync) {
|
|
@@ -9,6 +14,12 @@ export async function isAvailableAsync() {
|
|
|
9
14
|
}
|
|
10
15
|
return false;
|
|
11
16
|
}
|
|
17
|
+
// @needsAudit
|
|
18
|
+
/**
|
|
19
|
+
* Opens action sheet to share file to different applications which can handle this type of file.
|
|
20
|
+
* @param url Local file URL to share.
|
|
21
|
+
* @param options A map of share options.
|
|
22
|
+
*/
|
|
12
23
|
export async function shareAsync(url, options = {}) {
|
|
13
24
|
if (!Sharing || !Sharing.shareAsync) {
|
|
14
25
|
throw new UnavailabilityError('Sharing', 'shareAsync');
|
package/build/Sharing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sharing.js","sourceRoot":"","sources":["../src/Sharing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"Sharing.js","sourceRoot":"","sources":["../src/Sharing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,OAAO,MAAM,eAAe,CAAC;AAmBpC,cAAc;AACd;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,OAAO,EAAE;QACX,IAAI,OAAO,CAAC,gBAAgB,EAAE;YAC5B,OAAO,MAAM,OAAO,CAAC,gBAAgB,EAAE,CAAC;SACzC;QACD,OAAO,IAAI,CAAC;KACb;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,cAAc;AACd;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,UAA0B,EAAE;IACxE,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;QACnC,MAAM,IAAI,mBAAmB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;KACxD;IACD,OAAO,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC","sourcesContent":["import { UnavailabilityError } from 'expo-modules-core';\n\nimport Sharing from './ExpoSharing';\n\n// @needsAudit\nexport type SharingOptions = {\n /**\n * Sets `mimeType` for `Intent` *(Android only)*\n */\n mimeType?: string;\n /**\n * ([Uniform Type Identifier](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_conc/understand_utis_conc.html))\n * the type of the target file *(iOS only)*\n */\n UTI?: string;\n /**\n * Sets share dialog title *(Android and Web only)*\n */\n dialogTitle?: string;\n};\n\n// @needsAudit\n/**\n * Determine if the sharing API can be used in this app.\n * @return A promise that fulfills with `true` if the sharing API can be used, and `false` otherwise.\n */\nexport async function isAvailableAsync(): Promise<boolean> {\n if (Sharing) {\n if (Sharing.isAvailableAsync) {\n return await Sharing.isAvailableAsync();\n }\n return true;\n }\n\n return false;\n}\n\n// @needsAudit\n/**\n * Opens action sheet to share file to different applications which can handle this type of file.\n * @param url Local file URL to share.\n * @param options A map of share options.\n */\nexport async function shareAsync(url: string, options: SharingOptions = {}): Promise<object> {\n if (!Sharing || !Sharing.shareAsync) {\n throw new UnavailabilityError('Sharing', 'shareAsync');\n }\n return await Sharing.shareAsync(url, options);\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Copyright © 2018 650 Industries. All rights reserved.
|
|
2
2
|
|
|
3
|
-
#import <
|
|
4
|
-
#import <
|
|
3
|
+
#import <ExpoModulesCore/EXExportedModule.h>
|
|
4
|
+
#import <ExpoModulesCore/EXModuleRegistryConsumer.h>
|
|
5
5
|
#import <UIKit/UIKit.h>
|
|
6
6
|
|
|
7
|
-
@interface EXSharingModule :
|
|
7
|
+
@interface EXSharingModule : EXExportedModule <EXModuleRegistryConsumer, UIDocumentInteractionControllerDelegate>
|
|
8
8
|
@end
|
|
@@ -1,46 +1,46 @@
|
|
|
1
1
|
// Copyright 2018-present 650 Industries. All rights reserved.
|
|
2
2
|
|
|
3
3
|
#import <EXSharing/EXSharingModule.h>
|
|
4
|
-
#import <
|
|
5
|
-
#import <
|
|
6
|
-
#import <
|
|
4
|
+
#import <ExpoModulesCore/EXUtilitiesInterface.h>
|
|
5
|
+
#import <ExpoModulesCore/EXFileSystemInterface.h>
|
|
6
|
+
#import <ExpoModulesCore/EXFilePermissionModuleInterface.h>
|
|
7
7
|
|
|
8
8
|
@interface EXSharingModule ()
|
|
9
9
|
|
|
10
|
-
@property (nonatomic, weak)
|
|
10
|
+
@property (nonatomic, weak) EXModuleRegistry *moduleRegistry;
|
|
11
11
|
@property (nonatomic, strong) UIDocumentInteractionController *documentInteractionController;
|
|
12
12
|
|
|
13
|
-
@property (nonatomic, strong)
|
|
13
|
+
@property (nonatomic, strong) EXPromiseResolveBlock pendingResolver;
|
|
14
14
|
|
|
15
15
|
@end
|
|
16
16
|
|
|
17
17
|
@implementation EXSharingModule
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
EX_EXPORT_MODULE(ExpoSharing);
|
|
20
20
|
|
|
21
|
-
- (void)setModuleRegistry:(
|
|
21
|
+
- (void)setModuleRegistry:(EXModuleRegistry *)moduleRegistry
|
|
22
22
|
{
|
|
23
23
|
_moduleRegistry = moduleRegistry;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
EX_EXPORT_METHOD_AS(shareAsync,
|
|
27
27
|
fileUrl:(NSString *)fileUrl
|
|
28
28
|
params:(NSDictionary *)params
|
|
29
|
-
resolve:(
|
|
30
|
-
reject:(
|
|
29
|
+
resolve:(EXPromiseResolveBlock)resolve
|
|
30
|
+
reject:(EXPromiseRejectBlock)reject)
|
|
31
31
|
{
|
|
32
32
|
if (_documentInteractionController) {
|
|
33
33
|
NSString *errorMessage = @"Another item is being shared. Await the `shareAsync` request and then share the item again.";
|
|
34
|
-
reject(@"E_SHARING_MUL", errorMessage,
|
|
34
|
+
reject(@"E_SHARING_MUL", errorMessage, EXErrorWithMessage(errorMessage));
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
NSURL *url = [NSURL URLWithString:fileUrl];
|
|
39
39
|
|
|
40
|
-
id<
|
|
41
|
-
if (filePermissionsModule && !([filePermissionsModule getPathPermissions:url.path] &
|
|
40
|
+
id<EXFilePermissionModuleInterface> filePermissionsModule = [_moduleRegistry getModuleImplementingProtocol:@protocol(EXFilePermissionModuleInterface)];
|
|
41
|
+
if (filePermissionsModule && !([filePermissionsModule getPathPermissions:url.path] & EXFileSystemPermissionRead)) {
|
|
42
42
|
NSString *errorMessage = @"You don't have access to provided file.";
|
|
43
|
-
reject(@"E_SHARING_PERM", errorMessage,
|
|
43
|
+
reject(@"E_SHARING_PERM", errorMessage, EXErrorWithMessage(errorMessage));
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -48,11 +48,11 @@ UM_EXPORT_METHOD_AS(shareAsync,
|
|
|
48
48
|
_documentInteractionController.delegate = self;
|
|
49
49
|
_documentInteractionController.UTI = params[@"UTI"];
|
|
50
50
|
|
|
51
|
-
UIViewController *viewController = [[_moduleRegistry getModuleImplementingProtocol:@protocol(
|
|
51
|
+
UIViewController *viewController = [[_moduleRegistry getModuleImplementingProtocol:@protocol(EXUtilitiesInterface)] currentViewController];
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
EX_WEAKIFY(self);
|
|
54
54
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
55
|
-
|
|
55
|
+
EX_ENSURE_STRONGIFY(self);
|
|
56
56
|
UIView *rootView = [viewController view];
|
|
57
57
|
if ([self.documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:rootView animated:YES]) {
|
|
58
58
|
self.pendingResolver = resolve;
|
package/ios/EXSharing.podspec
CHANGED
|
@@ -10,11 +10,11 @@ Pod::Spec.new do |s|
|
|
|
10
10
|
s.license = package['license']
|
|
11
11
|
s.author = package['author']
|
|
12
12
|
s.homepage = package['homepage']
|
|
13
|
-
s.platform = :ios, '
|
|
13
|
+
s.platform = :ios, '12.0'
|
|
14
14
|
s.source = { git: 'https://github.com/expo/expo.git' }
|
|
15
|
+
s.static_framework = true
|
|
15
16
|
|
|
16
|
-
s.dependency '
|
|
17
|
-
s.dependency 'UMFileSystemInterface'
|
|
17
|
+
s.dependency 'ExpoModulesCore'
|
|
18
18
|
|
|
19
19
|
if !$ExpoUseSources&.include?(package['name']) && ENV['EXPO_USE_SOURCE'].to_i == 0 && File.exist?("#{s.name}.xcframework") && Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.10.0')
|
|
20
20
|
s.source_files = "#{s.name}/**/*.h"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-sharing",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.1",
|
|
4
4
|
"description": "ExpoSharing standalone module",
|
|
5
5
|
"main": "build/Sharing.js",
|
|
6
6
|
"types": "build/Sharing.d.ts",
|
|
@@ -29,14 +29,12 @@
|
|
|
29
29
|
},
|
|
30
30
|
"author": "650 Industries, Inc.",
|
|
31
31
|
"license": "MIT",
|
|
32
|
-
"homepage": "https://docs.expo.
|
|
33
|
-
"dependencies": {
|
|
34
|
-
|
|
35
|
-
"@unimodules/core": "*",
|
|
36
|
-
"unimodules-file-system-interface": "*"
|
|
32
|
+
"homepage": "https://docs.expo.dev/versions/latest/sdk/sharing/",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"expo-modules-core": "~0.4.2"
|
|
37
35
|
},
|
|
38
36
|
"devDependencies": {
|
|
39
37
|
"expo-module-scripts": "^2.0.0"
|
|
40
38
|
},
|
|
41
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "2718b696f4a6919905b0f47ebb24ff65b42d8ff9"
|
|
42
40
|
}
|
package/src/ExpoSharing.ts
CHANGED
package/src/ExpoSharing.web.ts
CHANGED
package/src/Sharing.ts
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
|
-
import { UnavailabilityError } from '
|
|
1
|
+
import { UnavailabilityError } from 'expo-modules-core';
|
|
2
2
|
|
|
3
3
|
import Sharing from './ExpoSharing';
|
|
4
4
|
|
|
5
|
+
// @needsAudit
|
|
5
6
|
export type SharingOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Sets `mimeType` for `Intent` *(Android only)*
|
|
9
|
+
*/
|
|
6
10
|
mimeType?: string;
|
|
11
|
+
/**
|
|
12
|
+
* ([Uniform Type Identifier](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_conc/understand_utis_conc.html))
|
|
13
|
+
* the type of the target file *(iOS only)*
|
|
14
|
+
*/
|
|
7
15
|
UTI?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Sets share dialog title *(Android and Web only)*
|
|
18
|
+
*/
|
|
8
19
|
dialogTitle?: string;
|
|
9
20
|
};
|
|
10
21
|
|
|
22
|
+
// @needsAudit
|
|
23
|
+
/**
|
|
24
|
+
* Determine if the sharing API can be used in this app.
|
|
25
|
+
* @return A promise that fulfills with `true` if the sharing API can be used, and `false` otherwise.
|
|
26
|
+
*/
|
|
11
27
|
export async function isAvailableAsync(): Promise<boolean> {
|
|
12
28
|
if (Sharing) {
|
|
13
29
|
if (Sharing.isAvailableAsync) {
|
|
@@ -19,6 +35,12 @@ export async function isAvailableAsync(): Promise<boolean> {
|
|
|
19
35
|
return false;
|
|
20
36
|
}
|
|
21
37
|
|
|
38
|
+
// @needsAudit
|
|
39
|
+
/**
|
|
40
|
+
* Opens action sheet to share file to different applications which can handle this type of file.
|
|
41
|
+
* @param url Local file URL to share.
|
|
42
|
+
* @param options A map of share options.
|
|
43
|
+
*/
|
|
22
44
|
export async function shareAsync(url: string, options: SharingOptions = {}): Promise<object> {
|
|
23
45
|
if (!Sharing || !Sharing.shareAsync) {
|
|
24
46
|
throw new UnavailabilityError('Sharing', 'shareAsync');
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
package expo.modules.sharing;
|
|
2
|
-
|
|
3
|
-
import android.app.Activity;
|
|
4
|
-
import android.content.Context;
|
|
5
|
-
import android.content.Intent;
|
|
6
|
-
import android.content.pm.PackageManager;
|
|
7
|
-
import android.content.pm.ResolveInfo;
|
|
8
|
-
import android.net.Uri;
|
|
9
|
-
import android.os.Bundle;
|
|
10
|
-
import androidx.core.content.FileProvider;
|
|
11
|
-
|
|
12
|
-
import org.unimodules.core.ExportedModule;
|
|
13
|
-
import org.unimodules.core.ModuleRegistry;
|
|
14
|
-
import org.unimodules.core.Promise;
|
|
15
|
-
import org.unimodules.core.arguments.ReadableArguments;
|
|
16
|
-
import org.unimodules.core.errors.InvalidArgumentException;
|
|
17
|
-
import org.unimodules.core.interfaces.ActivityEventListener;
|
|
18
|
-
import org.unimodules.core.interfaces.ActivityProvider;
|
|
19
|
-
import org.unimodules.core.interfaces.ExpoMethod;
|
|
20
|
-
import org.unimodules.core.interfaces.services.UIManager;
|
|
21
|
-
import org.unimodules.interfaces.filesystem.FilePermissionModuleInterface;
|
|
22
|
-
import org.unimodules.interfaces.filesystem.Permission;
|
|
23
|
-
|
|
24
|
-
import java.io.File;
|
|
25
|
-
import java.net.URLConnection;
|
|
26
|
-
import java.util.List;
|
|
27
|
-
|
|
28
|
-
public class SharingModule extends ExportedModule implements ActivityEventListener {
|
|
29
|
-
private static final int REQUEST_CODE = 8524;
|
|
30
|
-
private static final String TAG = "ExpoSharing";
|
|
31
|
-
private static final String MIME_TYPE_OPTIONS_KEY = "mimeType";
|
|
32
|
-
private static final String DIALOG_TITLE_OPTIONS_KEY = "dialogTitle";
|
|
33
|
-
|
|
34
|
-
private ModuleRegistry mModuleRegistry;
|
|
35
|
-
private Context mContext;
|
|
36
|
-
private Promise mPendingPromise;
|
|
37
|
-
|
|
38
|
-
public SharingModule(Context context) {
|
|
39
|
-
super(context);
|
|
40
|
-
mContext = context;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
@Override
|
|
44
|
-
public String getName() {
|
|
45
|
-
return TAG;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@Override
|
|
49
|
-
public void onCreate(ModuleRegistry moduleRegistry) {
|
|
50
|
-
mModuleRegistry = moduleRegistry;
|
|
51
|
-
UIManager uiManager = mModuleRegistry.getModule(UIManager.class);
|
|
52
|
-
uiManager.registerActivityEventListener(this);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
@Override
|
|
56
|
-
public void onDestroy() {
|
|
57
|
-
UIManager uiManager = mModuleRegistry.getModule(UIManager.class);
|
|
58
|
-
uiManager.unregisterActivityEventListener(this);
|
|
59
|
-
|
|
60
|
-
mModuleRegistry = null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
@ExpoMethod
|
|
64
|
-
public void shareAsync(String url, ReadableArguments params, final Promise promise) {
|
|
65
|
-
if (mPendingPromise != null) {
|
|
66
|
-
promise.reject("ERR_SHARING_MUL", "Another share request is being processed now.");
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
File fileToShare = getLocalFileFoUrl(url);
|
|
72
|
-
Uri contentUri = FileProvider.getUriForFile(mContext, mContext.getApplicationInfo().packageName + ".SharingFileProvider", fileToShare);
|
|
73
|
-
|
|
74
|
-
String mimeType = params.getString(MIME_TYPE_OPTIONS_KEY);
|
|
75
|
-
if (mimeType == null) {
|
|
76
|
-
String guessedMimeType = URLConnection.guessContentTypeFromName(fileToShare.getName());
|
|
77
|
-
if (guessedMimeType != null) {
|
|
78
|
-
mimeType = guessedMimeType;
|
|
79
|
-
} else {
|
|
80
|
-
mimeType = "*/*";
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
Intent intent = Intent.createChooser(createSharingIntent(contentUri, mimeType), params.getString(DIALOG_TITLE_OPTIONS_KEY));
|
|
85
|
-
|
|
86
|
-
List<ResolveInfo> resInfoList = mContext.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
|
87
|
-
|
|
88
|
-
for (ResolveInfo resolveInfo : resInfoList) {
|
|
89
|
-
String packageName = resolveInfo.activityInfo.packageName;
|
|
90
|
-
mContext.grantUriPermission(packageName, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
mModuleRegistry.getModule(ActivityProvider.class).getCurrentActivity().startActivityForResult(intent, REQUEST_CODE);
|
|
94
|
-
|
|
95
|
-
mPendingPromise = promise;
|
|
96
|
-
} catch (InvalidArgumentException e) {
|
|
97
|
-
promise.reject("ERR_SHARING_URL", e.getMessage(), e);
|
|
98
|
-
} catch (Exception e) {
|
|
99
|
-
promise.reject("ERR_SHARING", "Failed to share the file: " + e.getMessage(), e);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
private File getLocalFileFoUrl(String url) throws InvalidArgumentException {
|
|
104
|
-
if (url == null) {
|
|
105
|
-
throw new InvalidArgumentException("URL to share cannot be null.");
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
Uri uri = Uri.parse(url);
|
|
109
|
-
if (uri.getPath() == null) {
|
|
110
|
-
throw new InvalidArgumentException("Path component of the URL to share cannot be null.");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (!"file".equals(uri.getScheme())) {
|
|
114
|
-
throw new InvalidArgumentException("Only local file URLs are supported (expected scheme to be 'file', got '" + uri.getScheme() + "'.");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (!isAllowedToRead(uri.getPath())) {
|
|
118
|
-
throw new InvalidArgumentException("Not allowed to read file under given URL.");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return new File(uri.getPath());
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
private boolean isAllowedToRead(String url) {
|
|
125
|
-
if (mModuleRegistry != null) {
|
|
126
|
-
FilePermissionModuleInterface permissionModuleInterface = mModuleRegistry.getModule(FilePermissionModuleInterface.class);
|
|
127
|
-
if (permissionModuleInterface != null) {
|
|
128
|
-
return permissionModuleInterface.getPathPermissions(mContext, url).contains(Permission.READ);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
protected Intent createSharingIntent(Uri uri, String mimeType) {
|
|
135
|
-
Intent intent = new Intent(Intent.ACTION_SEND);
|
|
136
|
-
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
|
137
|
-
intent.setTypeAndNormalize(mimeType);
|
|
138
|
-
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
139
|
-
return intent;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
@Override
|
|
143
|
-
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
|
144
|
-
if (requestCode == REQUEST_CODE && mPendingPromise != null) {
|
|
145
|
-
mPendingPromise.resolve(Bundle.EMPTY);
|
|
146
|
-
mPendingPromise = null;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
@Override
|
|
151
|
-
public void onNewIntent(Intent intent) {
|
|
152
|
-
// do nothing
|
|
153
|
-
}
|
|
154
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
package expo.modules.sharing;
|
|
2
|
-
|
|
3
|
-
import android.content.Context;
|
|
4
|
-
|
|
5
|
-
import org.unimodules.core.BasePackage;
|
|
6
|
-
import org.unimodules.core.ExportedModule;
|
|
7
|
-
|
|
8
|
-
import java.util.Collections;
|
|
9
|
-
import java.util.List;
|
|
10
|
-
|
|
11
|
-
public class SharingPackage extends BasePackage {
|
|
12
|
-
@Override
|
|
13
|
-
public List<ExportedModule> createExportedModules(Context context) {
|
|
14
|
-
return Collections.<ExportedModule>singletonList(new SharingModule(context));
|
|
15
|
-
}
|
|
16
|
-
}
|