I have an app that has a background service running that listens for events.
One of the events should unlock the phone and bring the app to the foreground.
What approaches here are possible?
I was thinking, would it be possible to send a local notification that is actually high priority so it opens the app automatically?
Currently I try to open apps activity this way:
private fun getIntent(pin: String): Intent = Intent(context, XActivity::class.java).apply {
putExtra(XActivity.EXTRA_SMTH, x)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
private fun showActivity(x: String) {
val intent = getIntent(x)
context.startActivity(intent)
}
This code piece works alright if the app is in the foreground, but does not if the app is in the background.
Any ideas/solutions are welcomed.
At first, if you listened for ACTION_SCREEN_ON or ACTION_SCREEN_ON, make sure to explicitly set your listeners ref.
Secondly, due to background restriction, you cannot start an Activity from background. You have to start a foreground service which you will start when the receiver receives the event. From that service, you can launch your activity with your desired intent.
Foreground service needs a notification. Inside your service, create a notification with your intent like following and call startForeground() with this notification. Also create and register NotificationChannel before if not already.
val fullScreenIntent = Intent(this, XActivity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(this, 0,
fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val notificationBuilder =
NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("Launch Activity")
.setContentText("Tap to launch Activity")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_ALARM) // Set your desired category
// Use a full-screen intent only for the highest-priority alerts where you
// have an associated activity that you would like to launch after the user
// interacts with the notification. Also, if your app targets Android 10
// or higher, you need to request the USE_FULL_SCREEN_INTENT permission in
// order for the platform to invoke this notification.
.setFullScreenIntent(fullScreenPendingIntent, true)
val alarmNotification = notificationBuilder.build()
Related
i am trying to implement notifications in my android application.
when the user clicks on the notification i run the activity if it isn't already running .. but if the activity is already running i don't want to recreate it, i just want to refresh the data by intercepting the intent from the onNewIntent function.
the problem is that every time I click on the notification, the activity is recreated, and the onNewIntent function is not called.
the problem occurs when I create the notification from a service.
but when I create the notification from the same activity that I am going to run, everything works fine.
I searched the internet and tried all the solutions I found, but it still doesn't work
I tried several combinations of intent flags, but nothing works
this is my code
val intent : Intent = Intent(this, HomeActivity::class.java)
finalIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
val pendingIntentFlags : Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
{
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
}
else
{
PendingIntent.FLAG_UPDATE_CURRENT
}
val pendingIntent : PendingIntent? = TaskStackBuilder.create(context).run {
addNextIntentWithParentStack(finalIntent)
getPendingIntent(0, pendingIntentFlags)
}
val notification = NotificationCompat.Builder(context, type.channelId)
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(Notification.CATEGORY_SERVICE)
.setDefaults(Notification.DEFAULT_ALL)
.setAllowSystemGeneratedContextualActions(true)
.setOnlyAlertOnce(true)
.setAutoCancel(true)
.setLocalOnly(true)
.setOngoing(false)
.setSilent(false)
.setSmallIcon(R.drawable.icon_notification)
.setContentIntent(pendingIntent)
.build()
notificationManager?.notify(NOTIFICATION_ID, notification)
Let your notification send a broadcast.
In your activity you should register a dynamic broadcast receiver.
This will allow you to refresh the content in the activity without reloading it.
For the case that the activity is not running, you should define a broadcast receiver in the manifest which starts the activity.
when your activity starts, disable the broadcast receiver and re-enable it when your activity stops.
You can just set launch mode attribute to singleInstance in the manifest on your activity and start it with FLAG_ACTIVITY_NEW_TASK only.
I'm building a video calling app and need to display an incoming call screen. I use a Notification that displays an activity with fullscreen intent mechanism as follows:
private fun createIncomingCallNotification(): Notification {
val fullscreenPendingIntent = IncomingCallActivity.intent() // from splitties.intents module
.toPendingActivity(flags =
PendingIntent.FLAG_UPDATE_CURRENT or
Intent.FLAG_ACTIVITY_CLEAR_TASK or
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_NO_HISTORY or
Intent.FLAG_ACTIVITY_CLEAR_TOP
)
return NotificationCompat.Builder(this, INCOMING_CALL_NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_baseline_call_24)
.setContentTitle("Incoming call")
.setContentText("(919) 555-1234")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setFullScreenIntent(fullscreenPendingIntent, true)
.setContentIntent(
VideoCallActivity.intent()
.toPendingActivity(flags =
Intent.FLAG_ACTIVITY_CLEAR_TASK or
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_NO_HISTORY or
Intent.FLAG_ACTIVITY_NO_HISTORY or
Intent.FLAG_ACTIVITY_CLEAR_TOP
)
)
.setAutoCancel(true)
.setVibrate(DEFAULT_VIBRATE_PATTERN)
.setSound(incomingCallSound)
.build()
}
It is used as a notification for a foreground service which is triggered when the receiver receives an incoming call request.
The fullscreen activity and notification display OK. However, when I accept the call by clicking on the notification, fullscreen activity remains in the history stack (even with all these flags added plus the autoCancel). So then, when I end my call the fullscreen IncomingCallActivity pops up. It remains even when I stop the foreground service associated with the notification.
Any help would be appreciated!
TL;DR:
I'm looking for a auto-cancel-like behavior for a fullScreenIntent accompaniying an annotation. As it's for the system to decide whether the activity is shown, I'd like to have a generic approach to cancel the notification and close the fullscreen activity on any action selection (reject or accept the call). I suspect it should be done with a BroadcastReceiver, but looking for tips on how it should be done in a concise manner.
EDIT:
I've managed to solve this with BroadcastReceiver registered within the fullscreen activity with a IntentFilter. In the notification reject action I just sendBroadcast which then finishes the fullscreen activity.
However still looking for a 'best practices' approach on that.
I have a foreground service that shows an ongoing notification while running.
Now, it's a streaming app and I want the user to be notified when the stream gets broken (e.g. internet connection dropped). I can't use the main app because the stream can be going while other apps are active. So I need to send a notification to the user from the Foreground Service I use for streaming. The problem is, the notification is not being displayed.
Here's the code I'm currently using:
// registering notification channels
private fun createNotificationChannels() {
val serviceChannel = NotificationChannel(
NOTIFICATION_CHANNEL_ID_SERVICE,
NOTIFICATION_CHANNEL_NAME_SERVICE,
NotificationManager.IMPORTANCE_DEFAULT
)
val appChannel = NotificationChannel(
NOTIFICATION_CHANNEL_ID_APP,
NOTIFICATION_CHANNEL_NAME_APP,
NotificationManager.IMPORTANCE_HIGH
).apply {
enableVibration(true)
}
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannels(mutableListOf(serviceChannel, appChannel))
}
// starting the service with a required notification
startForeground(
nextInt(100000),
NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID_SERVICE)
.setSmallIcon(R.drawable.recording_notification)
.setContentText("Stream is in progress...")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build(),
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
)
// letting the user know that stream crashed
private fun sendDisconnectNotification() {
val intent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val builder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID_APP)
.setSmallIcon(R.drawable.disconnected_notification)
.setContentTitle("The stream stopped unexpectedly!")
.setContentText("Please check your internet connection.")
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setDefaults(NotificationCompat.DEFAULT_ALL)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.notify(nextInt(100000), builder.build())
}
I know that sendDisconnectNotification() is being called (put logs there) but the notification never appears.
I was changing so many things that it's hard to specify every piece of code that I tried. But some of the important things I tried were changing the priorities for channels/notifications and sending the notifications in the same/different channels. I also uninstall the app and reboot the phone after every change to make sure the notification settings are applied.
Nothing has been working so far and it got me thinking it's impossible to do. I think that the foreground service only allows one notification to be displayed (the main ongoing one).
Can someone confirm this or give some advice on how to make it work?
I can provide some more code samples if needed.
OK, I’m feeling kinda dumb but it turned out “Do Not Disturb” was turned on on the device. That was the reason the notifications were not visible. Writing this as an answer in case someone like me forgets to turn off DND and finds this SO question.
I'm working on an android application where i need to show a time-sensitive notification in a specific situation.
Normaly our app is running in the background and when the phone is in use the heads up notification is displayed correctly and when the user interacts with it, the correct activity is started.
When the phone is not in use (e.g. locked) the fullScreenIntent works and the correct activity is started.
In the case the user is working with our app at the moment the notification appears, i would like to lead directly to the activity i set as the fullScreenIntent. But at the moment also the head up notification is shown and the activity doesn't start automatically. Is there a way i can make my activity to be started when our app is currently being used by the user?
I build my notification like this and it is used for a foreground service.
val pendingCancelIntent = getCancelPendingIntent()
val pendingFinishIntent = getFinishPendingIntent()
val fullScreenPendingIntent = getFullScreenPendingIntent()
val notificationBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_icon)
.setContentTitle(getString(R.string.title))
.setContentText(getString(R.string.text))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setFullScreenIntent(fullScreenPendingIntent, true)
notificationBuilder.addAction(
R.drawable.ic_cancel,
getString(R.string.general_cancel),
pendingCancelIntent
)
notificationBuilder.addAction(
R.drawable.ic_finish,
getString(R.string.finish),
pendingFinishIntent
)
notificationBuilder.build()
I have 5 activities in my app. Every activity starts the same foreground service.
In onStartCommand method of the service foreground notification is created which unfortunately means that every call of startForegroundService() in any activity plays notification sound (even though the service is already running). How can I create the foreground notification only once or at least how not to play notification sound on successive startForegroundService() calls?
The other related question is: how can I go back to my application when I click the foreground notification? I have 5 activites and I would like to reopen the activity that was the last one the user was interacting with.
#1. before starting the service just check if its already running or not. In that case this will help you https://stackoverflow.com/a/5921190/6413387
#2. To reopen your last opened activity, you need to update the pending intent of your notification. Hope you will find your answer here https://stackoverflow.com/a/20142620/6413387
How can I create the foreground notification only once or at least how not to play notification sound on successive startForegroundService() calls?
You can check if the notification is already visible and show it only if it's not visible. You need to have a reference to the notification PendingIntent and notificationId.
fun isNotificationVisible(context: Context, notificationIntent: Intent, notificationId: Int): Boolean {
return PendingIntent.getActivity(context, notificationId, notificationIntent, PendingIntent.FLAG_NO_CREATE) != null
}
how can I go back to my application when I click the foreground notification?
You need a PendingIntent to open the app from a notification. To open the last activity shown you can remember this using Preferences in the onResume() method of each activity and route the notification into a routing activity that starts the right activity according to the value saved into the preferences.
val intent = Intent(context, RouteActivity::class.java)
val notificationBuilder = NotificationCompat.Builder(context, channelId)
.setContentIntent(intent)
val notificationManager = NotificationManagerCompat.from(context)
val notification = notificationBuilder.build()
notificationManager.notify(notificationId, notification)
Another way to do this is to update the notification PendingIntent if it's already visible with the last activity shown. In this case you don't have to store any value on Preferences and you don't need a route activity.