From 649187f1794b18c37f1f78e44f4fccc5f0e39ce1 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Sun, 12 Dec 2021 14:27:57 +0200 Subject: [PATCH 1/9] [share_plus] Android dependencies update --- packages/share_plus/share_plus/CHANGELOG.md | 5 +++++ packages/share_plus/share_plus/android/build.gradle | 6 +++--- .../android/gradle/wrapper/gradle-wrapper.properties | 5 +++++ packages/share_plus/share_plus/android/settings.gradle | 2 +- .../share_plus/share_plus/example/lib/image_previews.dart | 1 - packages/share_plus/share_plus/example/lib/main.dart | 4 ++-- packages/share_plus/share_plus/example/pubspec.yaml | 2 +- packages/share_plus/share_plus/pubspec.yaml | 2 +- 8 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 packages/share_plus/share_plus/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/share_plus/share_plus/CHANGELOG.md b/packages/share_plus/share_plus/CHANGELOG.md index 33e6f04463..dfc14c0880 100644 --- a/packages/share_plus/share_plus/CHANGELOG.md +++ b/packages/share_plus/share_plus/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.5 + +- Android: Update dependencies, build config updates +- Example: Fix project title + ## 3.0.4 - iOS: Fixed sharing malformed URLs diff --git a/packages/share_plus/share_plus/android/build.gradle b/packages/share_plus/share_plus/android/build.gradle index 1ffb74eb60..7bb80f0756 100644 --- a/packages/share_plus/share_plus/android/build.gradle +++ b/packages/share_plus/share_plus/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.0.2' + classpath 'com.android.tools.build:gradle:7.0.4' } } @@ -33,7 +33,7 @@ android { } dependencies { - implementation 'androidx.core:core:1.6.0' - implementation 'androidx.annotation:annotation:1.2.0' + implementation 'androidx.core:core:1.7.0' + implementation 'androidx.annotation:annotation:1.3.0' } } diff --git a/packages/share_plus/share_plus/android/gradle/wrapper/gradle-wrapper.properties b/packages/share_plus/share_plus/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..84d1f85fd6 --- /dev/null +++ b/packages/share_plus/share_plus/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/share_plus/share_plus/android/settings.gradle b/packages/share_plus/share_plus/android/settings.gradle index 64350ae697..82e4e954fa 100644 --- a/packages/share_plus/share_plus/android/settings.gradle +++ b/packages/share_plus/share_plus/android/settings.gradle @@ -1 +1 @@ -rootProject.name = 'share' +rootProject.name = 'share_plus' diff --git a/packages/share_plus/share_plus/example/lib/image_previews.dart b/packages/share_plus/share_plus/example/lib/image_previews.dart index f496ad9a2b..e7af9413e8 100644 --- a/packages/share_plus/share_plus/example/lib/image_previews.dart +++ b/packages/share_plus/share_plus/example/lib/image_previews.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; /// Widget for displaying a preview of images class ImagePreviews extends StatelessWidget { diff --git a/packages/share_plus/share_plus/example/lib/main.dart b/packages/share_plus/share_plus/example/lib/main.dart index 045f596caf..e9823cf4cf 100644 --- a/packages/share_plus/share_plus/example/lib/main.dart +++ b/packages/share_plus/share_plus/example/lib/main.dart @@ -29,10 +29,10 @@ class DemoAppState extends State { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Share Plugin Demo', + title: 'Share Plus Plugin Demo', home: Scaffold( appBar: AppBar( - title: const Text('Share Plugin Demo'), + title: const Text('Share Plus Plugin Demo'), ), body: SingleChildScrollView( child: Padding( diff --git a/packages/share_plus/share_plus/example/pubspec.yaml b/packages/share_plus/share_plus/example/pubspec.yaml index 363511c3a3..3c86b71aee 100644 --- a/packages/share_plus/share_plus/example/pubspec.yaml +++ b/packages/share_plus/share_plus/example/pubspec.yaml @@ -6,7 +6,7 @@ dependencies: sdk: flutter share_plus: path: ../ - image_picker: ^0.8.4+2 + image_picker: ^0.8.4 dependency_overrides: share_plus_linux: diff --git a/packages/share_plus/share_plus/pubspec.yaml b/packages/share_plus/share_plus/pubspec.yaml index 7bf9381d2b..9ff2c4fe0c 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: 3.0.4 +version: 3.0.5 homepage: https://plus.fluttercommunity.dev/ repository: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/ From 545afc594bcb23621e6f793d7ac9edb23918f4bc Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Sun, 12 Dec 2021 14:36:40 +0200 Subject: [PATCH 2/9] [share_plus] Add Kotlin to config, migrate ShareFilProvider to Kotlin --- .../share_plus/android/build.gradle | 50 ++++++++++--------- .../plus/share/ShareFileProvider.java | 14 ------ .../plus/share/ShareFileProvider.kt | 10 ++++ 3 files changed, 37 insertions(+), 37 deletions(-) delete mode 100644 packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/ShareFileProvider.java create mode 100644 packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/ShareFileProvider.kt diff --git a/packages/share_plus/share_plus/android/build.gradle b/packages/share_plus/share_plus/android/build.gradle index 7bb80f0756..f2de2fad83 100644 --- a/packages/share_plus/share_plus/android/build.gradle +++ b/packages/share_plus/share_plus/android/build.gradle @@ -2,38 +2,42 @@ group 'dev.fluttercommunity.plus.share' version '1.0-SNAPSHOT' buildscript { - repositories { - google() - mavenCentral() - } + ext.kotlin_version = '1.5.32' + repositories { + google() + mavenCentral() + } - dependencies { - classpath 'com.android.tools.build:gradle:7.0.4' - } + dependencies { + classpath 'com.android.tools.build:gradle:7.0.4' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } } rootProject.allprojects { - repositories { - google() - mavenCentral() - } + repositories { + google() + mavenCentral() + } } apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' android { - compileSdkVersion 31 + compileSdkVersion 31 - defaultConfig { - minSdkVersion 16 - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - lintOptions { - disable 'InvalidPackage' - } + defaultConfig { + minSdkVersion 16 + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + lintOptions { + disable 'InvalidPackage' + } - dependencies { - implementation 'androidx.core:core:1.7.0' - implementation 'androidx.annotation:annotation:1.3.0' - } + dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.annotation:annotation:1.3.0' + } } diff --git a/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/ShareFileProvider.java b/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/ShareFileProvider.java deleted file mode 100644 index cea5ae018e..0000000000 --- a/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/ShareFileProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2019 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package dev.fluttercommunity.plus.share; - -import androidx.core.content.FileProvider; - -/** - * Providing a custom {@code FileProvider} prevents manifest {@code } name collisions. - * - *

