How to send Intent.ACTION_SEND inside BroadcastReceiver's onReceive? - android

I want to add an action to my notification, when the user clicks the button, it shares the text of the notification with other apps, here is my code:
class NotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val message = intent?.getStringExtra("sharedMessage")
val shareIntent: Intent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_TEXT, message)
type = "text/plain"
}
shareIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
if (context != null) {
if (shareIntent.resolveActivity(context.packageManager) != null) {
context.startActivity(shareIntent)
}
}
}
}
The onReceive function can receive the click action, but it does not start my shareIntent which should prompt the user to choose an app for sharing. What is the issue?

There are restrictions on background processes (Service and BroadcastReceiver) launching activities. See https://developer.android.com/guide/components/activities/background-starts
This is probably why you aren't seeing the Activity launch.
However, why are you doing this in such a roundabout way? If you want an Activity launched when the user clicks on the notification, just do that directly instead of having the notification click start a BroadcastReceiver.

Related

Is there any way to kill an activity when its in picture-in-picture mode from another activity?

I have two Activities ActivityA and ActivityB (this one has pip mode enabled)
when ActivityB is in pip mode,ActivityA comes to the foreground now I want to finish/destroy/kill ActivityB from ActivityA is there any way to do this?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val supportsPIP = context!!.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
if (supportsPIP) {
enterPictureInPictureMode(mPictureInPictureParamsBuilder!!.build())
}
}
}
After checking this answer https://stackoverflow.com/a/56896347/13373099 I realized that All I had to do was just use LocalBroadcastManager
if anyone having trouble implementing this, here is what I did
in ActivityB
private val mReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
intent?.let { intent ->
if (intent.action == "FINISH_ACTIVITY") {
finish(); // finish/kill activity also destroys the pip
}}
}
now register the listener
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, IntentFilter("FINISH_ACTIVITY));
in ActivityA
just send a broadcast with intent action "FINISH ACTIVITY"
val intent = Intent("FINISH_ACTIVITY")
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
Another way it's to save the reference to the activity in a singleton and when you want to kill it you call finish() and the set the reference to null again.

How to send a reply from a notification?

I'm doing an app that manages the received notifications. Currently, I'm implementing a feature that the user can reply through the reply action, but I couldn't find a way to properly set the reply message and send the message.
Here is what I have tried
fun sendReplyMessage(sbn: StatusBarNotification, replyMessage: String) {
sbn.notification.actions.firstOrNull { it.remoteInputs != null }?.let { action ->
action.remoteInputs?.get(0)?.extras
?.putCharSequence(action.remoteInputs?.get(0)?.resultKey, replyMessage)
action.actionIntent.send()
}
}
You have to get the notification action to access the pending intent, add the remote input on this intent, and then, call the method PendingIntent#send(context, requestCode, intent)
val notificationAction: android.app.Notification.Action = "Get the Action here"
val bundle = Bundle().apply{
putString(remoteInput.resultKey, "Add the text here")
}
val intent = Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
RemoteInput.addResultsToIntent(notificationAction.getRemoteInputs(), intent, bundle)
notificationAction.actionIntent.send(context, 0, intent)

Intent service can't be started in background

In my android app I want to detect activity change from still to walking and start tracking location, regardless of the state of the app (in background or shut down completely).
I was able to create location tracking service which works while app in background by making it a foreground service (showing notification), but I have not been able to start tracking based on activity detection.
This is fragment of code of IntentService, which supposed to start location tracking service, after receiving intent with activity transition detected:
class ActivityDetectionIntent : IntentService(TAG) {
override fun onHandleIntent(intent: Intent?) {
val i = Intent(this#ActivityDetectionIntent, LocationTracking::class.java)
if (Build.VERSION.SDK_INT >= 26) {
startForegroundService(i)
// this followed by foregroundService call in LocationTracking service
} else {
startService(i)
}
}
// ...
}
This is the error message I'm getting:
2019-12-04 19:57:59.797 3866-15015/? W/ActivityManager: Background start not
allowed: service Intent { cmp=com.anatoliymakesapps.myapplication/.ActivityDetectionIntent
(has extras) } to com.anatoliymakesapps.myapplication/.ActivityDetectionIntent
from pid=-1 uid=10377 pkg=com.anatoliymakesapps.myapplication startFg?=false
I wonder if I miss something obvious, or maybe this whole approach is wrong and I need to try something else? Any piece of advice to achieve the desired result is appreciated.
I tried changing IntentService to JobIntentService but it made no difference, error looks the same.
Turns out intent service can not be started directly, but with help of broadcast receiver it can be achieved indirectly.
This is what I used instead of IntentService:
class ActivityTransitionBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.i(TAG, "got activity transition signal")
val i = Intent(context, LocationTrackingService::class.java)
if (Build.VERSION.SDK_INT >= 26) {
startForegroundService(context, i)
} else {
context.startService(i)
}
}
companion object {
private val TAG = ActivityTransitionBroadcastReceiver::class.java.simpleName
}
}
manifest:
<receiver android:name=".ActivityTransitionBroadcastReceiver" android:exported="true" />

