Skip to content

Commit

Permalink
fix(share_plus): return correct share result on android (#1301)
Browse files Browse the repository at this point in the history
  • Loading branch information
Coronon committed Oct 31, 2022
1 parent 48cc832 commit 994fef7
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 33 deletions.
@@ -1,6 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.fluttercommunity.plus.share">
<application>
<!-- Declares a provider which allows us to store files to share in
'.../caches/share_plus' and grant the receiving action access -->
<provider
android:name="dev.fluttercommunity.plus.share.ShareFileProvider"
android:authorities="${applicationId}.flutter.share_provider"
Expand All @@ -10,5 +12,12 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/flutter_share_file_paths"/>
</provider>
<!-- This manifest declared broadcast receiver allows us to use an explicit
Intent when creating a PendingItent to be informed of the user's choice -->
<receiver android:name=".SharePlusPendingIntent" android:exported="true">
<intent-filter>
<action android:name="EXTRA_CHOSEN_COMPONENT" />
</intent-filter>
</receiver>
</application>
</manifest>
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -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
)
Expand Down
@@ -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<ComponentName>(Intent.EXTRA_CHOSEN_COMPONENT)
}

// Unambiguously identify the chosen action
if (chosenComponent != null) {
result = chosenComponent.flattenToString()
}
}
}
Expand Up @@ -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)
}

Expand Down
Expand Up @@ -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
Expand All @@ -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)
}

/**
Expand All @@ -70,24 +57,19 @@ 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
}
}

/**
* 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<ComponentName>(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"
}
}
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="share_plus/" />
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Used in conjunction with the provider declared in AndroidManifest.xml -->
<cache-path name="cache" path="share_plus/" />
</paths>

0 comments on commit 994fef7

Please sign in to comment.