See https://developer.android.com/guide/topics/manifest/provider-element.html for details. - */ -public class ShareFileProvider extends FileProvider {} diff --git a/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/ShareFileProvider.kt b/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/ShareFileProvider.kt new file mode 100644 index 0000000000..4c35925519 --- /dev/null +++ b/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/ShareFileProvider.kt @@ -0,0 +1,10 @@ +package dev.fluttercommunity.plus.share + +import androidx.core.content.FileProvider + +/** + * Providing a custom `FileProvider` prevents manifest `` name collisions. + * + * See https://developer.android.com/guide/topics/manifest/provider-element.html for details. + */ +class ShareFileProvider : FileProvider() From 72dbb401a9bdf2e49415c7adf35e21728661e107 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Sun, 12 Dec 2021 14:47:24 +0200 Subject: [PATCH 3/9] [share_plus] Update Android source set config for Kotlin files --- packages/share_plus/share_plus/android/build.gradle | 3 +++ .../dev/fluttercommunity/plus/share/ShareFileProvider.kt | 0 2 files changed, 3 insertions(+) rename packages/share_plus/share_plus/android/src/main/{java => kotlin}/dev/fluttercommunity/plus/share/ShareFileProvider.kt (100%) diff --git a/packages/share_plus/share_plus/android/build.gradle b/packages/share_plus/share_plus/android/build.gradle index f2de2fad83..121a6e128d 100644 --- a/packages/share_plus/share_plus/android/build.gradle +++ b/packages/share_plus/share_plus/android/build.gradle @@ -27,6 +27,9 @@ apply plugin: 'kotlin-android' android { compileSdkVersion 31 + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } defaultConfig { minSdkVersion 16 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/ShareFileProvider.kt b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/ShareFileProvider.kt similarity index 100% rename from packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/ShareFileProvider.kt rename to packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/ShareFileProvider.kt From 48196262879c288f8060e5d686fd171c90c6b3b9 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Thu, 23 Dec 2021 14:13:11 +0200 Subject: [PATCH 4/9] [share_plus] Migrate Android part to Kotlin --- .../plus/share/MethodCallHandler.java | 59 ----- .../fluttercommunity/plus/share/Share.java | 235 ------------------ .../plus/share/SharePlusPlugin.java | 54 ---- .../plus/share/MethodCallHandler.kt | 45 ++++ .../dev/fluttercommunity/plus/share/Share.kt | 209 ++++++++++++++++ .../plus/share/SharePlusPlugin.kt | 44 ++++ 6 files changed, 298 insertions(+), 348 deletions(-) delete mode 100644 packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/MethodCallHandler.java delete mode 100644 packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/Share.java delete mode 100644 packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/SharePlusPlugin.java create mode 100644 packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/MethodCallHandler.kt create mode 100644 packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/Share.kt create mode 100644 packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/SharePlusPlugin.kt diff --git a/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/MethodCallHandler.java b/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/MethodCallHandler.java deleted file mode 100644 index d06a87f832..0000000000 --- a/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/MethodCallHandler.java +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package dev.fluttercommunity.plus.share; - -import androidx.annotation.NonNull; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; -import java.io.*; -import java.util.List; -import java.util.Map; - -/** Handles the method calls for the plugin. */ -class MethodCallHandler implements MethodChannel.MethodCallHandler { - - private final Share share; - - MethodCallHandler(Share share) { - this.share = share; - } - - @Override - @SuppressWarnings("unchecked") - public void onMethodCall(MethodCall call, @NonNull MethodChannel.Result result) { - switch (call.method) { - case "share": - expectMapArguments(call); - // Android does not support showing the share sheet at a particular point on screen. - share.share((String) call.argument("text"), (String) call.argument("subject")); - result.success(null); - break; - case "shareFiles": - expectMapArguments(call); - - // Android does not support showing the share sheet at a particular point on screen. - try { - share.shareFiles( - (List) call.argument("paths"), - (List) call.argument("mimeTypes"), - (String) call.argument("text"), - (String) call.argument("subject")); - result.success(null); - } catch (IOException e) { - result.error(e.getMessage(), null, null); - } - break; - default: - result.notImplemented(); - break; - } - } - - private void expectMapArguments(MethodCall call) throws IllegalArgumentException { - if (!(call.arguments instanceof Map)) { - throw new IllegalArgumentException("Map argument expected"); - } - } -} diff --git a/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/Share.java b/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/Share.java deleted file mode 100644 index 44990f14b5..0000000000 --- a/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/Share.java +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2019 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package dev.fluttercommunity.plus.share; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import androidx.annotation.NonNull; -import androidx.core.content.FileProvider; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; - -/** Handles share intent. */ -class Share { - - private final Context context; - private Activity activity; - - private final String providerAuthority; - - /** - * Constructs a Share object. The {@code context} and {@code activity} are used to start the share - * intent. The {@code activity} might be null when constructing the {@link Share} object and set - * to non-null when an activity is available using {@link #setActivity(Activity)}. - */ - Share(Context context, Activity activity) { - this.context = context; - this.activity = activity; - - this.providerAuthority = getContext().getPackageName() + ".flutter.share_provider"; - } - - /** - * Sets the activity when an activity is available. When the activity becomes unavailable, use - * this method to set it to null. - */ - void setActivity(Activity activity) { - this.activity = activity; - } - - void share(String text, String subject) { - if (text == null || text.isEmpty()) { - throw new IllegalArgumentException("Non-empty text expected"); - } - - Intent shareIntent = new Intent(); - shareIntent.setAction(Intent.ACTION_SEND); - shareIntent.putExtra(Intent.EXTRA_TEXT, text); - shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject); - shareIntent.setType("text/plain"); - Intent chooserIntent = Intent.createChooser(shareIntent, null /* dialog title optional */); - startActivity(chooserIntent); - } - - void shareFiles(List paths, List mimeTypes, String text, String subject) - throws IOException { - if (paths == null || paths.isEmpty()) { - throw new IllegalArgumentException("Non-empty path expected"); - } - - clearShareCacheFolder(); - ArrayList fileUris = getUrisForPaths(paths); - - Intent shareIntent = new Intent(); - if (fileUris.isEmpty()) { - share(text, subject); - return; - } else if (fileUris.size() == 1) { - shareIntent.setAction(Intent.ACTION_SEND); - shareIntent.putExtra(Intent.EXTRA_STREAM, fileUris.get(0)); - shareIntent.setType( - !mimeTypes.isEmpty() && mimeTypes.get(0) != null ? mimeTypes.get(0) : "*/*"); - } else { - shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE); - shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, fileUris); - shareIntent.setType(reduceMimeTypes(mimeTypes)); - } - if (text != null) shareIntent.putExtra(Intent.EXTRA_TEXT, text); - if (subject != null) shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject); - shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - Intent chooserIntent = Intent.createChooser(shareIntent, null /* dialog title optional */); - - List resInfoList = - getContext() - .getPackageManager() - .queryIntentActivities(chooserIntent, PackageManager.MATCH_DEFAULT_ONLY); - for (ResolveInfo resolveInfo : resInfoList) { - String packageName = resolveInfo.activityInfo.packageName; - for (Uri fileUri : fileUris) { - getContext() - .grantUriPermission( - packageName, - fileUri, - Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); - } - } - - startActivity(chooserIntent); - } - - private void startActivity(Intent intent) { - if (activity != null) { - activity.startActivity(intent); - } else if (context != null) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } else { - throw new IllegalStateException("Both context and activity are null"); - } - } - - private ArrayList getUrisForPaths(List paths) throws IOException { - ArrayList uris = new ArrayList<>(paths.size()); - - for (String path : paths) { - File file = new File(path); - if (fileIsInShareCache(file)) { - // If file was saved in '.../caches/share_plus' it will have been erased by 'clearShareCacheFolder();' - throw new IOException( - "File to share not allowed to be located in '" - + getShareCacheFolder().getCanonicalPath() - + "'"); - } - file = copyToShareCacheFolder(file); - - uris.add(FileProvider.getUriForFile(getContext(), providerAuthority, file)); - } - - return uris; - } - - private String reduceMimeTypes(List mimeTypes) { - if (mimeTypes.size() > 1) { - String reducedMimeType = mimeTypes.get(0); - for (int i = 1; i < mimeTypes.size(); i++) { - String mimeType = mimeTypes.get(i); - if (!reducedMimeType.equals(mimeType)) { - if (getMimeTypeBase(mimeType).equals(getMimeTypeBase(reducedMimeType))) { - reducedMimeType = getMimeTypeBase(mimeType) + "/*"; - } else { - reducedMimeType = "*/*"; - break; - } - } - } - return reducedMimeType; - } else if (mimeTypes.size() == 1) { - return mimeTypes.get(0); - } else { - return "*/*"; - } - } - - @NonNull - private String getMimeTypeBase(String mimeType) { - if (mimeType == null || !mimeType.contains("/")) { - return "*"; - } - - return mimeType.substring(0, mimeType.indexOf("/")); - } - - private boolean fileIsInShareCache(File file) { - try { - String filePath = file.getCanonicalPath(); - return filePath.startsWith(getShareCacheFolder().getCanonicalPath()); - } catch (IOException e) { - return false; - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private void clearShareCacheFolder() { - File folder = getShareCacheFolder(); - final File[] files = folder.listFiles(); - if (folder.exists() && files != null) { - for (File file : files) { - file.delete(); - } - folder.delete(); - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private File copyToShareCacheFolder(File file) throws IOException { - File folder = getShareCacheFolder(); - if (!folder.exists()) { - folder.mkdirs(); - } - - File newFile = new File(folder, file.getName()); - copy(file, newFile); - return newFile; - } - - @NonNull - private File getShareCacheFolder() { - return new File(getContext().getCacheDir(), "share_plus"); - } - - private Context getContext() { - if (activity != null) { - return activity; - } - if (context != null) { - return context; - } - - throw new IllegalStateException("Both context and activity are null"); - } - - private static void copy(File src, File dst) throws IOException { - try (InputStream in = new FileInputStream(src)) { - try (OutputStream out = new FileOutputStream(dst)) { - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - } - } - } -} diff --git a/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/SharePlusPlugin.java b/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/SharePlusPlugin.java deleted file mode 100644 index d10509ae47..0000000000 --- a/packages/share_plus/share_plus/android/src/main/java/dev/fluttercommunity/plus/share/SharePlusPlugin.java +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2019 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package dev.fluttercommunity.plus.share; - -import androidx.annotation.NonNull; -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.embedding.engine.plugins.activity.ActivityAware; -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; -import io.flutter.plugin.common.MethodChannel; - -/** Plugin method host for presenting a share sheet via Intent */ -public class SharePlusPlugin implements FlutterPlugin, ActivityAware { - - private static final String CHANNEL = "dev.fluttercommunity.plus/share"; - private Share share; - private MethodChannel methodChannel; - - @Override - public void onAttachedToEngine(FlutterPluginBinding binding) { - methodChannel = new MethodChannel(binding.getBinaryMessenger(), CHANNEL); - share = new Share(binding.getApplicationContext(), null); - MethodCallHandler handler = new MethodCallHandler(share); - methodChannel.setMethodCallHandler(handler); - } - - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - methodChannel.setMethodCallHandler(null); - methodChannel = null; - share = null; - } - - @Override - public void onAttachedToActivity(ActivityPluginBinding binding) { - share.setActivity(binding.getActivity()); - } - - @Override - public void onDetachedFromActivity() { - share.setActivity(null); - } - - @Override - public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { - onAttachedToActivity(binding); - } - - @Override - public void onDetachedFromActivityForConfigChanges() { - onDetachedFromActivity(); - } -} 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 new file mode 100644 index 0000000000..331a201b21 --- /dev/null +++ b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/MethodCallHandler.kt @@ -0,0 +1,45 @@ +package dev.fluttercommunity.plus.share + +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import java.io.IOException + +/** Handles the method calls for the plugin. */ +internal class MethodCallHandler(private val share: Share) : MethodChannel.MethodCallHandler { + + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + when (call.method) { + "share" -> { + expectMapArguments(call) + // 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?, + ) + 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"), + ) + result.success(null) + } catch (e: IOException) { + result.error(e.message, null, null) + } + } + else -> result.notImplemented() + } + } + + @Throws(IllegalArgumentException::class) + private fun expectMapArguments(call: MethodCall) { + require(call.arguments is Map<*, *>) { "Map arguments expected" } + } +} diff --git a/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/Share.kt b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/Share.kt new file mode 100644 index 0000000000..539dbc37d5 --- /dev/null +++ b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/Share.kt @@ -0,0 +1,209 @@ +package dev.fluttercommunity.plus.share + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import androidx.core.content.FileProvider +import java.io.File +import java.io.IOException + +/** + * Handles share intent. The `context` and `activity` are used to start the share + * intent. The `activity` might be null when constructing the [Share] object and set + * to non-null when an activity is available using [.setActivity]. + */ +internal class Share(private val context: Context?, private var activity: Activity?) { + private val providerAuthority: String by lazy { + getContext().packageName + ".flutter.share_provider" + } + + private val shareCacheFolder: File + get() = File(getContext().cacheDir, "share_plus") + + private fun getContext(): Context { + if (activity != null) { + return activity!! + } + if (context != null) { + return context + } + throw IllegalStateException("Both context and activity are null") + } + + /** + * Sets the activity when an activity is available. When the activity becomes unavailable, use + * this method to set it to null. + */ + fun setActivity(activity: Activity?) { + this.activity = activity + } + + fun share(text: String, subject: String?) { + val shareIntent = Intent().apply { + action = Intent.ACTION_SEND + type = "text/plain" + putExtra(Intent.EXTRA_TEXT, text) + putExtra(Intent.EXTRA_SUBJECT, subject) + } + + val chooserIntent = Intent.createChooser(shareIntent, null /* dialog title optional */) + startActivity(chooserIntent) + } + + @Throws(IOException::class) + fun shareFiles( + paths: List, + mimeTypes: List?, + text: String?, + subject: String? + ) { + clearShareCacheFolder() + val fileUris = getUrisForPaths(paths) + val shareIntent = Intent() + when { + (fileUris.isEmpty() && !text.isNullOrBlank()) -> { + share(text, subject) + return + } + fileUris.size == 1 -> { + val mimeType = if (!mimeTypes.isNullOrEmpty()) { + mimeTypes.first() + } else { + "*/*" + } + shareIntent.apply { + action = Intent.ACTION_SEND + type = mimeType + putExtra(Intent.EXTRA_STREAM, fileUris.first()) + } + } + else -> { + shareIntent.apply { + action = Intent.ACTION_SEND_MULTIPLE + type = reduceMimeTypes(mimeTypes) + putParcelableArrayListExtra(Intent.EXTRA_STREAM, fileUris) + } + } + } + if (text != null) shareIntent.putExtra(Intent.EXTRA_TEXT, text) + if (subject != null) shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject) + shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + val chooserIntent = Intent.createChooser(shareIntent, null /* dialog title optional */) + val resInfoList = getContext().packageManager.queryIntentActivities( + chooserIntent, PackageManager.MATCH_DEFAULT_ONLY + ) + resInfoList.forEach { resolveInfo -> + val packageName = resolveInfo.activityInfo.packageName + fileUris.forEach { fileUri -> + getContext().grantUriPermission( + packageName, + fileUri, + Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION, + ) + } + } + startActivity(chooserIntent) + } + + private fun startActivity(intent: Intent) { + when { + activity != null -> { + activity!!.startActivity(intent) + } + context != null -> { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + } + else -> { + throw IllegalStateException("Both context and activity are null") + } + } + } + + @Throws(IOException::class) + private fun getUrisForPaths(paths: List): ArrayList { + val uris = ArrayList(paths.size) + paths.forEach { path -> + var file = File(path) + if (fileIsInShareCache(file)) { + // If file is saved in '.../caches/share_plus' it will be erased by 'clearShareCacheFolder()' + throw IOException("Shared file can not be located in '${shareCacheFolder.canonicalPath}'") + } + file = copyToShareCacheFolder(file) + uris.add(FileProvider.getUriForFile(getContext(), providerAuthority, file)) + } + return uris + } + + /** + * Reduces provided MIME types to a common one to provide [Intent] with a correct type + * to share multiple files + */ + private fun reduceMimeTypes(mimeTypes: List?): String { + var reducedMimeType = "*/*" + + mimeTypes?.let { types -> + { + if (types.size == 1) { + reducedMimeType = types.first() + } else if (types.size > 1) { + var commonMimeType = types.first() + for (i in 1..types.lastIndex) { + if (commonMimeType != types[i]) { + if (getMimeTypeBase(commonMimeType) == getMimeTypeBase(types[i])) { + commonMimeType = getMimeTypeBase(types[i]) + "/*" + } else { + commonMimeType = "*/*" + break + } + } + } + reducedMimeType = commonMimeType + } + } + } + return reducedMimeType + } + + /** + * Returns the first part of provided MIME type, which comes before '/' symbol + */ + private fun getMimeTypeBase(mimeType: String?): String { + return if (mimeType == null || !mimeType.contains("/")) { + "*" + } else { + mimeType.substring(0, mimeType.indexOf("/")) + } + } + + private fun fileIsInShareCache(file: File): Boolean { + return try { + val filePath = file.canonicalPath + filePath.startsWith(shareCacheFolder.canonicalPath) + } catch (e: IOException) { + false + } + } + + private fun clearShareCacheFolder() { + val folder = shareCacheFolder + val files = folder.listFiles() + if (folder.exists() && !files.isNullOrEmpty()) { + files.forEach { it.delete() } + folder.delete() + } + } + + @Throws(IOException::class) + private fun copyToShareCacheFolder(file: File): File { + val folder = shareCacheFolder + if (!folder.exists()) { + folder.mkdirs() + } + val newFile = File(folder, file.name) + file.copyTo(newFile, true) + return newFile + } +} diff --git a/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/SharePlusPlugin.kt b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/SharePlusPlugin.kt new file mode 100644 index 0000000000..e62703c599 --- /dev/null +++ b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/SharePlusPlugin.kt @@ -0,0 +1,44 @@ +package dev.fluttercommunity.plus.share + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding +import io.flutter.embedding.engine.plugins.activity.ActivityAware +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding +import io.flutter.plugin.common.MethodChannel + +/** Plugin method host for presenting a share sheet via Intent */ +class SharePlusPlugin : FlutterPlugin, ActivityAware { + private lateinit var share: Share + private lateinit var methodChannel: MethodChannel + + override fun onAttachedToEngine(binding: FlutterPluginBinding) { + methodChannel = MethodChannel(binding.binaryMessenger, CHANNEL) + share = Share(context = binding.applicationContext, activity = null) + val handler = MethodCallHandler(share) + methodChannel.setMethodCallHandler(handler) + } + + override fun onDetachedFromEngine(binding: FlutterPluginBinding) { + methodChannel.setMethodCallHandler(null) + } + + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + share.setActivity(binding.activity) + } + + override fun onDetachedFromActivity() { + share.setActivity(null) + } + + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + onAttachedToActivity(binding) + } + + override fun onDetachedFromActivityForConfigChanges() { + onDetachedFromActivity() + } + + companion object { + private const val CHANNEL = "dev.fluttercommunity.plus/share" + } +} From e86fc50f15f05a87d2c5e4b783ee320e4e247721 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Thu, 23 Dec 2021 14:40:38 +0200 Subject: [PATCH 5/9] Update changelog, update Gradle wrapper --- packages/share_plus/share_plus/CHANGELOG.md | 3 ++- .../example/android/gradle/wrapper/gradle-wrapper.properties | 3 +-- packages/share_plus/share_plus/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/share_plus/share_plus/CHANGELOG.md b/packages/share_plus/share_plus/CHANGELOG.md index dfc14c0880..0256390b35 100644 --- a/packages/share_plus/share_plus/CHANGELOG.md +++ b/packages/share_plus/share_plus/CHANGELOG.md @@ -1,5 +1,6 @@ -## 3.0.5 +## 3.1.0 +- Android: Migrate to Kotlin - Android: Update dependencies, build config updates - Example: Fix project title diff --git a/packages/share_plus/share_plus/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/share_plus/share_plus/example/android/gradle/wrapper/gradle-wrapper.properties index 6dd0229556..8d8786327e 100644 --- a/packages/share_plus/share_plus/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/share_plus/share_plus/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Tue Oct 05 15:14:03 EEST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/packages/share_plus/share_plus/pubspec.yaml b/packages/share_plus/share_plus/pubspec.yaml index 9ff2c4fe0c..858a9ce9b5 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: 3.0.5 +version: 3.1.0 homepage: https://plus.fluttercommunity.dev/ repository: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/ From a14a87765bf7d57c6d3ec74ad8e108b34b82a7d6 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Sat, 25 Dec 2021 20:38:58 +0200 Subject: [PATCH 6/9] Remove redundant sourceSets block --- packages/share_plus/share_plus/android/build.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/share_plus/share_plus/android/build.gradle b/packages/share_plus/share_plus/android/build.gradle index 121a6e128d..f2de2fad83 100644 --- a/packages/share_plus/share_plus/android/build.gradle +++ b/packages/share_plus/share_plus/android/build.gradle @@ -27,9 +27,6 @@ apply plugin: 'kotlin-android' android { compileSdkVersion 31 - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } defaultConfig { minSdkVersion 16 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From 869048943137be98075cb6327a8a04b80a788be4 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Sat, 25 Dec 2021 20:42:47 +0200 Subject: [PATCH 7/9] Update README --- packages/share_plus/share_plus/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/share_plus/share_plus/README.md b/packages/share_plus/share_plus/README.md index 7484d9a596..caadde26a0 100644 --- a/packages/share_plus/share_plus/README.md +++ b/packages/share_plus/share_plus/README.md @@ -1,10 +1,12 @@ [![Flutter Community: share_plus](https://fluttercommunity.dev/_github/header/share_plus)](https://github.com/fluttercommunity/community) +[![share_plus](https://github.com/fluttercommunity/plus_plugins/actions/workflows/share_plus.yaml/badge.svg)](https://github.com/fluttercommunity/plus_plugins/actions/workflows/share_plus.yaml) [![pub package](https://img.shields.io/pub/v/share_plus.svg)](https://pub.dev/packages/share_plus)

