expo-sharing 11.0.0 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,18 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 11.1.0 — 2022-12-30
14
+
15
+ ### 🎉 New features
16
+
17
+ - Migrated Android implementation to Expo Modules API. ([#20112](https://github.com/expo/expo/pull/20112) by [@alanhughes](https://github.com/alanjhughes))
18
+
19
+ ## 11.0.1 — 2022-10-27
20
+
21
+ ### 🐛 Bug fixes
22
+
23
+ - On iOS, dismiss share sheet after sharing to an app is canceled, so the file doesn't fail to attach when trying to share again ([#19656](https://github.com/expo/expo/pull/19656) by [@keith-kurak](https://github.com/keith-kurak))
24
+
13
25
  ## 11.0.0 — 2022-10-25
14
26
 
15
27
  ### 🛠 Breaking changes
package/README.md CHANGED
@@ -4,7 +4,7 @@ Sharing standalone module
4
4
 
5
5
  # API documentation
6
6
 
7
- - [Documentation for the main branch](https://github.com/expo/expo/blob/main/docs/pages/versions/unversioned/sdk/sharing.md)
7
+ - [Documentation for the main branch](https://github.com/expo/expo/blob/main/docs/pages/versions/unversioned/sdk/sharing.mdx)
8
8
  - [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/sharing/)
9
9
 
10
10
  # Installation in managed Expo projects
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '11.0.0'
6
+ version = '11.1.0'
7
7
 
8
8
  buildscript {
9
9
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -74,7 +74,7 @@ android {
74
74
  minSdkVersion safeExtGet("minSdkVersion", 21)
75
75
  targetSdkVersion safeExtGet("targetSdkVersion", 31)
76
76
  versionCode 16
77
- versionName '11.0.0'
77
+ versionName '11.1.0'
78
78
  }
79
79
  lintOptions {
80
80
  abortOnError false
@@ -0,0 +1,15 @@
1
+ package expo.modules.sharing
2
+
3
+ import expo.modules.kotlin.exception.CodedException
4
+
5
+ internal class MissingCurrentActivityException :
6
+ CodedException("Activity which was provided during module initialization is no longer available")
7
+
8
+ internal class SharingInProgressException :
9
+ CodedException("Another share request is being processed now.")
10
+
11
+ internal class SharingFailedException(message: String, e: Exception) :
12
+ CodedException(message, e.cause)
13
+
14
+ internal class SharingInvalidArgsException(message: String?, e: Exception) :
15
+ CodedException(message, e.cause)
@@ -1,81 +1,69 @@
1
1
  package expo.modules.sharing
2
2
 
3
- import android.app.Activity
4
3
  import android.content.Context
5
4
  import android.content.Intent
6
5
  import android.content.pm.PackageManager
7
6
  import android.net.Uri
8
7
  import androidx.core.content.FileProvider
9
- import expo.modules.core.ExportedModule
10
- import expo.modules.core.ModuleRegistry
11
- import expo.modules.core.ModuleRegistryDelegate
12
- import expo.modules.core.Promise
13
- import expo.modules.core.arguments.ReadableArguments
14
8
  import expo.modules.core.errors.InvalidArgumentException
15
- import expo.modules.core.interfaces.ActivityEventListener
16
- import expo.modules.core.interfaces.ActivityProvider
17
- import expo.modules.core.interfaces.ExpoMethod
18
- import expo.modules.core.interfaces.services.UIManager
19
- import expo.modules.interfaces.filesystem.FilePermissionModuleInterface
20
9
  import expo.modules.interfaces.filesystem.Permission
10
+ import expo.modules.kotlin.Promise
11
+ import expo.modules.kotlin.exception.Exceptions
12
+ import expo.modules.kotlin.modules.Module
13
+ import expo.modules.kotlin.modules.ModuleDefinition
21
14
  import java.io.File
22
15
  import java.net.URLConnection
23
16
 
24
- class SharingModule(
25
- context: Context,
26
- private val moduleRegistryDelegate: ModuleRegistryDelegate = ModuleRegistryDelegate()
27
- ) : ExportedModule(context), ActivityEventListener {
17
+ class SharingModule : Module() {
18
+ private val context: Context
19
+ get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
20
+ private val currentActivity
21
+ get() = appContext.currentActivity ?: throw MissingCurrentActivityException()
28
22
  private var pendingPromise: Promise? = null
29
- private val uiManager: UIManager by moduleRegistry()
30
- override fun getName() = "ExpoSharing"
31
23
 
32
- private inline fun <reified T> moduleRegistry() =
33
- moduleRegistryDelegate.getFromModuleRegistry<T>()
24
+ override fun definition() = ModuleDefinition {
25
+ Name("ExpoSharing")
34
26
 
35
- override fun onCreate(moduleRegistry: ModuleRegistry) {
36
- moduleRegistryDelegate.onCreate(moduleRegistry)
37
- uiManager.registerActivityEventListener(this)
38
- }
39
-
40
- override fun onDestroy() {
41
- uiManager.unregisterActivityEventListener(this)
42
- }
43
-
44
- @ExpoMethod
45
- fun shareAsync(url: String?, params: ReadableArguments, promise: Promise) {
46
- if (pendingPromise != null) {
47
- promise.reject("ERR_SHARING_MUL", "Another share request is being processed now.")
48
- return
27
+ AsyncFunction("shareAsync") { url: String?, params: SharingOptions, promise: Promise ->
28
+ if (pendingPromise != null) {
29
+ throw SharingInProgressException()
30
+ }
31
+ try {
32
+ val fileToShare = getLocalFileFoUrl(url)
33
+ val contentUri = FileProvider.getUriForFile(
34
+ context,
35
+ context.applicationInfo.packageName + ".SharingFileProvider",
36
+ fileToShare
37
+ )
38
+ val mimeType = params.mimeType
39
+ ?: URLConnection.guessContentTypeFromName(fileToShare.name)
40
+ ?: "*/*"
41
+ val intent = Intent.createChooser(
42
+ createSharingIntent(contentUri, mimeType),
43
+ params.dialogTitle
44
+ )
45
+ val resInfoList = context.packageManager.queryIntentActivities(
46
+ intent,
47
+ PackageManager.MATCH_DEFAULT_ONLY
48
+ )
49
+ resInfoList.forEach {
50
+ val packageName = it.activityInfo.packageName
51
+ context.grantUriPermission(packageName, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
52
+ }
53
+ pendingPromise = promise
54
+ currentActivity.startActivity(intent)
55
+ } catch (e: InvalidArgumentException) {
56
+ throw SharingInvalidArgsException(e.message, e)
57
+ } catch (e: Exception) {
58
+ throw SharingFailedException("Failed to share the file: ${e.message}", e)
59
+ }
49
60
  }
50
- try {
51
- val fileToShare = getLocalFileFoUrl(url)
52
- val contentUri = FileProvider.getUriForFile(
53
- context,
54
- context.applicationInfo.packageName + ".SharingFileProvider",
55
- fileToShare
56
- )
57
- val mimeType = params.getString(MIME_TYPE_OPTIONS_KEY)
58
- ?: URLConnection.guessContentTypeFromName(fileToShare.name)
59
- ?: "*/*"
60
- val intent = Intent.createChooser(
61
- createSharingIntent(contentUri, mimeType),
62
- params.getString(DIALOG_TITLE_OPTIONS_KEY)
63
- )
64
- val resInfoList = context.packageManager.queryIntentActivities(
65
- intent,
66
- PackageManager.MATCH_DEFAULT_ONLY
67
- )
68
- resInfoList.forEach {
69
- val packageName = it.activityInfo.packageName
70
- context.grantUriPermission(packageName, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
61
+
62
+ OnActivityResult { _, (requestCode) ->
63
+ if (requestCode == REQUEST_CODE && pendingPromise != null) {
64
+ pendingPromise?.resolve(null)
65
+ pendingPromise = null
71
66
  }
72
- val activityProvider: ActivityProvider by moduleRegistry()
73
- activityProvider.currentActivity.startActivityForResult(intent, REQUEST_CODE)
74
- pendingPromise = promise
75
- } catch (e: InvalidArgumentException) {
76
- promise.reject("ERR_SHARING_URL", e.message, e)
77
- } catch (e: Exception) {
78
- promise.reject("ERR_SHARING", "Failed to share the file: " + e.message, e)
79
67
  }
80
68
  }
81
69
 
@@ -97,8 +85,9 @@ class SharingModule(
97
85
  }
98
86
 
99
87
  private fun isAllowedToRead(url: String?): Boolean {
100
- val permissionModuleInterface: FilePermissionModuleInterface by moduleRegistry()
101
- return permissionModuleInterface.getPathPermissions(context, url).contains(Permission.READ)
88
+ val permissions = appContext.filePermission
89
+ return permissions?.getPathPermissions(context, url)?.contains(Permission.READ)
90
+ ?: false
102
91
  }
103
92
 
104
93
  private fun createSharingIntent(uri: Uri, mimeType: String?) =
@@ -108,18 +97,7 @@ class SharingModule(
108
97
  addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
109
98
  }
110
99
 
111
- override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
112
- if (requestCode == REQUEST_CODE && pendingPromise != null) {
113
- pendingPromise?.resolve(null)
114
- pendingPromise = null
115
- }
116
- }
117
-
118
- override fun onNewIntent(intent: Intent) = Unit
119
-
120
100
  companion object {
121
101
  private const val REQUEST_CODE = 8524
122
- private const val MIME_TYPE_OPTIONS_KEY = "mimeType"
123
- private const val DIALOG_TITLE_OPTIONS_KEY = "dialogTitle"
124
102
  }
125
103
  }
@@ -0,0 +1,10 @@
1
+ package expo.modules.sharing
2
+
3
+ import expo.modules.kotlin.records.Field
4
+ import expo.modules.kotlin.records.Record
5
+
6
+ data class SharingOptions(
7
+ @Field val mimeType: String?,
8
+ @Field val UTI: String?,
9
+ @Field val dialogTitle: String?
10
+ ) : Record
@@ -1,3 +1,3 @@
1
- declare const _default: import("expo-modules-core").ProxyNativeModule;
1
+ declare const _default: any;
2
2
  export default _default;
3
3
  //# sourceMappingURL=ExpoSharing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoSharing.d.ts","sourceRoot":"","sources":["../src/ExpoSharing.ts"],"names":[],"mappings":";AAEA,wBAA8C"}
1
+ {"version":3,"file":"ExpoSharing.d.ts","sourceRoot":"","sources":["../src/ExpoSharing.ts"],"names":[],"mappings":";AACA,wBAAkD"}
@@ -1,3 +1,3 @@
1
- import { NativeModulesProxy } from 'expo-modules-core';
2
- export default NativeModulesProxy.ExpoSharing;
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ export default requireNativeModule('ExpoSharing');
3
3
  //# sourceMappingURL=ExpoSharing.js.map
@@ -1 +1 @@
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"]}
1
+ {"version":3,"file":"ExpoSharing.js","sourceRoot":"","sources":["../src/ExpoSharing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,eAAe,mBAAmB,CAAC,aAAa,CAAC,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\nexport default requireNativeModule('ExpoSharing');\n"]}
@@ -1,4 +1,4 @@
1
- declare type ShareOptions = {
1
+ type ShareOptions = {
2
2
  title?: string;
3
3
  text?: string;
4
4
  url?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoSharing.web.d.ts","sourceRoot":"","sources":["../src/ExpoSharing.web.ts"],"names":[],"mappings":"AAEA,aAAK,YAAY,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;;;wBAMxC,QAAQ,OAAO,CAAC;oBAOpB,MAAM,YAAW,YAAY,GAAQ,QAAQ,IAAI,CAAC;;AAX1E,wBAmBE"}
1
+ {"version":3,"file":"ExpoSharing.web.d.ts","sourceRoot":"","sources":["../src/ExpoSharing.web.ts"],"names":[],"mappings":"AAEA,KAAK,YAAY,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;;;wBAMxC,QAAQ,OAAO,CAAC;oBAOpB,MAAM,YAAW,YAAY,GAAQ,QAAQ,IAAI,CAAC;;AAX1E,wBAmBE"}
@@ -1,4 +1,4 @@
1
- export declare type SharingOptions = {
1
+ export type SharingOptions = {
2
2
  /**
3
3
  * Sets `mimeType` for `Intent`.
4
4
  * @platform android
@@ -1 +1 @@
1
- {"version":3,"file":"Sharing.d.ts","sourceRoot":"","sources":["../src/Sharing.ts"],"names":[],"mappings":"AAKA,oBAAY,cAAc,GAAG;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAGF;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CASzD;AAGD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKzF"}
1
+ {"version":3,"file":"Sharing.d.ts","sourceRoot":"","sources":["../src/Sharing.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAGF;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CASzD;AAGD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKzF"}
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "expo-sharing",
3
+ "platforms": ["ios", "android"],
4
+ "android": {
5
+ "modules": ["expo.modules.sharing.SharingModule"]
6
+ }
7
+ }
@@ -48,11 +48,10 @@ EX_EXPORT_METHOD_AS(shareAsync,
48
48
  _documentInteractionController.delegate = self;
49
49
  _documentInteractionController.UTI = params[@"UTI"];
50
50
 
51
- UIViewController *viewController = [[_moduleRegistry getModuleImplementingProtocol:@protocol(EXUtilitiesInterface)] currentViewController];
52
-
53
51
  EX_WEAKIFY(self);
54
52
  dispatch_async(dispatch_get_main_queue(), ^{
55
53
  EX_ENSURE_STRONGIFY(self);
54
+ UIViewController *viewController = [[self.moduleRegistry getModuleImplementingProtocol:@protocol(EXUtilitiesInterface)] currentViewController];
56
55
  UIView *rootView = [viewController view];
57
56
  if ([self.documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:rootView animated:YES]) {
58
57
  self.pendingResolver = resolve;
@@ -68,7 +67,19 @@ EX_EXPORT_METHOD_AS(shareAsync,
68
67
  _pendingResolver(nil);
69
68
  _pendingResolver = nil;
70
69
 
71
- _documentInteractionController = nil;
70
+ // This delegate method is called whenever:
71
+ // a) the share sheet is canceled
72
+ // b) an app is chosen, it's dialog opened, and the share is confirmed/ sent
73
+ // c) an app is chosen, it's dialog opened, and that dialog is canceled
74
+ // In case c), the share sheet remains open, even though the promise was resolved
75
+ // Future attempts to share without closing the sheet will fail to attach the file, so we need to close it
76
+ // No other delegate methods fire only when the share sheet is dismissed, unfortunately
77
+ EX_WEAKIFY(self);
78
+ dispatch_async(dispatch_get_main_queue(), ^{
79
+ EX_ENSURE_STRONGIFY(self);
80
+ [self.documentInteractionController dismissMenuAnimated:true];
81
+ self.documentInteractionController = nil;
82
+ });
72
83
  }
73
84
 
74
85
  @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-sharing",
3
- "version": "11.0.0",
3
+ "version": "11.1.0",
4
4
  "description": "ExpoSharing standalone module",
5
5
  "main": "build/Sharing.js",
6
6
  "types": "build/Sharing.d.ts",
@@ -37,5 +37,5 @@
37
37
  "peerDependencies": {
38
38
  "expo": "*"
39
39
  },
40
- "gitHead": "eab2b09c735fb0fc2bf734a3f29a6593adba3838"
40
+ "gitHead": "ba80e8181b79d06e00a245653727f4eaeb80420e"
41
41
  }
@@ -1,3 +1,2 @@
1
- import { NativeModulesProxy } from 'expo-modules-core';
2
-
3
- export default NativeModulesProxy.ExpoSharing;
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ export default requireNativeModule('ExpoSharing');
@@ -1,9 +0,0 @@
1
- package expo.modules.sharing
2
-
3
- import android.content.Context
4
- import expo.modules.core.BasePackage
5
-
6
- class SharingPackage : BasePackage() {
7
- override fun createExportedModules(context: Context) =
8
- listOf(SharingModule(context))
9
- }
package/unimodule.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "name": "expo-sharing",
3
- "platforms": ["ios", "android"]
4
- }