I'm new to coding. I have made a net speed indicator app for fun. It displays the network speed as a status bar icon. It also allows users to set data usage limits (like 100 GB per month) and sends a notification when they reach the limit.
The final output looks like this:
I followed this answer to display the status bar icon.
Here is the Notifications class I use:
class Notifications(private val context: Context) {
private val notificationChannelPrimary =
"Primary Channel Notification" // This is the persistent channel that shows live data usage
val notificationPersistentChannelID = 15 // To send notifications of persistent channel
private val notificationChannelDataLimit = "Data Limit Warning Notifications"
private val notificationDataLimitID = 10
private val intentNotification = Intent(context, MainActivity::class.java)
private val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
private var mNotificationBuilder: Notification.Builder? = null
private var tempValues = arrayOf(String())
// The following variables are for creating status bar icon
private val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
private val canvas = Canvas(bitmap)
private val paintSpeed = Paint() // This is for speed value
private val paintUnit = Paint() // This is for speed unit
init {
// Set the paint styles
paintSpeed.color = Color.WHITE
paintSpeed.isAntiAlias = true
paintSpeed.textSize = 60f
paintSpeed.textAlign = Paint.Align.CENTER
paintSpeed.typeface = Typeface.DEFAULT_BOLD
paintUnit.color = Color.WHITE
paintUnit.isAntiAlias = true
paintUnit.textSize = 40f
paintUnit.textAlign = Paint.Align.CENTER
paintUnit.typeface = Typeface.DEFAULT_BOLD
createNotificationChannel()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mNotificationBuilder =
getNotificationBuilder()
}
}
// This method sets the speed to zero when the device is offline
fun setZeroSpeed() {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
canvas.drawText("0", 48f, 52f, paintSpeed)
canvas.drawText("Kb/s", 48f, 95f, paintUnit)
try {
mNotificationBuilder?.setSmallIcon(Icon.createWithBitmap(bitmap))
notificationManager.notify(notificationPersistentChannelID, mNotificationBuilder?.build())
} catch (e: Exception) {
e.printStackTrace()
}
}
// This method shows the speed + data usage on the notification
// It is called every second (from a worker thread)
fun updateDataUsage(
dataSpeed: Long,
remainingData: String?,
planDetails: String?
) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
// BytesConversion.convertSpeed() method takes long value and returns speed with unit array (like 2 Mb/s)
tempValues = BytesConversion.convertSpeed(dataSpeed)
canvas.drawText(tempValues[0], 50f, 50f, paintSpeed)
canvas.drawText(tempValues[1], 50f, 95f, paintUnit)
// If the argument of setContentTitle is null, it's not displaying anything in the title
mNotificationBuilder?.setContentTitle(remainingData)
mNotificationBuilder?.setContentText(planDetails)
try {
mNotificationBuilder?.setSmallIcon(Icon.createWithBitmap(bitmap))
notificationManager.notify(notificationPersistentChannelID, mNotificationBuilder?.build())
} catch (e: Exception) {
}
}
// This method sends notifications when the data usage limit is reached
// You don't need to check supported or unsupported
// because it works in all the devices
// It's just a normal notification
fun sendDataLimitWarning(dataUsageTitle: String?, dataUsageDescription: String?) {
val notificationDataLimit = getNotificationBuilderDataLimit()
notificationDataLimit.setContentText(dataUsageDescription)
notificationDataLimit.setContentTitle(dataUsageTitle)
notificationManager.notify(
notificationDataLimitID,
notificationDataLimit.build()
)
}
// Notification channel is only available from Android Oreo
// So, check the condition
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannels = mutableListOf<NotificationChannel>()
val notificationChannel1 = NotificationChannel(
notificationChannelPrimary, "Persistent Notification",
NotificationManager.IMPORTANCE_HIGH
)
notificationChannel1.description = "Notification that shows data usage and speed"
notificationChannel1.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
val notificationChannel2 = NotificationChannel(
notificationChannelDataLimit, "Data Limit Warning",
NotificationManager.IMPORTANCE_HIGH
)
notificationChannel2.description = "Notification that shows data limit warnings"
notificationChannel2.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
notificationChannel2.enableVibration(true)
notificationChannel2.enableLights(true)
notificationChannels.add(notificationChannel1)
notificationChannels.add(notificationChannel2)
notificationManager.createNotificationChannels(notificationChannels)
}
}
// This is for sending data limit warnings
private fun getNotificationBuilderDataLimit(): NotificationCompat.Builder {
val pendingIntentDataLimit = PendingIntent.getActivity(
context,
notificationDataLimitID,
intentNotification,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
return NotificationCompat.Builder(
context,
notificationChannelDataLimit
)
.setContentTitle("Data Limit Warning")
.setSmallIcon(R.drawable.icon_notification)
.setColor(ContextCompat.getColor(context, R.color.primary_color))
.setContentIntent(pendingIntentDataLimit)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationManager.IMPORTANCE_HIGH)
}
// This is for sending data + speed notification
// Speed indicator requires Notification.Builder
#RequiresApi(Build.VERSION_CODES.O)
fun getNotificationBuilder(
): Notification.Builder {
val pendingIntentNotification = PendingIntent.getActivity(
context,
notificationPersistentChannelID,
intentNotification,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
return Notification.Builder(
context,
notificationChannelPrimary
)
.setSmallIcon(R.drawable.icon_notification)
.setContentIntent(pendingIntentNotification)
.setAutoCancel(false)
.setStyle(Notification.BigTextStyle())
.setOnlyAlertOnce(true)
.setColor(ContextCompat.getColor(context, R.color.primary_color))
}
}
The problem is that I'm getting the following crashes a lot.
Fatal Exception: android.app.RemoteServiceException: Bad notification(tag=null, id=15) posted from package [package_name], crashing app(uid=10769, pid=16295): Couldn't inflate contentViewsjava.util.ConcurrentModificationException
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1894)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7156)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:975)
and
Fatal Exception: android.app.RemoteServiceException: Bad notification(tag=null, id=15) posted from package [package_name], crashing app(uid=10337, pid=27920): Couldn't inflate contentViewsjava.lang.ArrayIndexOutOfBoundsException: src.length=8 srcPos=0 dst.length=8 dstPos=2 length=8
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2168)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:227)
at android.app.ActivityThread.main(ActivityThread.java:7822)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1026)
I searched for "Bad notification posted crash", I found that mNotificationBuilder.setSmallIcon() argument must be a PNG icon (I'm calling it in updateDataUsage() method). Can someone please tell me how to convert the bitmap to PNG and pass it to setSmallIcon()?
Related
Running a foreground service in android kotlin to show notifications every minute through out the day using handler and runnable .Get the notification when i run the below code but after sometime the notifications stops. If the phone is in locked state and if i unlock it ,the notification starts coming but again stops after sometime. Please help me with this.
class MedTimerService : Service() {
private val FOREGROUND_NOTIFY_ID = 5051
private val TIME_GAP_FOR_NEXT_MEDICATION = 24 * 60 * 60 * 1000
private var morningTime = 0L
private var afternoonTime = 0L
private var eveningTime = 0L
private var nightTime = 0L
private val minuteHandler: Handler = Handler()
private val morningHandler: Handler = Handler()
private val afternoonHandler: Handler = Handler()
private val eveningHandler: Handler = Handler()
private val nightHandler: Handler = Handler()
private val teleconsultReminder: Handler = Handler()
#RequiresApi(Build.VERSION_CODES.O)
private val minuteRunnable: Runnable = Runnable {
doTheProcess()
}
#RequiresApi(Build.VERSION_CODES.O)
private fun doTheProcess() {
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).absolutePath + "/Wekare/data/handlerForEveryMinute").mkdirs()
FL.init(
FLConfig.Builder(this#MedTimerService)
.logger(null) // customise how to hook up with logcat
.defaultTag("HandlerForEveryMinute Tag") // customise default tag
.minLevel(FLConst.Level.V) // customise minimum logging level
.logToFile(true) // enable logging to file
.dir(File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).absolutePath + "/Wekare/data/handlerForEveryMinute")) // customise directory to hold log files
.retentionPolicy(FLConst.RetentionPolicy.FILE_COUNT) // customise retention strategy
.maxFileCount(FLConst.DEFAULT_MAX_FILE_COUNT) // customise how many log files to keep if retention by file count
.maxTotalSize(FLConst.DEFAULT_MAX_TOTAL_SIZE) // customise how much space log files can occupy if retention by total size
.build()
)
FL.setEnabled(true)
showMedsReminderNotification("HandlerForEveryMinute",DateUtils.getEpochDateTimeFromDate(System.currentTimeMillis()))
FL.d(
"HandlerForEveryMinute",
"minute handler Current Time ${DateUtils.getEpochDateTimeFromDate(System.currentTimeMillis())}"
)
minuteHandler.removeCallbacksAndMessages(minuteRunnable)
minuteHandler.postDelayed(minuteRunnable, 60 * 1000L)
}
override fun onCreate() {
super.onCreate()
}
#RequiresApi(Build.VERSION_CODES.O)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// FL.d("onStartCommand")
if (intent != null) {
val action = intent.action
// TimberLogger.i("FOREGROUND-START-ID", startId.toString())
//TimberLogger.i("FOREGROUND-ACTION", action.toString())
return if (action == "START") {
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).absolutePath + "/Wekare/data/medicationTimerService").mkdirs()
FL.init(
FLConfig.Builder(this#MedTimerService)
.logger(null) // customise how to hook up with logcat
.defaultTag("MedicationTimerService Tag") // customise default tag
.minLevel(FLConst.Level.V) // customise minimum logging level
.logToFile(true) // enable logging to file
.dir(File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).absolutePath + "/Wekare/data/medicationTimerService")) // customise directory to hold log files
.retentionPolicy(FLConst.RetentionPolicy.FILE_COUNT) // customise retention strategy
.maxFileCount(FLConst.DEFAULT_MAX_FILE_COUNT) // customise how many log files to keep if retention by file count
.maxTotalSize(FLConst.DEFAULT_MAX_TOTAL_SIZE) // customise how much space log files can occupy if retention by total size
.build()
)
FL.setEnabled(true)
FL.d("started MedicationTimerService")
// processTimings(Injector.provideWeKareRepository().reminderTimeInfo)
minuteHandler.postDelayed(minuteRunnable, 60 * 1000L)
startForegroundService()
showMedsReminderNotification("MedicationTimerService",DateUtils.getEpochDateTimeFromDate(System.currentTimeMillis()))
START_STICKY
} else {
minuteHandler.removeCallbacksAndMessages(null)
stopForeground(true)
stopSelfResult(startId)
FL.d("stopped MedicationTimerService")
STOP_FOREGROUND_REMOVE
}
} else {
return super.onStartCommand(intent, flags, startId)
}
}
#RequiresApi(Build.VERSION_CODES.O)
private fun startForegroundService() {
val notification = buildForegroundNotification()
notification.flags = Notification.FLAG_FOREGROUND_SERVICE.let {
notification.flags = notification.flags or it; notification.flags
}
startForeground(
FOREGROUND_NOTIFY_ID,
notification
)
FL.d("start foreground service")
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).absolutePath + "/Wekare/data/logs5").mkdirs()
FL.init(
FLConfig.Builder(WeKareApplication.application.applicationContext)
.logger(null) // customise how to hook up with logcat
.defaultTag("Background Tag") // customise default tag
.minLevel(FLConst.Level.V) // customise minimum logging level
.logToFile(true) // enable logging to file
.dir(File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).absolutePath + "/Wekare/data/logs5")) // customise directory to hold log files
// .formatter(FileFormatter()) // customise log format and file name
.retentionPolicy(FLConst.RetentionPolicy.FILE_COUNT) // customise retention strategy
.maxFileCount(FLConst.DEFAULT_MAX_FILE_COUNT) // customise how many log files to keep if retention by file count
.maxTotalSize(FLConst.DEFAULT_MAX_TOTAL_SIZE) // customise how much space log files can occupy if retention by total size
.build()
)
FL.setEnabled(true)
}
#RequiresApi(Build.VERSION_CODES.O)
private fun buildForegroundNotification(): Notification {
// return getDefaultNotification()
return getCustomNotification()
}
#RequiresApi(Build.VERSION_CODES.O)
private fun getCustomNotification(): Notification {
val notificationCompatBuilder =
NotificationCompat.Builder(
applicationContext,
WeKareConstants.NOTIFICATION_CHANNEL_ID_MED
)
val notificationManager = notificationManager()
createNotificationChannel(
notificationCompatBuilder,
notificationManager,
WeKareConstants.NOTIFICATION_CHANNEL_ID_MED,
WeKareConstants.NOTIFICATION_CHANNEL_NAME_MED,
NotificationManager.IMPORTANCE_LOW
)
val notificationLayout = RemoteViews(packageName, R.layout.notification_meds_layout)
val resultPendingIntent: PendingIntent? = getResultPIntent()
notificationCompatBuilder
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(resultPendingIntent)
// .setStyle(NotificationCompat.DecoratedCustomViewStyle())
.setCustomContentView(notificationLayout)
.setOngoing(true)
.setAutoCancel(true)
return notificationCompatBuilder.build()
FL.d("custom notification")
}
private fun notificationManager(): NotificationManager {
return applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
}
private fun getResultPIntent(): PendingIntent? {
val resultIntent = Intent(applicationContext, MainActivity::class.java)
resultIntent.putExtra(WeKareConstants.IS_FROM_NOTIFICATION, true)
resultIntent.putExtra(WeKareConstants.IS_FROM_THERAPY, false)
resultIntent.putExtra(WeKareConstants.IS_MEDICATION_REMINDER, true)
return TaskStackBuilder.create(applicationContext).run {
addNextIntentWithParentStack(resultIntent)
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
#RequiresApi(Build.VERSION_CODES.O)
private fun showMedsReminderNotification(title: String, message: String) {
val notificationCompatBuilder =
NotificationCompat.Builder(
applicationContext,
WeKareConstants.NOTIFICATION_CHANNEL_IMPORTANT_ID
)
val notificationManager =
notificationManager()
createNotificationChannel(
notificationCompatBuilder,
notificationManager,
WeKareConstants.NOTIFICATION_CHANNEL_IMPORTANT_ID,
WeKareConstants.NOTIFICATION_CHANNEL_IMPORTANT_NAME,
NotificationManager.IMPORTANCE_HIGH
)
val bitmap = applicationContext.vectorToBitmap(R.drawable.ic_launcher_background)
val resultPendingIntent: PendingIntent? = getResultPIntent()
notificationCompatBuilder
.setLargeIcon(bitmap).setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle(title)
.setContentText(message)
.setContentIntent(resultPendingIntent)
.setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(Color.parseColor("#f06d35"))
.setStyle(NotificationCompat.BigTextStyle().bigText(message))
.setAutoCancel(true)
notificationManager.notify(
System.currentTimeMillis().toInt(),
notificationCompatBuilder.build()
)
FL.d("Posted Medication Notification to the system ")
}
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 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'm trying to run a one time job with WorkManager. I don't know why but if I close my application from recent app menu job does not start until I start the application :/. Tried with samsung galaxy s9+. Android 9 Pie. Any help would be appreciated. As far as I know workmanager should work even when phone is restarted.
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.NOT_REQUIRED).build()
val request = OneTimeWorkRequestBuilder<NotificationJob>()
.addTag(item.uniqueId)
.setInputData(data)
.setConstraints(constraints)
//TODO revert
// .setInitialDelay(warrantyItem.reminderDate - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
.setInitialDelay(1, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(this#MainActivity).enqueue(request)
job is pretty simple. Just notification:
override fun doWork(): Result {
// Get the input
val uniqueId = inputData.getString(JOB_KEY)
if (uniqueId != null) {
sendNotification(uniqueId, "title", "subtitle")
}
return Result.success()
}
private fun sendNotification(uniqueId: String, title: String, subtitle: String) {
val intent = Intent(applicationContext, MainActivity::class.java)
intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
intent.putExtra(NOTIFICATION_ID, id)
val notificationManager =
applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val bitmap = applicationContext.vectorToBitmap(R.drawable.ic_warranty_icon)
val titleNotification = "$title"
val subtitleNotification = subtitle
val pendingIntent = getActivity(applicationContext, 0, intent, 0)
val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL)
.setLargeIcon(bitmap).setSmallIcon(R.drawable.ic_warranty_icon)
.setContentTitle(titleNotification).setContentText(subtitleNotification)
.setDefaults(DEFAULT_ALL).setContentIntent(pendingIntent).setAutoCancel(true)
notification.priority = PRIORITY_MAX
if (SDK_INT >= O) {
notification.setChannelId(NOTIFICATION_CHANNEL)
val ringtoneManager = getDefaultUri(TYPE_NOTIFICATION)
val audioAttributes = AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE)
.setContentType(CONTENT_TYPE_SONIFICATION).build()
val channel =
NotificationChannel(NOTIFICATION_CHANNEL, NOTIFICATION_NAME, IMPORTANCE_HIGH)
channel.enableLights(true)
channel.lightColor = RED
channel.enableVibration(true)
channel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
channel.setSound(ringtoneManager, audioAttributes)
notificationManager.createNotificationChannel(channel)
}
with(NotificationManagerCompat.from(applicationContext)) {
// notificationId is a unique int for each notification that you must define
notify(uniqueId, ID, notification.build())
}
}
Tried Searching With no Luck yet
i am testing on API 24
whats wrong with this code? the notification is showing correctly but is not a heads-up notification though i specified the priority to high and added vibration and sound correctly.
Please any help would be appreciated
Here is my code:
private fun sendNotification(title: String, message: String) {
val mNotificationCompatBuilder = mNotificationUtils.getNotificationBuilder(
title,
message,
false,
R.drawable.ic_logo,
NotificationUtils.ALERT_MESSAGES_ID
)
mNotificationCompatBuilder.setStyle(NotificationCompat.BigTextStyle().bigText(message))
mNotificationUtils.openTopActivityOnClick(mNotificationCompatBuilder, FTApplication.applicationContext())
mNotificationUtils.setSoundAndVibrate(mNotificationCompatBuilder)
val mNotificationManager = mContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mNotificationManager.notify((title+message).hashCode(), mNotificationCompatBuilder.build())
}
fun getNotificationBuilder(title: String, body: String, onGoing: Boolean, icon: Int, id: String): NotificationCompat.Builder {
val largeIcon = BitmapFactory.decodeResource(
context.resources,
R.drawable.ic_logo
)
return NotificationCompat.Builder(context, id)
.setSmallIcon(icon)
.setLargeIcon(largeIcon)
.setBadgeIconType(icon)
.setContentTitle(title)
.setContentText(body)
.setOngoing(onGoing)
.setAutoCancel(!onGoing)
.setWhen(System.currentTimeMillis())
}
fun setSoundAndVibrate(builder: NotificationCompat.Builder) {
#Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) builder.priority = NotificationManager.IMPORTANCE_HIGH
else builder.priority = Notification.PRIORITY_HIGH
builder.priority = NotificationCompat.PRIORITY_HIGH // heads-up test
val alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
builder.setSound(alarmSound)
builder.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
builder.setDefaults(Notification.DEFAULT_ALL)
}
here is the channel im creating (which is not used since as mentioned i am testing now on API 24)
#RequiresApi(Build.VERSION_CODES.O)
private fun createAlertMessageChannel() {
if (getManager()!!.getNotificationChannel(ALERT_CHANNEL_NAME) != null) {
return
}
// create alert channel
val alertChannel = NotificationChannel(
ALERT_MESSAGES_ID,
ALERT_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH
)
alertChannel.description = "For Alerting User of Events"
alertChannel.setShowBadge(false)
alertChannel.enableLights(true)
alertChannel.enableVibration(true)
//alertChannel.setSound(null, null)
// Sets the notification light color for notifications posted to this channel
alertChannel.lightColor = Color.GREEN
// Sets whether notifications posted to this channel appear on the lock screen or not
alertChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
getManager()!!.createNotificationChannel(alertChannel)
}