-

build
+
build

+ # Share plugin A Flutter plugin to share content from your Flutter app via the platform's @@ -61,7 +63,4 @@ Check out our documentation website to learn more. [Plus plugins documentation]( ### Mobile platforms (Android and iOS) -Due to restrictions set up by Facebook this plugin isn't capable of sharing data reliably to Facebook related apps on Android and iOS. This includes eg. sharing text to the Facebook Messenger. If you require this functionality please check the native Facebook Sharing SDK ([https://developers.facebook.com/docs/sharing](https://developers.facebook.com/docs/sharing)) or search for other Flutter plugins implementing this SDK. More information can be found in [this issue](https://github.com/fluttercommunity/plus_plugins/issues/413). - - -**Important:** As of January 2021, the Flutter team is no longer accepting non-critical PRs for the original set of plugins in `flutter/plugins`, and instead they should be submitted in this project. [You can read more about this announcement here.](https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md#important-note) as well as [in the Flutter 2 announcement blog post.](https://medium.com/flutter/whats-new-in-flutter-2-0-fe8e95ecc65) +Due to restrictions set up by Facebook this plugin isn't capable of sharing data reliably to Facebook related apps on Android and iOS. This includes eg. sharing text to the Facebook Messenger. If you require this functionality please check the native Facebook Sharing SDK ([https://developers.facebook.com/docs/sharing](https://developers.facebook.com/docs/sharing)) or search for other Flutter plugins implementing this SDK. More information can be found in [this issue](https://github.com/fluttercommunity/plus_plugins/issues/413). \ No newline at end of file From aa19f2c0276869f73996883ab8c0d30805772a06 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Mon, 27 Dec 2021 19:24:49 +0200 Subject: [PATCH 8/9] Update README --- packages/share_plus/share_plus/README.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/share_plus/share_plus/README.md b/packages/share_plus/share_plus/README.md index caadde26a0..f9a32ab6b7 100644 --- a/packages/share_plus/share_plus/README.md +++ b/packages/share_plus/share_plus/README.md @@ -1,18 +1,16 @@ +# Share plugin + [![Flutter Community: share_plus](https://fluttercommunity.dev/_github/header/share_plus)](https://github.com/fluttercommunity/community) [![share_plus](https://github.com/fluttercommunity/plus_plugins/actions/workflows/share_plus.yaml/badge.svg)](https://github.com/fluttercommunity/plus_plugins/actions/workflows/share_plus.yaml) [![pub package](https://img.shields.io/pub/v/share_plus.svg)](https://pub.dev/packages/share_plus) -

