diff --git a/packages/share_plus/share_plus/android/src/main/AndroidManifest.xml b/packages/share_plus/share_plus/android/src/main/AndroidManifest.xml index 8bbbe44d82..40801f1d83 100644 --- a/packages/share_plus/share_plus/android/src/main/AndroidManifest.xml +++ b/packages/share_plus/share_plus/android/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ + + + + + + + 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 index e2c1d3248b..95cfca5f06 100644 --- 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 @@ -33,7 +33,7 @@ internal class Share( */ private val immutabilityIntentFlags: Int by lazy { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_MUTABLE } else { 0 } @@ -71,7 +71,7 @@ internal class Share( PendingIntent.getBroadcast( context, 0, - Intent(ShareSuccessManager.BROADCAST_CHANNEL), + Intent(context, SharePlusPendingIntent::class.java), PendingIntent.FLAG_UPDATE_CURRENT or immutabilityIntentFlags ).intentSender ) @@ -129,7 +129,7 @@ internal class Share( PendingIntent.getBroadcast( context, 0, - Intent(ShareSuccessManager.BROADCAST_CHANNEL), + Intent(context, SharePlusPendingIntent::class.java), PendingIntent.FLAG_UPDATE_CURRENT or immutabilityIntentFlags ).intentSender ) diff --git a/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/SharePlusPendingIntent.kt b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/SharePlusPendingIntent.kt new file mode 100644 index 0000000000..284ccd507a --- /dev/null +++ b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/SharePlusPendingIntent.kt @@ -0,0 +1,39 @@ +package dev.fluttercommunity.plus.share + +import android.content.* +import android.os.Build + +/** + * This helper class allows us to use FLAG_MUTABLE on the PendingIntent used in the Share class, + * as it allows us to make the underlying Intent explicit, therefore avoiding any risks an implicit + * mutable Intent may carry. + * + * When the PendingIntent is sent, the system will instantiate this class and call `onReceive` on it. + */ +internal class SharePlusPendingIntent: BroadcastReceiver() { + companion object { + /** + * Static member to access the result of the system instantiated instance + */ + var result: String = "" + } + + /** + * Handler called after an action was chosen. Called only on success. + */ + override fun onReceive(context: Context, intent: Intent) { + // Extract chosen ComponentName + val chosenComponent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // Only available from API level 33 onwards + intent.getParcelableExtra(Intent.EXTRA_CHOSEN_COMPONENT, ComponentName::class.java) + } else { + // Deprecated in API level 33 + intent.getParcelableExtra(Intent.EXTRA_CHOSEN_COMPONENT) + } + + // Unambiguously identify the chosen action + if (chosenComponent != null) { + result = chosenComponent.flattenToString() + } + } +} 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 index b3b831ed10..d53a508ddb 100644 --- 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 @@ -15,14 +15,12 @@ class SharePlusPlugin : FlutterPlugin, ActivityAware { override fun onAttachedToEngine(binding: FlutterPluginBinding) { methodChannel = MethodChannel(binding.binaryMessenger, CHANNEL) manager = ShareSuccessManager(binding.applicationContext) - manager.register() share = Share(context = binding.applicationContext, activity = null, manager = manager) val handler = MethodCallHandler(share, manager) methodChannel.setMethodCallHandler(handler) } override fun onDetachedFromEngine(binding: FlutterPluginBinding) { - manager.discard() methodChannel.setMethodCallHandler(null) } diff --git a/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/ShareSuccessManager.kt b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/ShareSuccessManager.kt index 36e1a4c50e..3ac7116f0b 100644 --- a/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/ShareSuccessManager.kt +++ b/packages/share_plus/share_plus/android/src/main/kotlin/dev/fluttercommunity/plus/share/ShareSuccessManager.kt @@ -9,31 +9,18 @@ import java.util.concurrent.atomic.AtomicBoolean * Handles the callback based status information about a successful or dismissed * share. Used to link multiple different callbacks together for easier use. */ -internal class ShareSuccessManager(private val context: Context) : BroadcastReceiver(), - ActivityResultListener { +internal class ShareSuccessManager(private val context: Context) : ActivityResultListener { private var callback: MethodChannel.Result? = null private var isCalledBack: AtomicBoolean = AtomicBoolean(true) - /** - * Register listener. Must be called before any share sheet is opened. - */ - fun register() { - context.registerReceiver(this, IntentFilter(BROADCAST_CHANNEL)) - } - - /** - * Deregister listener. Must be called before the base activity is invalidated. - */ - fun discard() { - context.unregisterReceiver(this) - } - /** * Set result callback that will wait for the share-sheet to close and get either * the componentname of the chosen option or an empty string on dismissal. */ fun setCallback(callback: MethodChannel.Result): Boolean { return if (isCalledBack.compareAndSet(true, false)) { + // Prepare all state for new share + SharePlusPendingIntent.result = "" isCalledBack.set(false) this.callback = callback true @@ -51,7 +38,7 @@ internal class ShareSuccessManager(private val context: Context) : BroadcastRece * Must be called if `.startActivityForResult` is not available to avoid deadlocking. */ fun unavailable() { - returnResult("dev.fluttercommunity.plus/share/unavailable") + returnResult(RESULT_UNAVAILABLE) } /** @@ -70,7 +57,7 @@ internal class ShareSuccessManager(private val context: Context) : BroadcastRece */ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { return if (requestCode == ACTIVITY_CODE) { - returnResult("") + returnResult(SharePlusPendingIntent.result) true } else { false @@ -78,16 +65,11 @@ internal class ShareSuccessManager(private val context: Context) : BroadcastRece } /** - * Handler called after a sharesheet was closed. Called only on success. + * Companion object holds constants used throughout the plugin when attempting to return + * the share result. */ - override fun onReceive(context: Context, intent: Intent) { - returnResult( - intent.getParcelableExtra(Intent.EXTRA_CHOSEN_COMPONENT).toString() - ) - } - companion object { - const val BROADCAST_CHANNEL = "dev.fluttercommunity.plus/share/success" const val ACTIVITY_CODE = 17062003 + const val RESULT_UNAVAILABLE = "dev.fluttercommunity.plus/share/unavailable" } } diff --git a/packages/share_plus/share_plus/android/src/main/res/xml/flutter_share_file_paths.xml b/packages/share_plus/share_plus/android/src/main/res/xml/flutter_share_file_paths.xml index 4e78448e9e..cb820aec00 100644 --- a/packages/share_plus/share_plus/android/src/main/res/xml/flutter_share_file_paths.xml +++ b/packages/share_plus/share_plus/android/src/main/res/xml/flutter_share_file_paths.xml @@ -1,4 +1,5 @@ - - + + +