Receiving explicit intents while activity is in the foreground - android

My app has one activity and one service. The service starts a foreground notification that has an action button that in some situations needs to tell the activity to do something besides just coming back to the front. It uses the Extras to indicate this something.
My issues are that I can't find any documentation on how to receive explicit intents other than through the "bundle" passed to onCreate() which isn't usually called because the activity could already be created.
How do you receive an intent after onCreate()?
Notification code snippet:
val actionIntent = Intent(this, MainActivity::class.java)
actionIntent.action = actionText
actionIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
val pendingActionIntent: PendingIntent = PendingIntent.getActivity(this, 0, actionIntent, 0)
val actionCancel: NotificationCompat.Action = NotificationCompat.Action.Builder(R.drawable.ic_cancel_black_24dp,
actionText,
pendingActionIntent).build()
val notificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle(getText(R.string.notification_title))
.setSmallIcon(R.drawable.ic_logo_24dp)
.setContentIntent(pendingIntent)
.setOnlyAlertOnce(true)
.addAction(actionCancel)
.setContentText(text)
startForeground(ONGOING_NOTIFICATION_ID, notificationBuilder.build())

Override onNewIntent(). If the activity already exists, and an Intent is bringing it back to the foreground, that Intent is delivered to onNewIntent().

Related

Activity get destroyed when clicking his push notification

I have an Activity, which has a fragment. In that fragment, I launch a service witch I need to do some stuff in the background. When some condition come to true in the service, I create a push notification, where i specify to reopen the activity (and load the fragment) when the notification is clicked.
The problem is that when i click the notification, the activity is destroyed and recreated again, and on the onDestroy method of the activity, I shut down the service (i want to kill it when the user close the app), so the service is no more running anymore.
How can i prevent the activity to be destroyed and recreated but simply resumed (like moving my self through the multitasking)?
How i create the notification:
val intent = Intent(context, SplashScreenActivity::class.java)
val pendingIntent = PendingIntent.getActivity(context, 0, intent, FLAG_ACTIVITY_SINGLE_TOP)
val builder = NotificationCompat.Builder(
ApplicationServices.instance.applicationContext,
channelId
)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(message)
.setStyle(NotificationCompat.BigTextStyle().bigText(message))
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
``
Try this:
// Activity Intent
val intent = Intent(context, SplashScreenActivity::class.java).apply {
// Note: Add flag(s) for Activity here
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
}
// Pending Intent
val pendingIntent = PendingIntent.getActivity(
context,
// Note: Use a non-zero positive Integer here, it's good practice ;-)
System.currentTimeMillis().toInt(),
intent,
// Note: Add flag(s) for Pending Intent here
PendingIntent.FLAG_UPDATE_CURRENT
)
// Notification Builder
val builder = NotificationCompat.Builder(
ApplicationServices.instance.applicationContext,
channelId
)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(message)
.setStyle(NotificationCompat.BigTextStyle().bigText(message))
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationCompat.PRIORITY_HIGH)
// Using Pending Intent
.setContentIntent(pendingIntent)
.setAutoCancel(true)

Pass data with PendingIntent and retrieve with onNewIntent when app launches

I have an Android application which will receive push notification. The notification have wearable support hence the notification will also be visible in Android wear with action button.
I am looking to pass data when the notification reaches onMessageReceived in FirebaseMessagingService class. Tried setting data in Intent and passed that with Pending Intent.
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.data = Uri.parse("12345")
intent.putExtra("user_id", "USER ID")
intent.putExtra("date", "DATE")
val pendingIntent = PendingIntent.getActivity(
applicationContext,
System.currentTimeMillis().toInt() , intent,
0
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel =
NotificationChannel(CHANNEL_ID,
CHANNEL_NAME,
CHANNEL_IMPORTANCE)
val notificationManager =
this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(notificationChannel)
val notificationBuilder = NotificationCompat.Builder(this,CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText(body)
.setAutoCancel(true)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.extend(NotificationCompat.WearableExtender()
.addAction(NotificationCompat.Action(R.drawable.icon,
"Explore",
pendingIntent))
)
notificationManager.notify(0, notificationBuilder.build())
}
When click on the notification from Wear, capturing the intent in onNewIntent to get the data passed. But couldn't find the data which were passed.
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent)
// intent.getStringExtra("date")
// intent.extras.getString("date")
// intent.extras.get("date")
// intent.data
}
Couldn't get the data. Is there any way to get the intent passed with the pending intent ? Or how to get the values passed with Pending Intent.
My assumption is that onNewIntent() is not being called.
If you specify FLAG_ACTIVITY_CLEAR_TOP and your app is already running and there is an instance of MainActivity in the task stack, Android will remove the existing instance of MainActivity and create a new one. The Intent with "extras" will then be delivered in onCreate() and onNewIntent() will not be called.
If your app is not running, tapping the notification will start your app and create a new instance of MainActivity and the Intent with the "extras" will be delived in onCreate() and onNewIntent() will not be called.
Also, since the Intent that you put in the PendingIntent will be used by Android to launch an Activity from a non-Activity context, you need to specify Intent.FLAG_ACTIVITY_NEW_TASK.
Also, you should always specify FLAG_UPDATE_CURRENT when getting a PendingIntent with "extras", because if you do not, you may get an existing PendingIntent with some old "extras" or maybe none at all. Using FLAG_UPDATE_CURRENT will ensure that your Intent "extras" are copied into the PendingIntent even if it already exists. Like this:
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.data = Uri.parse("12345")
intent.putExtra("user_id", "USER ID")
intent.putExtra("date", "DATE")
val pendingIntent = PendingIntent.getActivity(
applicationContext,
System.currentTimeMillis().toInt() , intent,
PendingIntent.FLAG_UPDATE_CURRENT
)

