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
Related
When I swipe up after the local notification comes, the application notifications don't appear on the screen for a while. What could be the reason for this? I've read something that when heads up notifications are swiped up, it sends a warning to the system to prevent heads up notifications for a certain period of time, but how can I prevent this? I don't have any problems getting notifications in my app other than that.
fun getChatChannels(): MutableList<NotificationChannel> {
val chatChannels = mutableListOf<NotificationChannel>()
val chatChannel = NotificationChannel(
IM_CHANNEL_ID,
Application.getString(R.string.chat_channel_name),
NotificationManager.IMPORTANCE_HIGH
)
chatChannel.setShowBadge(false)
chatChannel.group = MESSAGE_GROUP_ID
chatChannels.add(chatChannel)
return chatChannels }
private fun getChannelList(): MutableList<NotificationChannel> {
val channelList = mutableListOf<NotificationChannel>()
channelList.addAll(getChatChannels())
return channelList
}
fun createGroupsAndChannels() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager =
Application.instance.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannelGroups(getGroupList())
notificationManager.createNotificationChannels(getChannelList())
}
Log.d(TAG, "Channels and groups created")
}
fun showChatNotification(destinationAddress: String, messageId: String, message: String) {
ThreadManager.createUITask {
val name = if (domainContact != null) {
"${domainContact.firstName} ${domainContact.lastName}"
} else {
destinationAddress
}
val notificationIntent = Intent(Application.instance, MainActivity::class.java)
notificationIntent.putExtra(
Application.getString(R.string.key_conversation_participant),
destinationAddress
)
notificationIntent.flags = Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
val pendingIntent = PendingIntent.getActivity(
Application.instance,
destinationAddress.hashCode(),
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val notificationId = UUID.randomUUID().hashCode()
val builder = NotificationCompat.Builder(Application.instance, IM_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification_message)
.setColor(ContextCompat.getColor(Application.instance, R.color.colorPrimary))
.setContentTitle(name)
.setContentText(message)
.setStyle(
NotificationCompat.BigTextStyle() // Expandable-Collapsible notification
.bigText(message)
)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(pendingIntent)
.setVisibility(VISIBILITY_PUBLIC)
.setAutoCancel(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.addAction(getChatNotificationReplyAction(destinationAddress, notificationId, messageId))
}
chatNotifications.add(mapOf(destinationAddress to notificationId))
createNotifications(builder, notificationId)
Log.d(TAG, "Chat notification created")
}
private fun createNotifications(builder: NotificationCompat.Builder, notificationId: Int) {
with(NotificationManagerCompat.from(Application.instance)) {
notify(notificationId, builder.build())
}
}
I have been trying to change the sound of the notification but it isn't changing at all.
It is using the default notification in all cases, even when I have assigned the channel.
Please check the codes below and let me know, where I am going wrong.
Created Notification Channel in Application class
class App : Application() {
override fun onCreate() {
super.onCreate()
createNotificationChannel()
}
private fun createNotificationChannel() {
val ordersChannelId = "Orders"
val orderSoundUri =
Uri.parse("android.resource://" + applicationContext + "/raw/ordernotification")
val attributes = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build()
val VIBRATE_PATTERN = longArrayOf(0,400,800,600,800,800,800,1000)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val ordersChannel =
NotificationChannel(ordersChannelId, "Orders", NotificationManager.IMPORTANCE_HIGH)
ordersChannel.apply {
description = "This is Orders Channel"
setSound(orderSoundUri, attributes)
vibrationPattern = VIBRATE_PATTERN
importance = NotificationManager.IMPORTANCE_HIGH
}
val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(ordersChannel)
}
}
Creating Notifications using FireBaseMessagingService
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
Log.d("NotificationFire", "From: ${remoteMessage?.data}")
val contentIntent = Intent(applicationContext, OrderInDetailActivity::class.java)
val orderSoundUri = Uri.parse("android.resource://"+applicationContext+"/raw/ordernotification")
val attributes = AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_ALARM).build()
val VIBRATE_PATTERN = longArrayOf(0, 500)
val contentPendingIntent = PendingIntent.getActivity(
applicationContext,
0,
contentIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
remoteMessage?.data?.let {
Log.d("NotificationFire", "Message Notification Data: ${it}")
//Message Services handle notification
val notification = NotificationCompat.Builder(this, "Orders")
.setSmallIcon(R.drawable.biskit_logo)
.setContentTitle(remoteMessage.data.toString())
.setContentText(remoteMessage.data.toString())
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setSound(orderSoundUri)
.setVibrate(VIBRATE_PATTERN)
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setContentIntent(contentPendingIntent)
.build()
val notificationManager = NotificationManagerCompat.from(this)
Log.d("NotificationFire","Notification")
notificationManager.notify(1,notification)
}
This code is able to display notifications with the default sound only.
In your sound path you appended applicationContext. It will add some random value in your path. Instead you need to add the package name like this below.
Uri.parse("android.resource://"+context.getPackageName()+"/"+R.raw.FILE_NAME);//Here is FILE_NAME is the name of file that you want to play
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'm trying to create a foreground service that check devices location every minute and I've managed to make it work. But I have some problems regarding the obligatory notification about the service being active.
I can't get the notification badge to stop showing and I can't make the notification minimized programatically.
Here are the relevant parts:
class LocationPollingService : Service() {
companion object {
const val NOTIF_ID = 2
const val NOTIF_CHANNEL_ID = "background_service_notification"
const val NOTIF_CHANNEL_NAME = "Background service"
}
#Nullable
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int
): Int {
createBackgroundNotifChannel()
startForeground()
requestLocation()
return super.onStartCommand(intent, flags, startId)
}
private fun startForeground() {
val notificationIntent = Intent(this, SplashActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this, 0,
notificationIntent, 0
)
val notification = NotificationCompat.Builder(
this,
NOTIF_CHANNEL_ID
)
.setOngoing(true)
.setSmallIcon(R.drawable.ic_app_icon)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.im_watching_you))
.setContentIntent(pendingIntent)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(NOTIF_ID, notification.build())
} else {
notification.priority = NotificationCompat.PRIORITY_MIN
startForeground(NOTIF_ID, notification.build())
}
}
private fun createBackgroundNotifChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelBackground = NotificationChannel(
NOTIF_CHANNEL_ID,
NOTIF_CHANNEL_NAME,
NotificationManager.IMPORTANCE_LOW
).apply {
description = getString(
R.string.notification_channel_background_description
)
enableLights(false)
enableVibration(false)
setShowBadge(false)
}
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channelBackground)
}
}
}
If I understand correctly, IMPORTANCE_LOW is supposed to be used for this kind of notification and it is supposed to make it minimized by default. The notification is never minimized nor can I minimize it by clicking. The only way I can make it minimized is by manually changing notification channel settings in device settings (setting Notification style to Silent and minimized, instead of just silent, which is the default). I tried setting the importance to IMPORTANCE_MIN and IMPORTANCE_NONE, but that didn't change anything.
As mentioned, the second problem is that the badge is shown (the notification counter displayed on app icon), despite of setShowBadge(false).
What am I missing?
You need to notify notification after build notification like below :
notificationManager.notify(NOTIF_ID , notification.build());
for hiding notification badge from statusbar
you should change notification channel priority:
val channelBackground = NotificationChannel(
NOTIF_CHANNEL_ID,
NOTIF_CHANNEL_NAME,
NotificationManager.IMPORTANCE_MIN
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