diff --git a/docs/share_plus/usage.mdx b/docs/share_plus/usage.mdx index 3dec4c1ec6..d7683e120f 100644 --- a/docs/share_plus/usage.mdx +++ b/docs/share_plus/usage.mdx @@ -35,3 +35,21 @@ To share one or multiple files invoke the static `shareFiles` method anywhere in Share.shareFiles(['${directory.path}/image.jpg'], text: 'Great picture'); Share.shareFiles(['${directory.path}/image1.jpg', '${directory.path}/image2.jpg']); ``` + +If you are interested in the action your user performed with the share sheet, you can instead use the `shareWithResult` and `shareFilesWithResult` methods. + +```dart +final result = await Share.shareWithResult('check out my website https://example.com'); + +if (result.status != ShareResultStatus.success) { + print('thank you for sharing my website!'); +} +``` + +```dart +final result = await Share.shareFilesWithResult(['${directory.path}/image.jpg'], text: 'Great picture'); + +if (result.status == ShareResultStatus.dismissed) { + print('did you not like the picture?'); +} +``` diff --git a/packages/share_plus/share_plus/CHANGELOG.md b/packages/share_plus/share_plus/CHANGELOG.md index ce449165c8..39f9141699 100644 --- a/packages/share_plus/share_plus/CHANGELOG.md +++ b/packages/share_plus/share_plus/CHANGELOG.md @@ -1,3 +1,9 @@ +## 4.0.3 + +- Android: Revert increased minSdkVersion back to 16 +- Gracefully fall back from `shareWithResult` to regular `share` methods on unsupported platforms +- Improve documentation for `shareWithResult` methods + ## 4.0.2 - Fix type mismatch on Android for some users diff --git a/packages/share_plus/share_plus/android/build.gradle b/packages/share_plus/share_plus/android/build.gradle index bc486e272c..4f9ee5dee7 100644 --- a/packages/share_plus/share_plus/android/build.gradle +++ b/packages/share_plus/share_plus/android/build.gradle @@ -28,7 +28,7 @@ android { compileSdkVersion 31 defaultConfig { - minSdkVersion 22 + minSdkVersion 16 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { diff --git a/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/MethodCallHandler.kt b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/MethodCallHandler.kt index 775fed782c..b2baad9346 100644 --- a/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/MethodCallHandler.kt +++ b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/MethodCallHandler.kt @@ -1,5 +1,6 @@ package dev.fluttercommunity.plus.share +import android.os.Build import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import java.io.IOException @@ -11,48 +12,34 @@ internal class MethodCallHandler( ) : MethodChannel.MethodCallHandler { override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + // The user used a *WithResult method + val isResultRequested = call.method.endsWith("WithResult") + // We don't attempt to return a result if the current API version doesn't support it + val isWithResult = isResultRequested && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 + when (call.method) { - "share" -> { + "share", "shareWithResult" -> { expectMapArguments(call) + if (isWithResult && !manager.setCallback(result)) return + // Android does not support showing the share sheet at a particular point on screen. share.share( call.argument("text") as String, call.argument("subject") as String?, - false, + isWithResult, ) - result.success(null) - } - "shareFiles" -> { - expectMapArguments(call) - // Android does not support showing the share sheet at a particular point on screen. - try { - share.shareFiles( - call.argument>("paths")!!, - call.argument?>("mimeTypes"), - call.argument("text"), - call.argument("subject"), - false, - ) - result.success(null) - } catch (e: IOException) { - result.error("Share failed", e.message, null) + if (!isWithResult) { + if (isResultRequested) { + result.success("dev.fluttercommunity.plus/share/unavailable") + } else { + result.success(null) + } } } - "shareWithResult" -> { + "shareFiles", "shareFilesWithResult" -> { expectMapArguments(call) - if (!manager.setCallback(result)) return - - // Android does not support showing the share sheet at a particular point on screen. - share.share( - call.argument("text") as String, - call.argument("subject") as String?, - true, - ) - } - "shareFilesWithResult" -> { - expectMapArguments(call) - if (!manager.setCallback(result)) return + if (isWithResult && !manager.setCallback(result)) return // Android does not support showing the share sheet at a particular point on screen. try { @@ -61,8 +48,16 @@ internal class MethodCallHandler( call.argument?>("mimeTypes"), call.argument("text"), call.argument("subject"), - true, + isWithResult, ) + + if (!isWithResult) { + if (isResultRequested) { + result.success("dev.fluttercommunity.plus/share/unavailable") + } else { + result.success(null) + } + } } catch (e: IOException) { result.error("Share failed", e.message, null) } diff --git a/packages/share_plus/share_plus/example/android/app/build.gradle b/packages/share_plus/share_plus/example/android/app/build.gradle index d30aca2256..8c0a10bb9c 100644 --- a/packages/share_plus/share_plus/example/android/app/build.gradle +++ b/packages/share_plus/share_plus/example/android/app/build.gradle @@ -33,7 +33,7 @@ android { defaultConfig { applicationId "io.flutter.plugins.shareexample" - minSdkVersion 22 + minSdkVersion 16 targetSdkVersion 31 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/packages/share_plus/share_plus/lib/share_plus.dart b/packages/share_plus/share_plus/lib/share_plus.dart index 3fec183976..ed206d137c 100644 --- a/packages/share_plus/share_plus/lib/share_plus.dart +++ b/packages/share_plus/share_plus/lib/share_plus.dart @@ -87,12 +87,18 @@ class Share { /// any other call to any share method that returns a result _might_ result in /// a [PlatformException] (on Android). /// - /// Because IOS, Android and macOS provide different feedback on share-sheet interaction, - /// a result on IOS will be more specific than on Android or macOS. While IOS can detect if - /// the user actually completed his selected action or aborted it midway, Android and macOS - /// only record if the user selected an action or outright dismissed the share-sheet. + /// Because IOS, Android and macOS provide different feedback on share-sheet + /// interaction, a result on IOS will be more specific than on Android or macOS. + /// While on IOS the selected action can inform its caller that it was completed + /// or dismissed midway (_actions are free to return whatever they want_), + /// Android and macOS only record if the user selected an action or outright + /// dismissed the share-sheet. It is not guaranteed that the user actually shared + /// something. /// /// **Currently only implemented on IOS, Android and macOS.** + /// + /// Will gracefully fall back to the non result variant if not implemented + /// for the current environment and return [ShareResult.unavailable]. static Future shareWithResult( String text, { String? subject, @@ -111,12 +117,18 @@ class Share { /// any other call to any share method that returns a result _might_ result in /// a [PlatformException] (on Android). /// - /// Because IOS, Android and macOS provide different feedback on share-sheet interaction, - /// a result on IOS will be more specific than on Android or macOS. While IOS can detect if - /// the user actually completed his selected action or aborted it midway, Android and macOS - /// only record if the user selected an action or outright dismissed the share-sheet. + /// Because IOS, Android and macOS provide different feedback on share-sheet + /// interaction, a result on IOS will be more specific than on Android or macOS. + /// While on IOS the selected action can inform its caller that it was completed + /// or dismissed midway (_actions are free to return whatever they want_), + /// Android and macOS only record if the user selected an action or outright + /// dismissed the share-sheet. It is not guaranteed that the user actually shared + /// something. /// /// **Currently only implemented on IOS, Android and macOS.** + /// + /// Will gracefully fall back to the non result variant if not implemented + /// for the current environment and return [ShareResult.unavailable]. static Future shareFilesWithResult( List paths, { List? mimeTypes, diff --git a/packages/share_plus/share_plus/pubspec.yaml b/packages/share_plus/share_plus/pubspec.yaml index 987288dd8f..2ea69782f5 100644 --- a/packages/share_plus/share_plus/pubspec.yaml +++ b/packages/share_plus/share_plus/pubspec.yaml @@ -1,6 +1,6 @@ name: share_plus description: Flutter plugin for sharing content via the platform share UI, using the ACTION_SEND intent on Android and UIActivityViewController on iOS. -version: 4.0.2 +version: 4.0.3 homepage: https://plus.fluttercommunity.dev/ repository: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/ @@ -26,7 +26,7 @@ dependencies: mime: ^1.0.0 flutter: sdk: flutter - share_plus_platform_interface: ^3.0.0 + share_plus_platform_interface: ^3.0.2 share_plus_linux: ^3.0.0 share_plus_macos: ^3.0.0 share_plus_windows: ^3.0.0 diff --git a/packages/share_plus/share_plus_platform_interface/CHANGELOG.md b/packages/share_plus/share_plus_platform_interface/CHANGELOG.md index 4d2df718d6..8d5901fd58 100644 --- a/packages/share_plus/share_plus_platform_interface/CHANGELOG.md +++ b/packages/share_plus/share_plus_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.2 + +- Gracefully fall back from `shareWithResult` to regular `share` methods on unsupported platforms + ## 3.0.1 - Set min Flutter to 1.20.0 to match Share plugins on all platforms diff --git a/packages/share_plus/share_plus_platform_interface/lib/share_plus_platform_interface.dart b/packages/share_plus/share_plus_platform_interface/lib/share_plus_platform_interface.dart index aad3ad4c4f..0049d6c128 100644 --- a/packages/share_plus/share_plus_platform_interface/lib/share_plus_platform_interface.dart +++ b/packages/share_plus/share_plus_platform_interface/lib/share_plus_platform_interface.dart @@ -66,8 +66,13 @@ class SharePlatform extends PlatformInterface { String? subject, Rect? sharePositionOrigin, }) async { - throw UnimplementedError( - 'shareWithResult() has only been implemented on IOS, Android & macOS'); + await _instance.share( + text, + subject: subject, + sharePositionOrigin: sharePositionOrigin, + ); + + return _resultUnavailable; } /// Share files with Result. @@ -78,8 +83,15 @@ class SharePlatform extends PlatformInterface { String? text, Rect? sharePositionOrigin, }) async { - throw UnimplementedError( - 'shareWithResult() has only been implemented on IOS, Android & macOS'); + await _instance.shareFiles( + paths, + mimeTypes: mimeTypes, + subject: subject, + text: text, + sharePositionOrigin: sharePositionOrigin, + ); + + return _resultUnavailable; } } @@ -94,8 +106,8 @@ class ShareResult { /// /// Note that an empty string means the share-sheet was /// dismissed without any action and the special value - /// `dev.fluttercommunity.plus/share/unavailable` is caused - /// by an unavailable Android Activity at runtime. + /// `dev.fluttercommunity.plus/share/unavailable` points + /// to the current environment not supporting share results. final String raw; /// The action the user has taken @@ -115,3 +127,9 @@ enum ShareResultStatus { /// The status can not be determined unavailable, } + +/// Returned if the platform is not supported +const _resultUnavailable = ShareResult( + 'dev.fluttercommunity.plus/share/unavailable', + ShareResultStatus.unavailable, +); diff --git a/packages/share_plus/share_plus_platform_interface/pubspec.yaml b/packages/share_plus/share_plus_platform_interface/pubspec.yaml index 50ca67971b..4e10b2a256 100644 --- a/packages/share_plus/share_plus_platform_interface/pubspec.yaml +++ b/packages/share_plus/share_plus_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: share_plus_platform_interface description: A common platform interface for the share_plus plugin. -version: 3.0.1 +version: 3.0.2 homepage: https://plus.fluttercommunity.dev/ repository: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/ diff --git a/packages/share_plus/share_plus_platform_interface/test/share_plus_platform_interface_test.dart b/packages/share_plus/share_plus_platform_interface/test/share_plus_platform_interface_test.dart index 38da5d0460..f33bdcc6bf 100644 --- a/packages/share_plus/share_plus_platform_interface/test/share_plus_platform_interface_test.dart +++ b/packages/share_plus/share_plus_platform_interface/test/share_plus_platform_interface_test.dart @@ -179,19 +179,48 @@ void main() { }); }); - test('withResult methods throw unimplemented on non IOS & Android', () async { + test('withResult methods return unavailable on non IOS & Android', () async { + const resultUnavailable = ShareResult( + 'dev.fluttercommunity.plus/share/unavailable', + ShareResultStatus.unavailable, + ); + expect( - () => sharePlatform.shareWithResult('some text to share'), - throwsA(const TypeMatcher()), + sharePlatform.shareWithResult('some text to share'), + completion(equals(resultUnavailable)), ); await withFile('tempfile-83649d.png', (File fd) async { expect( - () => sharePlatform.shareFilesWithResult([fd.path]), - throwsA(const TypeMatcher()), + sharePlatform.shareFilesWithResult([fd.path]), + completion(equals(resultUnavailable)), ); }); }); + + test('withResult methods invoke normal share on non IOS & Android', () async { + await sharePlatform.shareWithResult( + 'some text to share', + subject: 'some subject to share', + sharePositionOrigin: const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0), + ); + verify(mockChannel.invokeMethod('share', { + 'text': 'some text to share', + 'subject': 'some subject to share', + 'originX': 1.0, + 'originY': 2.0, + 'originWidth': 3.0, + 'originHeight': 4.0, + })); + + await withFile('tempfile-83649e.png', (File fd) async { + await sharePlatform.shareFilesWithResult([fd.path]); + verify(mockChannel.invokeMethod('shareFiles', { + 'paths': [fd.path], + 'mimeTypes': ['image/png'], + })); + }); + }); } /// Execute a block within a context that handles creation and deletion of a helper file