PendingIntent.FLAG_UPDATE_CURRENT recreates Activity

I have a notification and when I tap it I want to launch application if it is still not running, but if application already running, I do not want to relaunch it.
So, I am using PendingIntent.FLAG_UPDATE_CURRENT flag when creating PendingIntent.
My code:
private val notificationManager by lazy { NotificationManagerCompat.from(this) }
fun testPush() {
val notificationBuilder = NotificationCompat.Builder(this, BuildConfig.APPLICATION_ID)
.setSmallIcon(R.drawable.ill_launcher)
notificationBuilder
.setContentTitle("Title")
.setContentText("Test text")
.setContentIntent(buildPendingIntent())
notificationBuilder
.setAutoCancel(true)
.priority = NotificationCompat.PRIORITY_DEFAULT
notificationManager.notify(1, notificationBuilder.build())
}
private fun buildPendingIntent(): PendingIntent {
val intent = Intent(this, RootActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
intent.putExtra("action", RootActivity.DEFAULT_INTENT)
return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
But when I launch the application and click on the notification, the activity is recreates.
Instead of building the Intent for RootActivity like you are doing, you need to create a "launch Intent" for your application. The easiest way to do this is to call PackageManager.getLaunchIntentForPackage() and pass your own package name. Use the returned Intent in the call to PendingIntent.getActivity().
This will launch your app if it isn't running, otherwise, if it is already running, it will just bring the existing task containing your app to the foreground.

Detect Activity Launch

I am using an external library in our project. That library showing notification. On tapping that notification starts activity in the library.
I want to detect that activity launch from push notification to track some analytics data.
Is there any way to detect those notification taps or activity launch?
As far as I understand you activity is already launched on notification tap. For detecting activity launch you can use ActivityLifecycleCallbacks. In such case you will need to override onActivityCreated/onActivityStarted which includes created/started activity as argument. You can inject analytics component inside and send events about launched activities.
class AppLifecycleCallbacks : ActivityLifecycleCallbacks {
override fun onActivityStarted(activity: Activity) {
if (activity is MyActivity) {
//...
}
}
//...
}
Pass some extra meta data along with your pending intent generated for showing notification. And parse same in your destination activity.
Intent destination = new Intent(context, HomeActivity.class);
destination.putExtra("SOURCE","NOTIFICATION");
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, destination, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
.setContentTitle("Notification Title")
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setContentIntent(pendingIntent)
.setContentInfo("App")
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher))
.setColor(context.getColor(R.color.colorAccent))
.setLights(Color.RED, 1000, 300)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setSmallIcon(R.drawable.ic_like);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
And in activity level :
if(getIntent().getStringExtra("SOURCE").equals("NOTIFICATION")){
// launched from notification
}
If your activity is already running your intent might get delivered to :
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
Of course, yes. When you create Intent for launching your target activity you can pass arguments to the intent and when Activity starts just obtain outer arguments from intent and if these arguments came from Notification do your actions need.

Direct reply notification starts an activity

I am trying to get the text typed on the direct reply. I can get the text but when click the send text button, it opens the activity that intent shows.
val resultIntent = Intent(this, MessagesActivity::class.java)
val stackBuilder = TaskStackBuilder.create(this)
stackBuilder.addNextIntent(resultIntent)
val resultPendingIntent = PendingIntent.getActivity(
this,
0,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
And here's Notification Builder
val mBuilder = Notification.Builder(this, id)
.setContentTitle(data["title"])
.setContentText(data["body"])
.setLargeIcon(image)
.addAction(action)
.setSmallIcon(R.drawable.logo)
.setAutoCancel(true)
I don't want it to open the activity. Also I tried to use a intent service, it does not work.
You are using PendingIntent.getActivity which means you want to handle result from your operation on activity. It's logical that this activity need to start before proceeding.
From documentation of getActivity:
Retrieve a PendingIntent that will start a new activity, like calling
Context.startActivity(Intent). Note that the activity will be started
outside of the context of an existing activity, so you must use the
Intent.FLAG_ACTIVITY_NEW_TASK launch flag in the Intent.
You need to use PendingIntent.getBroadcast.
For more information look here

Categories

Resources