My chat application crashes with the above error when its time to display notifications
class MyFirebaseMessaging() : FirebaseMessagingService() {
lateinit var message:Notification.MessagingStyle.Message
val KEY_TEXT_REPLY = "key_text_reply"
#RequiresApi(Build.VERSION_CODES.KITKAT_WATCH)
override fun onMessageReceived(mRemoteMessage: RemoteMessage) {
Log.d("notif","called 1")
super.onMessageReceived(mRemoteMessage)
val sented = mRemoteMessage.data["sented"]
val user = mRemoteMessage.data["user"]
val sharedPref = getSharedPreferences("PREFS", Context.MODE_PRIVATE)
val currentOnlineUser = sharedPref.getString("currentUser", "none")
val firebaseUser = FirebaseAuth.getInstance().currentUser
if (firebaseUser !== null && sented == firebaseUser.uid) {
if (currentOnlineUser !== user) {
sendOreoNotification(mRemoteMessage)
}
}
}
#SuppressLint("NewApi")
#RequiresApi(Build.VERSION_CODES.KITKAT_WATCH)
private fun sendOreoNotification(mRemoteMessage: RemoteMessage) {
val user = mRemoteMessage.data["user"]
val body = mRemoteMessage.data["body"]
val name = mRemoteMessage.data["name"]
Log.d("notif","called")
val replyLabel = "Enter your reply here"
val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY)
.setLabel(replyLabel)
.build()
val j = user!!.replace("[\\D]".toRegex(), "").toInt()
val intent = Intent(this, HelperActivity::class.java)
val bundle = Bundle()
val pendingIntent =
PendingIntent.getActivity(this, j, intent, PendingIntent.FLAG_ONE_SHOT)
bundle.putString("userid", user)
intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
var i = 0
if (j > 0) {
i = j
}
val oreoNotification = OreoNotification(this)
// if (!appInForeground(applicationContext)) {
val icon_ = Icon.createWithResource(applicationContext,
android.R.drawable.ic_dialog_info)
val replyAction = Notification.Action.Builder(
icon_,
"Reply", pendingIntent)
.addRemoteInput(remoteInput)
.build()
val userP = android.app.Person.Builder().setName(name).setKey(user).setIcon(
Icon.createWithResource(this, R.mipmap.ic_launcher)).build()
message = body?.let {
Notification.MessagingStyle.Message(
it,
Date().time, userP)
}!!
Log.d("notif","recieved :${message.text}")
val style = Notification.MessagingStyle(userP).addMessage(message)
val builder: Notification.Builder =
oreoNotification.getOreoNotification(pendingIntent, replyAction, style)
Log.d("notif","$body")
oreoNotification.getManager?.notify(i, builder.build())
// } else {
// oreoNotification.getManager!!.cancel(i)
// }
}
private fun appInForeground(context: Context): Boolean {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val runningAppProcesses = activityManager.runningAppProcesses ?: return false
return runningAppProcesses.any { it.processName == context.packageName && it.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND }
}
}
OreoNotification.kt file
val getManager :NotificationManager? get() {
if(notificationManager==null){
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
return notificationManager
}
#TargetApi(Build.VERSION_CODES.O)
fun getOreoNotification(pendingIntent: PendingIntent?,replyAction:Notification.Action,style: Notification.Style)
:Notification.Builder{
return Notification.Builder(applicationContext, OreoNotification.CHANNEL_ID)
.setContentIntent(pendingIntent)
.setContentTitle("Vcare")
.setAutoCancel(true)
.addAction(replyAction)
.setStyle(style)
}
Logcat of the crash
2020-09-25 23:54:32.658 7896-8031/com.example.vcare E/AndroidRuntime: FATAL EXCEPTION: Firebase-Messaging-Intent-Handle
Process: com.example.vcare, PID: 7896
java.lang.RuntimeException: User must be valid and have a name.
at android.app.Notification$MessagingStyle.validate(Notification.java:6690)
at android.app.Notification$Builder.build(Notification.java:5573)
at com.example.vcare.notifications.MyFirebaseMessaging.sendOreoNotification(MyFirebaseMessaging.kt:105)
at com.example.vcare.notifications.MyFirebaseMessaging.onMessageReceived(MyFirebaseMessaging.kt:39)
at com.google.firebase.messaging.FirebaseMessagingService.dispatchMessage(com.google.firebase:firebase-messaging##20.2.4:85)
at com.google.firebase.messaging.FirebaseMessagingService.passMessageIntentToSdk(com.google.firebase:firebase-messaging##20.2.4:55)
at com.google.firebase.messaging.FirebaseMessagingService.handleMessageIntent(com.google.firebase:firebase-messaging##20.2.4:34)
at com.google.firebase.messaging.FirebaseMessagingService.handleIntent(com.google.firebase:firebase-messaging##20.2.4:23)
at com.google.firebase.messaging.EnhancedIntentService.lambda$processIntent$0$EnhancedIntentService(com.google.firebase:firebase-messaging##20.2.4:43)
at com.google.firebase.messaging.EnhancedIntentService$$Lambda$0.run(Unknown Source:6)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at com.google.android.gms.common.util.concurrent.zza.run(com.google.android.gms:play-services-basement##17.2.1:6)
at java.lang.Thread.run(Thread.java:764)
This line seems to be erroneous according to logcat (kt:105)
oreoNotification.getManager?.notify(i, builder.build())
the app crashes as soon as i send a message from another device and its time to display notifications.
What is this supposed to mean?
java.lang.RuntimeException: User must be valid and have a name.
Related
I'm trying to implement a notification always visible to play and stop a radio. I don't understand what I'm doing wrong. So Here i post my code:
I don't understand if something is missing for the notification to be shown or if I build the notification badly. In any case, the service works correctly if I activate the radio from the button I inserted in the Compose part, and if I close the app everything continues to work correctly, but I would like to understand why there is no notification at the top which allows you to activate or deactivate radio
Manifest
<service
android:name="com.example.appradio.services.MediaSessionService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</service>
<receiver android:name="androidx.media.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
MainActivity
Inside on create method:
ContextCompat.startForegroundService(
this#MainActivity.applicationContext,
Intent(this#MainActivity.applicationContext, MediaSessionService::class.java)
)
Service
class MediaSessionService : Service() {
var mediaPlayer: MediaPlayer? = null
private var mMediaNotificationManager: MediaNotificationManager? = null
private var mediaSession: MediaSessionCompat? = null
override fun onCreate() {
super.onCreate()
mediaPlayer = MediaPlayer()
mMediaNotificationManager = MediaNotificationManager(this)
mediaSession = MediaSessionCompat(this, "SOME_TAG")
mediaSession!!.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
mediaSession!!.setCallback(object : MediaSessionCompat.Callback() {
override fun onPlay() {
mediaPlayer!!.start()
}
override fun onPause() {
mediaPlayer!!.pause()
}
})
val notification = mMediaNotificationManager!!.getNotification(
metadata, state, mediaSession!!.sessionToken
)
startForeground(NOTIFICATION_ID, notification)
}
val metadata: MediaMetadataCompat
get() {
val builder = MediaMetadataCompat.Builder()
builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "artist")
builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title")
builder.putLong(
MediaMetadataCompat.METADATA_KEY_DURATION, mediaPlayer!!.duration
.toLong()
)
return builder.build()
}
private val state: PlaybackStateCompat
private get() {
val actions =
if (mediaPlayer!!.isPlaying) PlaybackStateCompat.ACTION_PAUSE else PlaybackStateCompat.ACTION_PLAY
val state =
if (mediaPlayer!!.isPlaying) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED
val stateBuilder = PlaybackStateCompat.Builder()
stateBuilder.setActions(actions)
stateBuilder.setState(
state,
mediaPlayer!!.currentPosition.toLong(),
1.0f,
SystemClock.elapsedRealtime()
)
return stateBuilder.build()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
if ("android.intent.action.MEDIA_BUTTON" == intent.action) {
val keyEvent = intent.extras!!["android.intent.extra.KEY_EVENT"] as KeyEvent?
if (keyEvent!!.keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
mediaPlayer!!.pause()
} else {
mediaPlayer!!.start()
}
}
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(intent: Intent): IBinder? {
return null
}
companion object {
const val NOTIFICATION_ID = 888
}
}
Notification Manager
class MediaNotificationManager(private val mService: MediaSessionService) {
private val mPlayAction: NotificationCompat.Action = NotificationCompat.Action(
R.drawable.ic_play,
"play",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PLAY
)
)
private val mPauseAction: NotificationCompat.Action = NotificationCompat.Action(
R.drawable.ic_stop,
"pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PAUSE
)
)
val notificationManager: NotificationManager = mService.getSystemService(Service.NOTIFICATION_SERVICE) as NotificationManager
fun getNotification(
metadata: MediaMetadataCompat,
state: PlaybackStateCompat,
token: MediaSessionCompat.Token
): Notification {
val isPlaying = state.state == PlaybackStateCompat.STATE_PLAYING
val description = metadata.description
val builder = buildNotification(state, token, isPlaying, description)
return builder.build()
}
private fun buildNotification(
state: PlaybackStateCompat,
token: MediaSessionCompat.Token,
isPlaying: Boolean,
description: MediaDescriptionCompat
): NotificationCompat.Builder {
if (isAndroidOOrHigher) {
createChannel()
}
val builder = NotificationCompat.Builder(mService, CHANNEL_ID)
builder.setStyle(
androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(token)
.setShowActionsInCompactView(0) // For backwards compatibility with Android L and earlier.
.setShowCancelButton(true)
.setCancelButtonIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_STOP
)
)
)
.setColor(ContextCompat.getColor(mService, R.color.black))
.setSmallIcon(R.drawable.ic_play) // Pending intent that is fired when user clicks on notification.
.setContentIntent(createContentIntent()) // Title - Usually Song name.
.setContentTitle(description.title) // When notification is deleted (when playback is paused and notification can be
// deleted) fire MediaButtonPendingIntent with ACTION_PAUSE.
.setDeleteIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService, PlaybackStateCompat.ACTION_PAUSE
)
)
builder.addAction(if (isPlaying) mPauseAction else mPlayAction)
return builder
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createChannel() {
if (notificationManager.getNotificationChannel(CHANNEL_ID) == null) {
val name: CharSequence = "MediaSession"
val description = "MediaSession and MediaPlayer"
val importance = NotificationManager.IMPORTANCE_LOW
val mChannel = NotificationChannel(CHANNEL_ID, name, importance)
mChannel.description = description
mChannel.enableLights(true)
mChannel.lightColor = Color.RED
mChannel.enableVibration(true)
mChannel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
notificationManager.createNotificationChannel(mChannel)
Timber.d("createChannel: New channel created")
} else {
Timber.d("createChannel: Existing channel reused")
}
}
private val isAndroidOOrHigher: Boolean
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
private fun createContentIntent(): PendingIntent {
val openUI = Intent(mService, MainActivity::class.java)
openUI.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
return PendingIntent.getActivity(
mService, REQUEST_CODE, openUI, PendingIntent.FLAG_CANCEL_CURRENT
)
}
companion object {
const val NOTIFICATION_ID = 412
private const val CHANNEL_ID = "com.example.appradio.musicplayer.channel"
private const val REQUEST_CODE = 501
}
init {
notificationManager.cancelAll()
}
}
I developing an application that receives a push notification when there is an emergency alert and needs to play a sound even when the device is in silent mode.
the sound aslo need to be custom.
I tried all kinds of ways without any success.
How do you do it on Android?
#AndroidEntryPoint
class AlertFirebaseMessagingService : FirebaseMessagingService() {
companion object {
private const val CHANNEL_ID = "HEADS_UP_NOTIFICATION"
}
#Inject
lateinit var dbRepository : DBRepositoryImpl
#Inject
lateinit var networkRepository : RepositoryImpl
override fun onNewToken(token : String) {
super.onNewToken(token)
Log.d("hofitTest", token)
AppSharedPreferences.saveToken(token)
if (AppSharedPreferences.getPhoneNumber().isEmpty().not()) {
sendRegistrationToServer(token)
}
}
// The method is called every time it receives a notification from Firebase.
#RequiresApi(Build.VERSION_CODES.O)
override fun onMessageReceived(
remoteMessage : RemoteMessage) {
Log.d("hofitTest", "From: ${remoteMessage.from}")
if (remoteMessage.data.isNullOrEmpty().not()) {
val type = remoteMessage.data["type"] ?: "1"
sendNotification(
remoteMessage.data["title"] ?: "", remoteMessage.data["body"] ?: "", type)
Log.d("hofitTest", "Message data payload: ${remoteMessage.data}")
}
// } else if(remoteMessage.notification != null){
// //TODO change def 1
// sendNotification(remoteMessage.notification?.title ?: "", remoteMessage.notification?.body ?: "", "1")
// }
super.onMessageReceived(remoteMessage)
}
private fun sendNotification(title : String, body : String, type : String) {
createChannel()
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
notification.setContentTitle(title)
notification.setContentText(body)
notification.priority = NotificationCompat.PRIORITY_HIGH
val intent = Intent(this, SplashActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
notification.setSmallIcon(R.drawable.icon_lifeguard_orange_rounded)
notification.color = ContextCompat.getColor(this, R.color.orange)
notification.setContentIntent(pendingIntent)
notification.setAutoCancel(true)
NotificationManagerCompat.from(this).notify(Random.nextInt(), notification.build())
CoroutineScope(Dispatchers.IO).launch {
dbRepository.saveEvent(
NotificationAlert(name = title ?: "", body = body ?: "", type = type, date = Date().time))
a()
}
}
private fun createChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID, "Heads Up Notification", NotificationManager.IMPORTANCE_HIGH)
channel.enableLights(true)
channel.enableVibration(true)
channel.lightColor = Color.BLUE
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
}
}
}
use RingtoneManager - this not works in silent mode
try {
val notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
val r = RingtoneManager.getRingtone(LifeguardApplication.context, notification)
r.play()
} catch (e : Exception) {
e.printStackTrace()
}
I am trying to send a data notification to my app and then use the data therein to open a fragment:
override fun onMessageReceived(message: RemoteMessage) {
Timber.d("onMessageReceived")
try {
val data = message.data
if (data != null && (data.containsKey(KEY_MSG) || data.containsKey(KEY_URL))) {
val url = data[KEY_URL]
if (!url.isNullOrEmpty()) {
val clickAction = message.notification?.clickAction
val intent = Intent(clickAction)
intent.putExtra(KEY_URL, url).putUseStateExtra(UseState(UseState.COME_FROM_NOTIFICATION)).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
} else {
sendNotification(data)
}
}
} catch (e: Throwable) {
Timber.e(e, "We didn't send the notification because ${e.message}")
}
}
I then after onMessageReceived() is called I build the notification and send it with the following methods. One to parse out the payload:
private fun sendNotification(data: Map<String, String>) {
Timber.d("Notification sent: $data type: ${data[KEY_CLOUD8_TYPE]}")
if (Interactors.preferences.notificationsEnabled == true) {
Timber.d(data.toString())
val title = data[KEY_TITLE]
val msg = data[KEY_MSG]
var cloud8Type = data[KEY_CLOUD8_TYPE] ?: ""
var notificationType = data[NOTIFICATION_TYPE] ?: ""
val campaignId = (data[KEY_CAMPAIGN_ID] ?: "0")
val url = data[KEY_URL]
if (!url.isNullOrBlank()) {
cloud8Type = Cloud8Type.Link
}
sendNotification(title, msg, cloud8Type, notificationType, campaignId, url)
}
}
One to build the notification:
private fun sendNotification(title: String?, message: String?, cloud8Type: String, notificationType: String, offerId: String, url: String?) {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "Main"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(channelId, "My Notifications", NotificationManager.IMPORTANCE_HIGH)
// Configure the notification channel.
notificationChannel.description = "Channel description"
notificationChannel.enableVibration(true)
notificationManager.createNotificationChannel(notificationChannel)
}
val pendingIntent = getNotificationIntent(cloud8Type, notificationType, offerId, url)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(this, channelId)
notificationBuilder.setSmallIcon(R.drawable.ic_notification)
notificationBuilder.setContentTitle(title)
notificationBuilder.setContentText(message)
notificationBuilder.setAutoCancel(true)
notificationBuilder.setSound(defaultSoundUri)
notificationBuilder.setContentIntent(pendingIntent)
notificationManager.notify(0, notificationBuilder.build())
}
And two to use the content of the payload to build an intent:
private fun getNotificationIntent(cloud8Type: String, notificationType: String, offerId: String, url: String?): PendingIntent {
Timber.d("Notification type: $cloud8Type}")
val useState = UseState(UseState.COME_FROM_NOTIFICATION)
val intent = getNotificationIntent(this, cloud8Type, notificationType, useState, offerId, url = url)
return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
internal fun getNotificationIntent(
context: Context,
cloud8Type: String?,
notificationType: String,
useState: UseState,
offerId: String?,
url: String?
): Intent {
var intent = Intent()
when (cloud8Type) {
Cloud8Type.NewOffer, Cloud8Type.NewChallengeOffer, Cloud8Type.Link ->
intent = StartActivity.newInstance(context, useState, offerId, url)
Cloud8Type.DailyEarning, Cloud8Type.YouDidIt, Cloud8Type.FundsTransfered, Cloud8Type.OfferPayment, Cloud8Type.OfferDonation -> {
intent = if (Interactors.preferences.the8CloudSdkInfo.showPayoutTab) {
openSponsorTree(context, useState, ASponsorTree.TAB_PAYOUT, null)
} else {
APayoutMain.newIntent(context)
}
}
Cloud8Type.NewOffers ->
intent = openSponsorTree(context, useState, ASponsorTree.TAB_FEED, null)
else -> {
when (notificationType) {
NotificationType.Payment -> intent = openSponsorTree(context, useState, ASponsorTree.TAB_PAYOUT, null)
}
}
}
return intent
}
I'm trying to debug the payload being received when the notification comes, but none of my log statements are showing up when the app is closed. Is there any way to see what is coming back with the RemoteMessage in onMessageReceived()? Is there anyting else I should know about how to accomplish what I want to accomplish?
I added the "notification" attribute to the payload and gave it a click_action, and then intercepted it in my start activity.
Please don't send your payload in the notification attribute. It will only hit when the app is in the foreground state. For getting notification also while in background state you have to send your payload into data attribute, not notification.
Example :
{
"condition": " Better now",
"priority" : "normal",
"time_to_live" : 0,,
"data" : {
"id" : 1,
"text" : "text is here!",
"link" : "www.gmail.com"
}
}
I am new to android studio. I was trying to create a notification when user click on a button embedded in recyclerView
user has to give time in Hours and Minutes only. I take user input as a string. And input is stored in SQLite database.
notifyMeUpdate function is working fine but Toast in Receiver class is not shown(I think Receiver class is not triggered).
Please ignore typo
Here is my code:
class User{
var id = 0
var time = ""
var amp = ""
var text = ""
var notifyMe = false
}
My User Class:
fun notifyMeUpdate(user: User) {
alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
var hour = 0
var min = 0
for(i in 0 until user.time.length) {
if (user.time[i] == ':'){
var str = user.time.subSequence(0, i).toString()
var minStr = user.time.subSequence(i+1, user.time.length).toString()
if(user.amp == "AM"){
hour = str.toInt()
min = minStr.toInt()
}
else{
hour = str.toInt() + 12
min = minStr.toInt()
}
}
}
val cur_cal: Calendar = GregorianCalendar()
cur_cal.timeInMillis = System.currentTimeMillis()
val cal: Calendar = GregorianCalendar()
cal.add(Calendar.DAY_OF_YEAR, cur_cal[Calendar.DAY_OF_YEAR])
cal[Calendar.HOUR_OF_DAY] = hour
cal[Calendar.MINUTE] = min
cal[Calendar.SECOND] = 0
cal[Calendar.MILLISECOND] = cur_cal[Calendar.MILLISECOND]
cal[Calendar.DATE] = cur_cal[Calendar.DATE]
cal[Calendar.MONTH] = cur_cal[Calendar.MONTH]
val intent = Intent(context, Receiever::class.java)
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.timeInMillis, pendingIntent)
}
And here is my Receiever class
class Receiever: BroadcastReceiver(){
lateinit var notificationManager: NotificationManager
lateinit var notificationChannel: NotificationChannel
lateinit var builder: Notification.Builder
private var channelId = "package com.example.recyclerpractise"
private var description = "Text Notification"
override fun onReceive(context: Context?, intent: Intent?){
Toast.makeText(context, "Triggred", Toast.LENGTH_SHORT).show()
val intent = Intent(context, LauncherActivity::class.java)
val pintent = PendingIntent.getActivity(context, 0 , intent, PendingIntent.FLAG_UPDATE_CURRENT)
if (context != null) {
notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationChannel =
NotificationChannel(channelId, description, NotificationManager.IMPORTANCE_HIGH)
notificationChannel.enableLights(true)
notificationChannel.lightColor = Color.GREEN
notificationChannel.enableVibration(true)
notificationManager.createNotificationChannel(notificationChannel)
if (context != null) {
builder = Notification.Builder(context, channelId)
.setContentTitle("Hello Moto")
.setContentText("Test Notification")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.ic_launcher_foreground))
.setContentIntent(pintent)
}
else{
builder = Notification.Builder(context)
.setContentTitle("Hello Moto")
.setContentText("Test Notification")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setLargeIcon(BitmapFactory.decodeResource(context?.resources, R.drawable.ic_launcher_foreground))
.setContentIntent(pintent)
}
notificationManager.notify(19867, builder.build())
}
}
}
Here is my onBindViewHolder of recycler list
override fun onBindViewHolder(p0: ViewHolder, p1: Int) {
val user: User = userList[p1]
p0.time.text = user.time
p0.text.text = user.text
p0.img.setOnClickListener{
val popup = PopupMenu(activity, p0.img)
popup.inflate(R.menu.schedulemenu)
popup.setOnMenuItemClickListener {
when(it.itemId){
R.id.menu_delete->{
activity.dbHelper.deleteData(userList[p1].id.toLong())
activity.refreshList()
}
R.id.menu_update->{
activity.updateUser(userList[p1])
}
R.id.notify->{
activity.notifyMeUpdate(userList[p1])
}
}
true
}
popup.show()
}
}
Thanks for Help
Thanks for all who tried. After several hours of debugging. I found that I did a very stupid mistake that haven't register my receiver in manifest file now it's working fine.
I am trying to start two work request, one worker sends a request to the server for generating excel file and obtains URL for download. Another work starts after previous and must to download that file. First work starts and returns Result.SUCCESS.
Problem is another WorkRequest just not execute. LoadInvoiceFileWorker do nothing.
What I need to do or what I do wrong?
Here is my code:
InvoiceDetailsViewModel:
class InvoiceDetailsViewModel : ViewModel() {
private val mWorkManager: WorkManager = WorkManager.getInstance()
fun generateAndLoadExcel(invoiceId: Int, invoiceName: String, enterpriseId: Int) {
val genInvoiceWorkerBuilder = OneTimeWorkRequest.Builder(GenerateExcelWorker::class.java)
genInvoiceWorkerBuilder.setInputData(createInputDataForGenerateExcel(invoiceId, invoiceName, enterpriseId))
val constraintBuilder = Constraints.Builder()
//constraintBuilder.setRequiredNetworkType(NetworkType.CONNECTED)
genInvoiceWorkerBuilder.setConstraints(constraintBuilder.build())
val continuation = mWorkManager.beginWith(
genInvoiceWorkerBuilder.build()
)
val loadFileWorkerBuilder = OneTimeWorkRequest.Builder(LoadInvoiceFileWorker::class.java)
//loadFileWorkerBuilder.setConstraints(Constraints.NONE)
continuation.then(loadFileWorkerBuilder.build())
continuation.enqueue()
}
private fun createInputDataForGenerateExcel(invoiceId: Int, invoiceName: String, enterpriseId: Int): Data {
val builder = Data.Builder()
builder.putInt(WorkerConstants.INVOICE_ID, invoiceId)
builder.putString(WorkerConstants.INVOICE_NAME, invoiceName)
builder.putInt(WorkerConstants.ENTERPRISE_ID, enterpriseId)
return builder.build()
}
}
GenerateExcelWorker:
class GenerateExcelWorker : Worker() {
companion object {
private val TAG = GenerateExcelWorker::class.java.simpleName
}
override fun doWork(): Result {
val appCont = applicationContext
val tokenType = PreferenceUtil.getString(TOKEN_TYPE, appCont, R.string.shared_pref_name)
val accessToken = PreferenceUtil.getString(ACCESS_TOKEN, appCont, R.string.shared_pref_name)
val enterpriseId = inputData.getInt(WorkerConstants.ENTERPRISE_ID, 0)
val invoiceId = inputData.getInt(WorkerConstants.INVOICE_ID, 0)
val invoiceName = inputData.getString(WorkerConstants.INVOICE_NAME)
makeStatusNotification(applicationContext, invoiceId, invoiceName
?: ("Invoice ${invoiceId.str()}"))
try {
val rd = RequestData()
rd.putValue("authorization", "$tokenType $accessToken", RequestData.TYPE_HEADER)
rd.putValue(FTUrls.SendingParameters.ENTERPRISE_ID, enterpriseId, RequestData.TYPE_PATH)
rd.putValue(FTUrls.SendingParameters.INVOICE_ID, invoiceId, RequestData.TYPE_PATH)
val excelUrl = InvoiceManager().generateIncomeInvoiceExcel(rd)
outputData = Data.Builder().putString(WorkerConstants.FILE_URL, excelUrl).build()
return Result.SUCCESS
} catch (t: Throwable) {
Log.e(TAG, "Error generating excel file for invoice $invoiceName ($invoiceId)", t)
if (t is UnauthenticatedException) {
outputData = Data.Builder().putBoolean(WorkerConstants.FILE_URL, true).build()
} else {
ExceptionLogger.logException(t)
Toast.makeText(applicationContext, t.message, Toast.LENGTH_SHORT).show()
}
return Result.FAILURE
}
}
private fun makeStatusNotification(context: Context, invoiceId: Int, invoiceTitle: String) {
// Make a channel if necessary
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
val name = WorkerConstants.NOTIFICATION_CHANNEL_NAME
val description = WorkerConstants.NOTIFICATION_CHANNEL_DESCRIPTION
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(WorkerConstants.CHANNEL_ID, name, importance)
channel.description = description
// Add the channel
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
val builder = NotificationCompat.Builder(context, WorkerConstants.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_autorenew_blue)
.setContentTitle(WorkerConstants.NOTIFICATION_TITLE)
.setContentText(String.format(WorkerConstants.NOTIFICATION_TEXT, invoiceTitle))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setVibrate(LongArray(0))
NotificationManagerCompat.from(context).notify(invoiceId, builder.build())
}
}
LoadInvoiceFileWorker:
class LoadInvoiceFileWorker : Worker() {
companion object {
private val TAG = LoadInvoiceFileWorker::class.java.simpleName
}
override fun doWork(): Result {
try {
val fileUrl = inputData.getString(WorkerConstants.FILE_URL)
val invoiceId = inputData.getInt(WorkerConstants.INVOICE_ID, 0)
val invoiceName = inputData.getString(WorkerConstants.INVOICE_NAME)
val r = DownloadManager.Request(Uri.parse(fileUrl))
// This put the download in the same Download dir the browser uses
r.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, invoiceName
?: ("Invoice ${invoiceId.str()}"))
// Notify user when download is completed
r.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
// Start download
val dm = applicationContext.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager?
if (dm != null) {
dm.enqueue(r)
} else {
Log.w(TAG, "Download manager not exists for load invoice excel file")
ToastError(applicationContext, R.string.download_manager_not_found, Toast.LENGTH_SHORT)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(fileUrl))
try {
applicationContext.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Log.e(TAG, "Error open browser for view invoice excel file", e)
ToastError(applicationContext, R.string.browser_not_found, Toast.LENGTH_SHORT)
}
}
clearGenerateFileNotification(invoiceId)
return Result.SUCCESS
} catch (t: Throwable) {
Log.e(TAG, "Error loading excel generated file", t)
ExceptionLogger.logException(t)
ToastError(applicationContext, R.string.error_during_loading_file, Toast.LENGTH_SHORT)
return Result.FAILURE
}
}
private fun clearGenerateFileNotification(invoiceId: Int) {
val notificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(invoiceId)
}
}
WorkerConstants:
object WorkerConstants {
const val ENTERPRISE_ID = "enterprise_id"
const val INVOICE_ID = "invoice_id"
const val INVOICE_NAME = "invoice_name"
const val FILE_URL = "file_url"
const val UNIQUE_WORK_NAME_FOR_INVOICE = "generate_and_load_excel_for_invoice"
const val NOTIFICATION_CHANNEL_NAME = "GenerateExcelWorker Notifications"
const val NOTIFICATION_CHANNEL_DESCRIPTION = "Shows notifications whenever work starts"
const val NOTIFICATION_TITLE = "Генерація ексель файла"
const val NOTIFICATION_TEXT = "Генерація ексель файла накладної %s"
const val CHANNEL_ID = "GENERATE_INVOICE_NOTIFICATION"
}
Ok, I found my mistake. Instead of this:
...
continuation.then(loadFileWorkerBuilder.build())
continuation.enqueue()
I need make this:
...
continuation = continuation.then(loadFileWorkerBuilder.build())
continuation.enqueue()
I was apllying enqueue() for first continuation of one request. Method WorkContinuation.then() returns new object which contains old continuation with new added request.