I am trying to create service which will be fetch data from server (periodicaly) and send notification to user. I achieved almost my goal, but service stops work when screen is off. When I turn on my phone I get plenty of notifications from previous time.
Service code:
class MyService: Service() {
override fun onCreate() {
super.onCreate()
val handler = Handler()
val runnable = object: Runnable {
override fun run() {
getDataFromServer()
handler.postDelayed(this, 1800000)
}
}
handler.post(runnable)
}
private fun getDataFromServer() {
// fetch data
displayNotification(description)
}
private fun displayNotification(description: String) {
val intent = Intent(this, MainActivity::class.java)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notificationId = (0..300).random()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val adminChannelName = "Messages channel"
val adminChannelDescription = "Notification about new data"
NotificationChannel(ADMIN_CHANNEL_ID, adminChannelName, NotificationManager.IMPORTANCE_HIGH).apply {
description = adminChannelDescription
enableLights(true)
lightColor = Color.RED
enableVibration(true)
notificationManager.createNotificationChannel(this)
}
}
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_ONE_SHOT
)
val notificationSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(this, ADMIN_CHANNEL_ID)
.setSmallIcon(R.drawable.icon_small)
.setContentTitle(getText(R.string.notification_new_data_title))
.setContentText(getString(R.string.notification_new_data_text, description))
.setAutoCancel(true)
.setSound(notificationSoundUri)
.setContentIntent(pendingIntent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notificationBuilder.color = ContextCompat.getColor(context, R.color.icon_background)
}
notificationManager.notify(notificationId, notificationBuilder.build())
}
override fun onBind(intent: Intent): IBinder ? {
return null
}
}
Thank you for your help!
Try to start you service using the startForegroundService instead of startService, whether you need a background service, you can convert you Service to JobIntentService:
https://developer.android.com/reference/androidx/core/app/JobIntentService
Related
I'm trying stopping running coroutineWorker from notification button. I tried 3 methods and 2 of them calls "Result.failure()" & working fine. However another one doesn't.
Below CoroutineWorker shows foregroundInfo and Starts ringtone.
class RingWork(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
companion object {
val ALARM_CHANNEL_ID = "alarm_channel6"
}
lateinit var ringtoneSound: Ringtone
val context = applicationContext
#RequiresApi(Build.VERSION_CODES.Q)
override suspend fun doWork(): Result {
return try {
val alarmId = inputData.getInt("alarmId", 0)
val notificationMgr =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//CHANNEL
val alarmChannel = NotificationChannel(
ALARM_CHANNEL_ID, "alarm" ,NotificationManager.IMPORTANCE_HIGH
)
alarmChannel.setSound(null, null)
alarmChannel.enableVibration(false)
alarmChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
notificationMgr.createNotificationChannel(alarmChannel)
val fullScreenIntent = Intent(context, LockscreenActivity::class.java).putExtra("alarmId", alarmId)
//This calls "failure" properly
val fullScreenPendingIntent = PendingIntent.getActivity(context, 0, fullScreenIntent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
//This calls "failure" properly
val stop1PendingIntent =
WorkManager.getInstance(context).createCancelPendingIntent(getId())
val s2Intent = Intent(context, StopAlarmReceiver::class.java).putExtra("alarmId", alarmId)
//This is not.
val stop2PendingIntent = PendingIntent.getBroadcast(context, 1, s2Intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
val builder = NotificationCompat.Builder(context, ALARM_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentTitle("title")
.setFullScreenIntent(fullScreenPendingIntent, true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setAutoCancel(true)
.setSound(null)
.setVibrate(null)
.addAction(R.drawable.ic_stat_name, "Stop1", stop1PendingIntent)
.addAction(R.drawable.ic_stat_name, "Stop2", stop2PendingIntent)
setForeground(
ForegroundInfo(1999999, builder.build(), FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
)
ringtoneSound =
RingtoneManager.getRingtone(context, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM))
ringtoneSound.play()
delay(30000L)
ringtoneSound.stop()
Result.success()
} catch (e: Exception) {
Result.failure()
} finally {
cleanup()
}
}
fun cleanup(){
ringtoneSound.stop()
}
}
In LockScreenActivity, there is a button to stop ringtone.
binding.stoppingbutton.setOnClickListener {
val workMgr = WorkManager.getInstance(applicationContext)
workMgr.cancelUniqueWork("RingWork-$alarmId")
finish()
}
This calls "result.failure" and "finally" then ringtone will stop, notification will disapear. working fine.
However, if I press "Stop2" button on the notification.
class StopAlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val alarmId = intent.getIntExtra("alarmId", 0)
val workMgr = WorkManager.getInstance(context)
workMgr.cancelUniqueWork("RingWork-$alarmId")
}
}
It cancels worker, but it won't call "result.failure" and "finally", so ringtone won't stop. Notification also won't disappear.
fullScreenPendingIntent and stop2PendingIntent are doing the same thing, but why it won't behave same?
You can edit your PendingIntent like this and it will trigger onReceive:
val stop2PendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
PendingIntent.getBroadcast(context, downloadFile.productId, cancelIntent, PendingIntent.FLAG_IMMUTABLE)
else
PendingIntent.getBroadcast(context, downloadFile.productId, cancelIntent, 0)
I am working on a parental control app which notify parent multiple times but when I try to create notification with a background service it generates only one 1.
Here is how I do it:
fun createNotification(parent_name: String, notificationText:String, id: Int){
val MchannelId = channelId+id.toString()
if (Build.VERSION.SDK_INT >= 26) {
val channel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel(
MchannelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT
)
} else {
TODO("VERSION.SDK_INT < O")
}
(getSystemService(NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(
channel
)
}
val notificationIntent = Intent(this, TabbedActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
id, notificationIntent, 0
)
val notification: Notification = NotificationCompat.Builder(this, "$MchannelId")
.setContentTitle("Hi $parent_name")
.setContentText(notificationText)
.setSmallIcon(R.drawable.icon_child)
//.setContentIntent(pendingIntent)
.build()
startForeground(random_number, notification)
}
My Full-Service Class:
const val TAG2 = "Child Service"
class ParentService: Service() {
val db = FirebaseFirestore.getInstance()
private val channelId = "Notification from Service"
var parent_name = userName
override fun onBind(intent: Intent?): IBinder? = null
//OnBind Function Implementation
init {
Log.d(TAG2, "Started Service!")
}
//onCreate Method Implementation
override fun onCreate() {
super.onCreate()
}
//OnStartCommand Override
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Thread{
while (true){
checkStatus()
Thread.sleep(PARENT_CHECK_TIME)
}
}.start()
return START_STICKY
}
private fun checkStatus() {
var listOfNames = ""
var i = 1
val calendar: Calendar = Calendar.getInstance()
var list = ArrayList<String>()
db.collection(LINKED_CHILDS)
.whereEqualTo(USER_PHONE, userPhone)
.get()
.addOnSuccessListener { documents ->
for (document in documents){
val startTime: Long = calendar.getTimeInMillis()
val diff = startTime - (document.data[ACTIVE_STATUS] as Long)
Log.d("TAG", "Time Difference : $diff")
Log.d("TAG", "${document.data[USER_NAME].toString()}")
if (diff> MAX_GAP_TIME){
Log.d("TAG", "Entered IFF")
list.add(document.data[USER_NAME].toString())
}
}
for (name in list){
listOfNames = listOfNames + "$i. Your child $name is not active\n"
i++
createNotification(parent_name, listOfNames, i)
Log.d("TAG Notification ID:", "ID: $i")
}
Log.d("TAG: ", "$listOfNames")
}
}
fun createNotification(parent_name: String, notificationText:String, id: Int){
val MchannelId = channelId+id.toString()
if (Build.VERSION.SDK_INT >= 26) {
val channel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel(
MchannelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT
)
} else {
TODO("VERSION.SDK_INT < O")
}
(getSystemService(NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(
channel
)
}
val notificationIntent = Intent(this, TabbedActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
id, notificationIntent, 0
)
val notification: Notification = NotificationCompat.Builder(this, "$MchannelId")
.setContentTitle("Hi $parent_name")
.setContentText(notificationText)
.setSmallIcon(R.drawable.icon_child)
//.setContentIntent(pendingIntent)
.build()
startForeground(id, notification)
}
}
Kinldy let me know how I can create multiple Notifications using this background service. Thank You so much in advance!
Kinldy let me know how I can create multiple Notifications using this background service. Thank You so much in advance!
Kinldy let me know how I can create multiple Notifications using this background service. Thank You so much in advance!
If you create a non-persistent notification, it will show your notifications. The permanent notification will be used for your service to run in the background.
#RequiresApi(Build.VERSION_CODES.O)
private fun createNotification() {
val intent = Intent(this, TabbedActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent =
PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
val notification = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.icon_child)
.setContentTitle("Hi $parent_name")
.setContentText(notificationText)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
with(NotificationManagerCompat.from(this)) {
notify(notifManagerId, notification.build())
notifManagerId++
}
parmanentNotification()
}
this is a permanent notification will not be lost and destroyed will keep the service running permanently
private fun parmanentNotification() {
val notification=NotificationCompat.Builder(this,channelId)
.setSmallIcon(R.drawable.icon_child)
.setContentTitle("Hi $parent_name")
.setContentText("Application service running in the background")
.build()
startForeground(1,notification)
}
you aren't creating a common Notification in this scenario, you are running a Service, which must have a foreground representation on screen. So Activity visible or sticked, fixed Notification, and you are showing it
Now you can have much Notifications using similar code, but don't show them using startForeground, instead use NotificationManager, preferably compat version
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.notify(uniqueId, notification);
or just like you are using it already when creating channel inside if: (getSystemService(NOTIFICATION_SERVICE) as NotificationManager).notify(...)
foreground-related Notification is sticky and lives as long as Service works in background, they are "tied". other Notifications may be configured to be sticky or swipeable, also should be posted on some own Channel (per child? per action?). Note that if you show yet another sticky Notification then you have to release it by own through code, just killing Service won't dismiss it as it does with foreground-related Notification
some DOC in here, read carefully, all answers are there
I've been researching awhile about how to keep active a constantly running audio playback in the background (online radio). For last I made a foreground service for it and its works for the most phones, but not on Samsung Android P and above... (as this article show in the "Foreground service limitations" section: https://proandroiddev.com/android-foreground-service-restrictions-d3baa93b2f70)
I heard that there is a advanced tool for audio playback called ExoPlayer. Could this lib help me out?
I'v been tried these solutions:
ping google.com every 2 sec
request battery optimization ignoring
set wake lock for mediplayer with: setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) (still in use)
Starting the service:
viewModel.isMusicControlServiceNeedToStart.observe(this, Observer {
if (it) {
val intent = Intent(this, MusicControlForegroundServiceImpl::class.java).apply { action = ACTION_SHOW_MUSIC_CONTROL }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startForegroundService(intent) else startService(intent)
} else {
stopService(Intent(this, MusicControlForegroundServiceImpl::class.java))
}
})
The service itself:
class MusicControlForegroundServiceImpl : Service(), KoinComponent {
private val notificationManager: NotificationManager by inject()
private val radioManager: RadioManager by inject()
private val context: Context by inject()
private val preferences: Preferences by inject()
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null && intent.action == ACTION_SHOW_MUSIC_CONTROL) {
val lastSelectedRadio = preferences.getJSON(Globals.LAST_SELECTED_RADIO_KEY, Radio::class.java)
?: return START_NOT_STICKY
val notification = notificationManager.createMediaControlNotificationIfNeeded(context, lastSelectedRadio)
startForeground(1, notification)
}
return START_NOT_STICKY
}
override fun onTaskRemoved(rootIntent: Intent?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startForegroundService(rootIntent) else startService(rootIntent)
super.onTaskRemoved(rootIntent)
}
override fun onDestroy() {
if (!notificationManager.musicControlServiceRestart) radioManager.release()
synchronized(MUSIC_CONTROL_SERVICE_LOCK) { notificationManager.musicControlServiceRestart = false }
synchronized(MEDIA_PLAYER_LOCK) { radioManager.lastPlayedMediaUrl = null }
stopForeground(true)
super.onDestroy()
}
override fun onBind(intent: Intent?): IBinder? = null
}
The notification creation:
override fun createMediaControlNotificationIfNeeded(context: Context, selectedRadio: Radio): Notification {
val resultIntent = Intent(context, RadioDetailActivity::class.java)
val resultPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
addNextIntentWithParentStack(resultIntent)
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}
val playIntent = Intent(context, NotificationReceiver::class.java).apply {
putExtra(MusicState::class.java.name, MusicState.PLAY)
}
val pauseIntent = Intent(context, NotificationReceiver::class.java).apply {
putExtra(MusicState::class.java.name, MusicState.PAUSE)
}
val notificationManager = NotificationManagerCompat.from(context)
#Suppress("DEPRECATION") val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification_icon)
.setContentTitle(selectedRadio.name)
.setDefaults(0)
.setOngoing(true)
.setNotificationSilent()
.addAction(
R.drawable.ic_notification_pause,
context.getString(R.string.pause),
PendingIntent.getBroadcast(context, 1, pauseIntent, 0)
)
.addAction(
R.drawable.ic_notification_play,
context.getString(R.string.play),
PendingIntent.getBroadcast(context, 2, playIntent, 0)
)
.setStyle(
androidx.media.app.NotificationCompat.MediaStyle().setMediaSession(
MediaSessionCompat(
context,
RadioDetailActivity::class.java.name
).sessionToken
)
)
.setContentIntent(resultPendingIntent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
context.getString(R.string.app_name),
NotificationManager.IMPORTANCE_LOW
)
notificationManager.createNotificationChannel(channel)
builder.setChannelId(CHANNEL_ID)
}
return builder.build()
}
If you need any other resources please let me know and help if you can! I'm struggling with this problem for weeks now.. :(
UPDATE
Now I throw my media control notification in every 2 minutes to update previous, so the app can survive like 30 minutes on the affected phone, but still not a working solution...
I have an app that triggers incoming calls by FCM data message(priority: high, no notification payload).
When the app is in the foreground or background, the app receives calls.
For the above case, the notification(Incoming Call) is received when in Locked Screen.
But for some reason, when the app is closed or swiped from the multi-task tray, calls are no more received. I think that the service is killed. what can I do?
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
Firebase Messaging class manifest declaration
<service
android:name=".firebase.MyFirebaseMessagingService"
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Workflow
When FCM notification is received, a background service is started, then the service changes itself to the foreground service with "Incoming call" notification. I'm also using full-screen intent. The below class is what I use as Service
class IncomingCallNotificationService : Service() {
private val TAG = IncomingCallNotificationService::class.java.simpleName
var isRunning = false
private lateinit var soundUri: Uri
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + applicationContext.packageName + "/" + R.raw.incoming)
val videoCallDetails: VideoNotification?= intent.getParcelableExtra(Constants.VIDEO_CALL_DETAILS)
val notificationId = intent.getIntExtra(Constants.INCOMING_CALL_NOTIFICATION_ID, 0)
val action = intent.action
if (action != null) {
when(action){
ACTION_INCOMING_CALL_NOTIFICATION -> { handleIncomingCall(videoCallDetails!!, notificationId) }
ACTION_ACCEPT -> {
endForeground()
val intent = Intent(this, VideoActivity::class.java)
intent.action = ACTION_INCOMING_CALL_NOTIFICATION
intent.putExtra(Constants.ROOM_CODE, videoCallDetails?.roomCode)
intent.putExtra(Constants.ROOM_NAME, videoCallDetails?.roomName)
intent.putExtra(Constants.ACCESS_TOKEN, videoCallDetails?.accessToken)
intent.putExtra(Constants.CALLER_NAME, videoCallDetails?.userName)
intent.putExtra(
Constants.CALL_RESPONSE_ACTION_KEY,
Constants.INCOMING_CALL_SCREEN
)
intent.putExtra(Constants.CALL_TYPE, Constants.VIDEO_CALL)
intent.putExtra(Constants.NOTIFCATION_ID, notificationId)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.action = ACTION_ACCEPT
startActivity(intent)
}
ACTION_REJECT -> {
endForeground()
val it = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
applicationContext.sendBroadcast(it)
}
}
}
return START_NOT_STICKY
}
override fun onBind(intent: Intent): IBinder? {
return null
}
private fun createNotification(
videoCallDetails: VideoNotification,
notificationId: Int,
channelImportance: Int
): Notification? {
val intent = Intent(this, VideoActivity::class.java)
intent.action = ACTION_INCOMING_CALL_NOTIFICATION
intent.putExtra(Constants.ROOM_CODE, videoCallDetails.roomCode)
intent.putExtra(Constants.ROOM_NAME, videoCallDetails.roomName)
intent.putExtra(Constants.ACCESS_TOKEN, videoCallDetails.accessToken)
intent.putExtra(Constants.CALLER_NAME, videoCallDetails.userName)
intent.putExtra(Constants.CALL_RESPONSE_ACTION_KEY, Constants.INCOMING_CALL_SCREEN)
intent.putExtra(Constants.CALL_TYPE, Constants.VIDEO_CALL)
intent.putExtra(Constants.INCOMING_VIDEO_CALL, true)
intent.putExtra(Constants.NOTIFCATION_ID, notificationId)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(
this,
notificationId,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
/*
* Pass the notification id and call sid to use as an identifier to cancel the
* notification later
*/
val extras = Bundle()
extras.putString(Constants.ROOM_CODE, videoCallDetails.roomCode)
extras.putString(Constants.ROOM_NAME, videoCallDetails.roomName)
extras.putString(Constants.ACCESS_TOKEN, videoCallDetails.accessToken)
extras.putString(Constants.CALLER_NAME, videoCallDetails.userName)
extras.putString(Constants.CALL_RESPONSE_ACTION_KEY, Constants.INCOMING_CALL_SCREEN)
extras.putString(Constants.CALL_TYPE, Constants.VIDEO_CALL)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
buildNotification(
videoCallDetails.userName + " is calling.",
pendingIntent,
extras,
videoCallDetails,
notificationId,
createChannel(channelImportance)!!
)
} else {
val builder = NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_call_end_white_24dp)
.setContentTitle(INCOMING_VIDEO_CALL_TEXT)
.setContentText(videoCallDetails.userName + " is calling.")
.setAutoCancel(true)
.setExtras(extras)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setTimeoutAfter(MomsezeApplication.notificationTimeOut)
.setGroup("test_app_notification")
.setColor(Notification.DEFAULT_LIGHTS)
.setSound(soundUri)
val notification = builder.build()
notification.flags = Notification.FLAG_INSISTENT
return notification
}
}
#TargetApi(Build.VERSION_CODES.O)
private fun buildNotification(
text: String, pendingIntent: PendingIntent, extras: Bundle,
videoCallDetails: VideoNotification,
notificationId: Int,
channelId: String
): Notification? {
val rejectIntent = Intent(applicationContext, IncomingCallNotificationService::class.java)
rejectIntent.action = Constants.ACTION_REJECT
rejectIntent.putExtra(Constants.VIDEO_CALL_DETAILS, videoCallDetails)
rejectIntent.putExtra(Constants.INCOMING_CALL_NOTIFICATION_ID, notificationId)
val piRejectIntent = PendingIntent.getService(
applicationContext,
0,
rejectIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val acceptIntent = Intent(applicationContext, IncomingCallNotificationService::class.java)
acceptIntent.action = ACTION_ACCEPT
acceptIntent.putExtra(Constants.VIDEO_CALL_DETAILS, videoCallDetails)
acceptIntent.putExtra(Constants.INCOMING_CALL_NOTIFICATION_ID, notificationId)
val piAcceptIntent = PendingIntent.getService(
applicationContext,
0,
acceptIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val builder = NotificationCompat.Builder(applicationContext, channelId)
.setSmallIcon(R.drawable.ic_call_end_white_24dp)
.setContentTitle(INCOMING_VIDEO_CALL_TEXT)
.setContentText(text)
.setCategory(Notification.CATEGORY_CALL)
.setFullScreenIntent(pendingIntent, true)
.setExtras(extras)
.setSound(soundUri)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.addAction(android.R.drawable.ic_menu_delete, getString(R.string.decline), piRejectIntent)
.addAction(android.R.drawable.ic_menu_call, getString(R.string.answer), piAcceptIntent)
.setFullScreenIntent(pendingIntent, true)
.setTimeoutAfter(MomsezeApplication.notificationTimeOut)
val notification = builder.build()
notification.flags = Notification.FLAG_INSISTENT
return notification
}
#TargetApi(Build.VERSION_CODES.O)
private fun createChannel(channelImportance: Int): String? {
var callInviteChannel = NotificationChannel(
Constants.VOICE_CHANNEL_HIGH_IMPORTANCE,
"Primary Voice Channel", NotificationManager.IMPORTANCE_HIGH
)
var channelId = Constants.VOICE_CHANNEL_HIGH_IMPORTANCE
if (channelImportance == NotificationManager.IMPORTANCE_LOW) {
callInviteChannel = NotificationChannel(
Constants.VOICE_CHANNEL_LOW_IMPORTANCE,
"Primary Voice Channel",
NotificationManager.IMPORTANCE_DEFAULT
)
channelId = Constants.VOICE_CHANNEL_LOW_IMPORTANCE
}
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.build()
callInviteChannel.setSound(soundUri, audioAttributes)
callInviteChannel.lightColor = Color.GREEN
callInviteChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(callInviteChannel)
return channelId
}
private fun handleIncomingCall(videoCallDetails: VideoNotification, notificationId: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setCallInProgressNotification(videoCallDetails, notificationId)
}
sendCallInviteToActivity(videoCallDetails, notificationId)
}
private fun endForeground() {
stopForeground(true)
}
#TargetApi(Build.VERSION_CODES.O)
private fun setCallInProgressNotification(
videoCallDetails: VideoNotification,
notificationId: Int
) {
if (isAppVisible()) {
startForeground(
notificationId, createNotification(
videoCallDetails,
notificationId,
NotificationManager.IMPORTANCE_LOW
)
)
} else {
startForeground(
notificationId, createNotification(
videoCallDetails,
notificationId,
NotificationManager.IMPORTANCE_HIGH
)
)
}
}
/*
* Send the CallInvite to the VoiceActivity. Start the activity if it is not running already.
*/
private fun sendCallInviteToActivity(videoCallDetails: VideoNotification, notificationId: Int) {
if (Build.VERSION.SDK_INT >= 29 && !isAppVisible()) {
return
}
val intent = Intent(this, VideoActivity::class.java)
intent.action = ACTION_INCOMING_CALL_NOTIFICATION
intent.putExtra(Constants.ROOM_CODE, videoCallDetails.roomCode)
intent.putExtra(Constants.ROOM_NAME, videoCallDetails.roomName)
intent.putExtra(Constants.ACCESS_TOKEN, videoCallDetails.accessToken)
intent.putExtra(Constants.CALLER_NAME, videoCallDetails.userName)
intent.putExtra(Constants.CALL_RESPONSE_ACTION_KEY, Constants.INCOMING_CALL_SCREEN)
intent.putExtra(Constants.CALL_TYPE, Constants.VIDEO_CALL)
intent.putExtra(Constants.INCOMING_VIDEO_CALL, true)
intent.putExtra(Constants.NOTIFCATION_ID, notificationId)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
this.startActivity(intent)
}
private fun isAppVisible(): Boolean {
return ProcessLifecycleOwner
.get()
.lifecycle
.currentState
.isAtLeast(Lifecycle.State.STARTED)
}
private fun createRandomCode(codeLength: Int): Int {
val chars = "1234567890".toCharArray()
val sb = StringBuilder()
val random: Random = SecureRandom()
for (i in 0 until codeLength) {
val c = chars[random.nextInt(chars.size)]
sb.append(c)
}
return sb.toString().toInt()
}
override fun onCreate() {
super.onCreate()
isRunning = true
LocalBroadcastManager.getInstance(this).registerReceiver(
broadCastReceiver, IntentFilter(
Constants.NOTIFCATION_ID
)
)
}
override fun onDestroy() {
super.onDestroy()
isRunning = false
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadCastReceiver)
}
private val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
endForeground()
}
}
}
You return START_NOT_STICKY from onStartCommand(). When you swipe the app from the recent tasks list, Android kills the OS process hosting your app (and all the components Activity, Service, etc.). If you want Android to restart your Service automatically in this case, you need to return START_STICKY from onStartCommand().
I have tried using "0" notification id and as well as unique notification id.
Also used setGroup() like below. It still generates a new notification every time. I want to merge the notification body and set the title as common.
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage?) {
super.onMessageReceived(remoteMessage)
remoteMessage?.let {
sendNotification(it.data["alert"])
}
}
private fun sendNotification(messageBody: String?) {
val channelId = "${this.packageName}-${this.getString(R.string.app_name)}"
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val builder = NotificationCompat.Builder(this, channelId).apply {
setDefaults(Notification.DEFAULT_ALL)
setSmallIcon(if (Build.VERSION.SDK_INT >= 21) R.mipmap.ic_launcher else R.mipmap.ic_launcher_round)
setContentTitle(getString(R.string.app_name))
setContentText(messageBody)
setDefaults(NotificationCompat.DEFAULT_SOUND or NotificationCompat.DEFAULT_VIBRATE or NotificationCompat.DEFAULT_LIGHTS)
setStyle(NotificationCompat.BigTextStyle().bigText(messageBody))
priority = NotificationCompat.PRIORITY_DEFAULT
setAutoCancel(true)
setSound(defaultSoundUri)
setGroup(getString(R.string.app_name))
setGroupSummary(true)
}
val manager = getSystemService(NOTIFICATION_SERVICE) as? NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, getString(R.string.default_channel_name), NotificationManager.IMPORTANCE_HIGH)
manager?.createNotificationChannel(channel)
}
val intent = Intent(this, DashboardFlowActivity::class.java)
intent.putExtra(DashboardFlowActivity.ISFROMNOTIFICATION, true)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
builder.setContentIntent(pendingIntent)
//manager?.cancelAll()
manager?.notify(this.getString(R.string.app_name), getID(), builder.build())
}
}
private val c = AtomicInteger(0)
private fun getID(): Int {
return c.incrementAndGet()
}
Anything I am doing wrong here? Also, I have gone through this answer. setgroup() in notification not working
Finally I got solution for this question using https://stackoverflow.com/a/41114135/6021469, https://www.developer.com/ws/android/creating-bundled-notifications-with-android.html
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage?) {
super.onMessageReceived(remoteMessage)
remoteMessage?.let {
sendNotification(it.data["alert"])
}
}
private fun sendNotification(messageBody: String?) {
val channelId = "${this.packageName}-${this.getString(R.string.app_name)}"
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val builder = NotificationCompat.Builder(this, channelId).apply {
setDefaults(Notification.DEFAULT_ALL)
setSmallIcon(if (Build.VERSION.SDK_INT >= 21) R.mipmap.ic_launcher else R.mipmap.ic_launcher_round)
setContentTitle(getString(R.string.app_name))
setShowWhen(true)
setContentText(messageBody)
setDefaults(NotificationCompat.DEFAULT_SOUND or NotificationCompat.DEFAULT_VIBRATE or NotificationCompat.DEFAULT_LIGHTS)
setStyle(NotificationCompat.BigTextStyle().bigText(messageBody))
priority = NotificationCompat.PRIORITY_DEFAULT
setAutoCancel(true)
setShowWhen(true)
setSound(defaultSoundUri)
setGroup(getString(R.string.app_name))
}
val builderSummary = NotificationCompat.Builder(this, channelId).apply {
setDefaults(Notification.DEFAULT_ALL)
setSmallIcon(if (Build.VERSION.SDK_INT >= 21) R.mipmap.ic_launcher else R.mipmap.ic_launcher_round)
setContentTitle(getString(R.string.app_name))
setShowWhen(true)
setContentText(messageBody)
setDefaults(NotificationCompat.DEFAULT_SOUND or NotificationCompat.DEFAULT_VIBRATE or NotificationCompat.DEFAULT_LIGHTS)
setStyle(NotificationCompat.BigTextStyle().bigText(messageBody))
priority = NotificationCompat.PRIORITY_DEFAULT
setAutoCancel(true)
setShowWhen(true)
setSound(defaultSoundUri)
setGroup(getString(R.string.app_name))
setGroupSummary(true)
}
val manager = getSystemService(NOTIFICATION_SERVICE) as? NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, getString(R.string.default_channel_name), NotificationManager.IMPORTANCE_HIGH)
manager?.createNotificationChannel(channel)
}
val intent = Intent(this, DashboardFlowActivity::class.java)
intent.putExtra(DashboardFlowActivity.ISFROMNOTIFICATION, true)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
builder.setContentIntent(pendingIntent)
manager?.notify(this.getString(R.string.app_name), getID(), builder.build())
manager?.notify(this.getString(R.string.app_name), 0, builderSummary.build())
}
}
private val c = AtomicInteger(0)
private fun getID(): Int {
return c.incrementAndGet()
}