How to handle createChooser's IntentSender without having the component class of the receiver Intent

I am trying to handle the IntentSender of Intent.createChooser() to do something when a user selects an app to share an image on. Most the examples I've found here (posted below), require using a BroadcastReceiver as follows:
Intent receiver = new Intent(context, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, receiver, PendingIntent.FLAG_UPDATE_CURRENT);
String type = "image/*";
Intent share = new Intent(Intent.ACTION_SEND);
share.setType(type);
share.putExtra(Intent.EXTRA_STREAM, awesome_photo_uri);
startActivity(Intent.createChooser(share, "some_title", pendingIntent.getIntentSender()));
My problem with this solution, is located in this line:
Intent receiver = new Intent(context, MyReceiver.class);
The Intent constructor used in these examples require me to make a static MyReceiver class, so I have a class to pass into the second argument of the constructor. But, this causes an issue because I'd like the BroadcastReceiver's onReceive to do stuff in my Fragment. Therefore, I would prefer to create a BroadcastReceiver dynamically in my Fragment.
To no avail, I attempted the following work-around:
Inside MyFragment.kt:
private val receiver: BroadcastReceiver = getBroadcastReceiver()
private val intentFilter = IntentFilter("com.my.app.CHOOSER_ACTION")
override fun onResume() {
requireContext().registerReceiver(receiver, intentFilter)
super.onResume()
}
override fun onPause() {
requireContext().unregisterReceiver(receiver)
super.onPause()
}
private fun shareImage(imageFile: File) {
Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Intent.ACTION_SEND
type = "image/*"
putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(
requireContext(),
"${myPackageName}.fileprovider",
imageFile
))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
val receiver = Intent("com.my.app.CHOOSER_ACTION")
val pendingIntent = PendingIntent.getBroadcast(requireContext(), 0, receiver, PendingIntent.FLAG_CANCEL_CURRENT)
startActivity(Intent.createChooser(this, "Share image using", pendingIntent.intentSender))
} else {
startActivity(Intent.createChooser(this, "Share image using"))
}
}
}
private fun getBroadcastReceiver() : BroadcastReceiver {
return object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.extras?.keySet()?.forEach {
Log.v("MyTest", "$it: ${intent.extras?.get(it)}")
}
doSomethingInMyFragment()
}
}
}
Inside AndroidManifest.xml:
<activity android:name="MyActivityThatHasMyFragment" />
<intent-filter>
<action android:name="com.my.app.CHOOSER_ACTION" />
</intent-filter>
</activity>
Unfortunately, the dynamic BroadcastReceiver's onReceive() function is never called after the user presses on a selection. A few questions:
Why does this not work? What am I missing? Am I somehow setting the Intent or IntentFilter incorrectly?
Is it even possible to use a dynamic BroadcastReceiver for handling the IntentSender of createChooser? If not, how can I create a static BroadcastReceiver that triggers something to happen in MyFragment?
Resources:
Get IntentSender object for createChooser method in Android
Get results from Android Chooser
How to tell which app was selected by Intent.createChooser?
What is the purpose of IntentSender?
Intent.createChooser()

Check which url is opened in custom chrome tabs

Is there any function in chrome custom tabs analogous to onPageStarted of Webview. IN onNavigation.. the bundle is always null
By design this is not possible with Chrome Custom Tabs. You can tell that a user has navigated but you can't tell where they've gone to. See: http://developer.android.com/reference/android/support/customtabs/CustomTabsCallback.html for details of what's possible.
You can see what URL is currently open in Chrome Custom Tabs if you can get the user to trigger a PendingIntent by clicking on a toolbar action button or a menu option.
In your fragment/activity, create a nested BroadcastReceiver class that will handle the incoming intent in it's onReceive() method:
class DigBroadcastReceiver() : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val uri: Uri? = intent.data
if (uri != null) {
Log.d("Broadcast URL",uri.toString())
main.genericToast(uri.toString())
}
}
}
Add the receiver to your manifest file:
<receiver
android:name=".ui.dig.DigTabs$DigBroadcastReceiver"
android:enabled="true" />
Create the PendingIntent and add it to your CustomTabsIntent.Builder:
val sendLinkIntent = Intent(main,DigBroadcastReceiver()::class.java)
sendLinkIntent.putExtra(Intent.EXTRA_SUBJECT,"This is the link you were exploring")
val pendingIntent = PendingIntent.getBroadcast(main,0,sendLinkIntent,PendingIntent.FLAG_UPDATE_CURRENT)
// Set the action button
AppCompatResources.getDrawable(main, R.drawable.close_icon)?.let {
DrawableCompat.setTint(it, Color.WHITE)
builder.setActionButton(it.toBitmap(),"Add this link to your dig",pendingIntent,false)
}
val customTabsIntent: CustomTabsIntent = builder.build()
customTabsIntent.launchUrl(main, Uri.parse(url))
See my article explaining this on Medium.

Categories

Resources