-

build
-

- -# Share plugin +build A Flutter plugin to share content from your Flutter app via the platform's share dialog. -Wraps the ACTION_SEND Intent on Android and UIActivityViewController +Wraps the `ACTION_SEND` Intent on Android and `UIActivityViewController` on iOS. ## Platform Support @@ -63,4 +61,4 @@ Check out our documentation website to learn more. [Plus plugins documentation]( ### Mobile platforms (Android and iOS) -Due to restrictions set up by Facebook this plugin isn't capable of sharing data reliably to Facebook related apps on Android and iOS. This includes eg. sharing text to the Facebook Messenger. If you require this functionality please check the native Facebook Sharing SDK ([https://developers.facebook.com/docs/sharing](https://developers.facebook.com/docs/sharing)) or search for other Flutter plugins implementing this SDK. More information can be found in [this issue](https://github.com/fluttercommunity/plus_plugins/issues/413). \ No newline at end of file +Due to restrictions set up by Facebook this plugin isn't capable of sharing data reliably to Facebook related apps on Android and iOS. This includes eg. sharing text to the Facebook Messenger. If you require this functionality please check the native Facebook Sharing SDK ([https://developers.facebook.com/docs/sharing](https://developers.facebook.com/docs/sharing)) or search for other Flutter plugins implementing this SDK. More information can be found in [this issue](https://github.com/fluttercommunity/plus_plugins/issues/413). From 750ab0d217806ab838b3c99fc8c7605ab92d1209 Mon Sep 17 00:00:00 2001 From: Volodymyr Buberenko Date: Sun, 20 Feb 2022 16:06:47 +0200 Subject: [PATCH 9/9] More Android updates --- packages/share_plus/share_plus/CHANGELOG.md | 1 + packages/share_plus/share_plus/android/build.gradle | 4 ++-- .../android/gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/gradle/wrapper/gradle-wrapper.properties | 2 +- packages/share_plus/share_plus/example/pubspec.yaml | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/share_plus/share_plus/CHANGELOG.md b/packages/share_plus/share_plus/CHANGELOG.md index d0d3d12c93..ac6f708106 100644 --- a/packages/share_plus/share_plus/CHANGELOG.md +++ b/packages/share_plus/share_plus/CHANGELOG.md @@ -3,6 +3,7 @@ - Android: Migrate to Kotlin - Android: Update dependencies, build config updates - Example: Fix project title +- Example: Set min Flutter version to 1.20.0 ## 3.0.5 diff --git a/packages/share_plus/share_plus/android/build.gradle b/packages/share_plus/share_plus/android/build.gradle index f2de2fad83..4f9ee5dee7 100644 --- a/packages/share_plus/share_plus/android/build.gradle +++ b/packages/share_plus/share_plus/android/build.gradle @@ -2,14 +2,14 @@ group 'dev.fluttercommunity.plus.share' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.5.32' + ext.kotlin_version = '1.6.10' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.4' + classpath 'com.android.tools.build:gradle:7.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/packages/share_plus/share_plus/android/gradle/wrapper/gradle-wrapper.properties b/packages/share_plus/share_plus/android/gradle/wrapper/gradle-wrapper.properties index 84d1f85fd6..41dfb87909 100644 --- a/packages/share_plus/share_plus/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/share_plus/share_plus/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/share_plus/share_plus/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/share_plus/share_plus/example/android/gradle/wrapper/gradle-wrapper.properties index 8d8786327e..c17f829cd3 100644 --- a/packages/share_plus/share_plus/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/share_plus/share_plus/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/packages/share_plus/share_plus/example/pubspec.yaml b/packages/share_plus/share_plus/example/pubspec.yaml index 3c86b71aee..0d843bb2ec 100644 --- a/packages/share_plus/share_plus/example/pubspec.yaml +++ b/packages/share_plus/share_plus/example/pubspec.yaml @@ -32,4 +32,4 @@ flutter: environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=1.9.1+hotfix.2" + flutter: ">=1.20.0"