This way failure notifications won't overwrite the existing chat notifications (if there are any) as they also use thread id as notification id
198 lines
8.6 KiB
Kotlin
198 lines
8.6 KiB
Kotlin
package com.simplemobiletools.smsmessenger.helpers
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.app.Notification
|
|
import android.app.NotificationChannel
|
|
import android.app.NotificationManager.IMPORTANCE_HIGH
|
|
import android.app.PendingIntent
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.graphics.Bitmap
|
|
import android.media.AudioAttributes
|
|
import android.media.AudioManager
|
|
import android.media.RingtoneManager
|
|
import androidx.core.app.NotificationCompat
|
|
import androidx.core.app.Person
|
|
import androidx.core.app.RemoteInput
|
|
import com.simplemobiletools.commons.extensions.getProperPrimaryColor
|
|
import com.simplemobiletools.commons.extensions.notificationManager
|
|
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
|
|
import com.simplemobiletools.commons.helpers.isNougatPlus
|
|
import com.simplemobiletools.commons.helpers.isOreoPlus
|
|
import com.simplemobiletools.smsmessenger.R
|
|
import com.simplemobiletools.smsmessenger.activities.ThreadActivity
|
|
import com.simplemobiletools.smsmessenger.extensions.config
|
|
import com.simplemobiletools.smsmessenger.receivers.DirectReplyReceiver
|
|
import com.simplemobiletools.smsmessenger.receivers.MarkAsReadReceiver
|
|
|
|
class NotificationHelper(private val context: Context) {
|
|
|
|
private val notificationManager = context.notificationManager
|
|
private val soundUri get() = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
|
|
private val user = Person.Builder()
|
|
.setName(context.getString(R.string.me))
|
|
.build()
|
|
|
|
@SuppressLint("NewApi")
|
|
fun showMessageNotification(address: String, body: String, threadId: Long, bitmap: Bitmap?, sender: String?, alertOnlyOnce: Boolean = false) {
|
|
maybeCreateChannel(name = context.getString(R.string.channel_received_sms))
|
|
|
|
val notificationId = threadId.hashCode()
|
|
val contentIntent = Intent(context, ThreadActivity::class.java).apply {
|
|
putExtra(THREAD_ID, threadId)
|
|
}
|
|
val contentPendingIntent =
|
|
PendingIntent.getActivity(context, notificationId, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
|
|
|
|
val markAsReadIntent = Intent(context, MarkAsReadReceiver::class.java).apply {
|
|
action = MARK_AS_READ
|
|
putExtra(THREAD_ID, threadId)
|
|
}
|
|
val markAsReadPendingIntent =
|
|
PendingIntent.getBroadcast(context, notificationId, markAsReadIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
|
|
|
|
var replyAction: NotificationCompat.Action? = null
|
|
if (isNougatPlus()) {
|
|
val replyLabel = context.getString(R.string.reply)
|
|
val remoteInput = RemoteInput.Builder(REPLY)
|
|
.setLabel(replyLabel)
|
|
.build()
|
|
|
|
val replyIntent = Intent(context, DirectReplyReceiver::class.java).apply {
|
|
putExtra(THREAD_ID, threadId)
|
|
putExtra(THREAD_NUMBER, address)
|
|
}
|
|
|
|
val replyPendingIntent =
|
|
PendingIntent.getBroadcast(
|
|
context.applicationContext,
|
|
notificationId,
|
|
replyIntent,
|
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
|
|
)
|
|
replyAction = NotificationCompat.Action.Builder(R.drawable.ic_send_vector, replyLabel, replyPendingIntent)
|
|
.addRemoteInput(remoteInput)
|
|
.build()
|
|
}
|
|
|
|
val largeIcon = bitmap ?: if (sender != null) {
|
|
SimpleContactsHelper(context).getContactLetterIcon(sender)
|
|
} else {
|
|
null
|
|
}
|
|
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL).apply {
|
|
when (context.config.lockScreenVisibilitySetting) {
|
|
LOCK_SCREEN_SENDER_MESSAGE -> {
|
|
setLargeIcon(largeIcon)
|
|
setStyle(getMessagesStyle(address, body, notificationId, sender))
|
|
}
|
|
LOCK_SCREEN_SENDER -> {
|
|
setContentTitle(sender)
|
|
setLargeIcon(largeIcon)
|
|
val summaryText = context.getString(R.string.new_message)
|
|
setStyle(NotificationCompat.BigTextStyle().setSummaryText(summaryText).bigText(body))
|
|
}
|
|
}
|
|
|
|
color = context.getProperPrimaryColor()
|
|
setSmallIcon(R.drawable.ic_messenger)
|
|
setContentIntent(contentPendingIntent)
|
|
priority = NotificationCompat.PRIORITY_MAX
|
|
setDefaults(Notification.DEFAULT_LIGHTS)
|
|
setCategory(Notification.CATEGORY_MESSAGE)
|
|
setAutoCancel(true)
|
|
setOnlyAlertOnce(alertOnlyOnce)
|
|
setSound(soundUri, AudioManager.STREAM_NOTIFICATION)
|
|
}
|
|
|
|
if (replyAction != null && context.config.lockScreenVisibilitySetting == LOCK_SCREEN_SENDER_MESSAGE) {
|
|
builder.addAction(replyAction)
|
|
}
|
|
|
|
builder.addAction(R.drawable.ic_check_vector, context.getString(R.string.mark_as_read), markAsReadPendingIntent)
|
|
.setChannelId(NOTIFICATION_CHANNEL)
|
|
|
|
notificationManager.notify(notificationId, builder.build())
|
|
}
|
|
|
|
@SuppressLint("NewApi")
|
|
fun showSendingFailedNotification(recipientName: String, threadId: Long) {
|
|
maybeCreateChannel(name = context.getString(R.string.message_not_sent_short))
|
|
|
|
val notificationId = generateRandomId().hashCode()
|
|
val intent = Intent(context, ThreadActivity::class.java).apply {
|
|
putExtra(THREAD_ID, threadId)
|
|
}
|
|
val contentPendingIntent = PendingIntent.getActivity(context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
|
|
|
|
val summaryText = String.format(context.getString(R.string.message_sending_error), recipientName)
|
|
val largeIcon = SimpleContactsHelper(context).getContactLetterIcon(recipientName)
|
|
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL)
|
|
.setContentTitle(context.getString(R.string.message_not_sent_short))
|
|
.setContentText(summaryText)
|
|
.setColor(context.getProperPrimaryColor())
|
|
.setSmallIcon(R.drawable.ic_messenger)
|
|
.setLargeIcon(largeIcon)
|
|
.setStyle(NotificationCompat.BigTextStyle().bigText(summaryText))
|
|
.setContentIntent(contentPendingIntent)
|
|
.setPriority(NotificationCompat.PRIORITY_MAX)
|
|
.setDefaults(Notification.DEFAULT_LIGHTS)
|
|
.setCategory(Notification.CATEGORY_MESSAGE)
|
|
.setAutoCancel(true)
|
|
.setChannelId(NOTIFICATION_CHANNEL)
|
|
|
|
notificationManager.notify(notificationId, builder.build())
|
|
}
|
|
|
|
private fun maybeCreateChannel(name: String) {
|
|
if (isOreoPlus()) {
|
|
val audioAttributes = AudioAttributes.Builder()
|
|
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
|
|
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
|
.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
|
|
.build()
|
|
|
|
val id = NOTIFICATION_CHANNEL
|
|
val importance = IMPORTANCE_HIGH
|
|
NotificationChannel(id, name, importance).apply {
|
|
setBypassDnd(false)
|
|
enableLights(true)
|
|
setSound(soundUri, audioAttributes)
|
|
enableVibration(true)
|
|
notificationManager.createNotificationChannel(this)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getMessagesStyle(address: String, body: String, notificationId: Int, name: String?): NotificationCompat.MessagingStyle {
|
|
val sender = if (name != null) {
|
|
Person.Builder()
|
|
.setName(name)
|
|
.setKey(address)
|
|
.build()
|
|
} else {
|
|
null
|
|
}
|
|
|
|
return NotificationCompat.MessagingStyle(user).also { style ->
|
|
getOldMessages(notificationId).forEach {
|
|
style.addMessage(it)
|
|
}
|
|
val newMessage = NotificationCompat.MessagingStyle.Message(body, System.currentTimeMillis(), sender)
|
|
style.addMessage(newMessage)
|
|
}
|
|
}
|
|
|
|
private fun getOldMessages(notificationId: Int): List<NotificationCompat.MessagingStyle.Message> {
|
|
if (!isNougatPlus()) {
|
|
return emptyList()
|
|
}
|
|
val currentNotification = notificationManager.activeNotifications.find { it.id == notificationId }
|
|
return if (currentNotification != null) {
|
|
val activeStyle = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(currentNotification.notification)
|
|
activeStyle?.messages.orEmpty()
|
|
} else {
|
|
emptyList()
|
|
}
|
|
}
|
|
}
|