I need to know what the user selected in order to share content for analytics. For this, I use the Google ShareSheet.
To obtain the value selected by the user from the Google ShareSheet, I use a PendingIntent. Here's how I've implemented it:
...
override fun doOpenShareSheet(
activity: AppCompatActivity
) {
var flag: Int = PendingIntent.FLAG_UPDATE_CURRENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
flag = PendingIntent.FLAG_IMMUTABLE
}
val pi: PendingIntent = PendingIntent.getBroadcast(
activity,
0,
Intent(activity, ChooserBroadcastReceiver::class.java),
flag
)
// Implement Google ShareSheet
val share: Intent = Intent.createChooser(
Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, config.url)
// Passing content URI to image to be displayed
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
type = "application/url"
},
config.mediaTitle,
pi.intentSender
)
activity.startActivity(share)
}
...
Now, to get the value, I need to register the BroadcastReceiver
dynamically (as I cannot pass the value to the receiver's constructor in the Manifest).
Here's what I have:
...
class ChooserBroadcastReceiver(private val vm: MyViewModel): BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val selectedComponent: String = intent?.extras?.get(Intent.EXTRA_CHOSEN_COMPONENT).toString()
vm.doOnChooserBroadcastReceiver(selectedComponent = selectedComponent)
}
}
....
@AndroidEntryPoint
class MyFragment : Fragment() {
...
override fun onResume() {
super.onResume()
registerBroadcast()
}
private fun registerBroadcast() {
chooserBroadcastReceiver = ChooserBroadcastReceiver(vm = vm)
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_SEND)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requireActivity().registerReceiver(chooserBroadcastReceiver, intentFilter, RECEIVER_EXPORTED)
} else {
requireActivity().registerReceiver(chooserBroadcastReceiver, intentFilter)
}
}
...
}
However, for some reason, I'm not receiving the value. I can see that the event is happening, and I can choose how to share the content through the Google ShareSheet, but the event is not received in onReceive.
P.S. For the test, I tried removing the view model from the constructor and registered the receiver through the manifest, and everything worked. Here's how:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<receiver
android:name=".ui.MyBroadcastReceiver"
android:exported="false">
</receiver>
</application>
</manifest>
But I need to pass a parameter to the constructor, so this method doesn't work for me.
What am I doing wrong? Why does it work when registered through the manifest, but the event is not received when registered dynamically in the code?
I cannot pass the value to the receiver's constructor in the Manifest
Putting a ViewModel
in the receiver's constructor is kinda weird. Regardless, your PendingIntent
and your IntentFilter
do not match.
val pi: PendingIntent = PendingIntent.getBroadcast(
activity,
0,
Intent(activity, ChooserBroadcastReceiver::class.java),
flag
)
This says "look in the manifest for ChooserBroadcastReceiver
and send the broadcast to it".
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_SEND)
This says "respond to ACTION_SEND
broadcasts" (which itself is weird, as ACTION_SEND
is usually an activity action).
You need those things to match. If you do not wish to register your receiver in the manifest, then you need to use a PendingIntent
that creates an Intent
that matches your IntentFilter
.
val pi: PendingIntent = PendingIntent.getBroadcast(
activity,
0,
Intent(Intent.ACTION_SEND),
flag
)
Also, please note that on most versions of Android, any app on the system can send a broadcast to that receiver, as it will be exported.
But I need to pass a parameter to the constructor,
You could use dependency inversion (Dagger/Hilt, Koin, etc.). That may be difficult to get a suitable Jetpack ViewModel
, but you could inject a repository or use case or something.