Merge pull request #520 from Naveen3Singh/notification_improvements

Improve and organise notification related code
This commit is contained in:
Tibor Kaputa 2022-12-11 12:26:58 +01:00 committed by GitHub
commit 7ea57de52b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 340 additions and 302 deletions

View File

@ -70,6 +70,7 @@ dependencies {
implementation "me.leolin:ShortcutBadger:1.1.22"
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'com.googlecode.ez-vcard:ez-vcard:0.11.3'
implementation 'androidx.lifecycle:lifecycle-process:2.5.1'
kapt "androidx.room:room-compiler:2.4.3"
implementation "androidx.room:room-runtime:2.4.3"

View File

@ -89,15 +89,18 @@ class MainActivity : SimpleActivity() {
super.onResume()
setupToolbar(main_toolbar)
if (storedTextColor != getProperTextColor()) {
(conversations_list.adapter as? ConversationsAdapter)?.updateTextColor(getProperTextColor())
getOrCreateConversationsAdapter().apply {
if (storedTextColor != getProperTextColor()) {
updateTextColor(getProperTextColor())
}
if (storedFontSize != config.fontSize) {
updateFontSize()
}
updateDrafts()
}
if (storedFontSize != config.fontSize) {
(conversations_list.adapter as? ConversationsAdapter)?.updateFontSize()
}
(conversations_list.adapter as? ConversationsAdapter)?.updateDrafts()
updateTextColors(main_coordinator)
val properPrimaryColor = getProperPrimaryColor()
@ -285,6 +288,25 @@ class MainActivity : SimpleActivity() {
}
}
private fun getOrCreateConversationsAdapter(): ConversationsAdapter {
var currAdapter = conversations_list.adapter
if (currAdapter == null) {
hideKeyboard()
currAdapter = ConversationsAdapter(
activity = this,
recyclerView = conversations_list,
onRefresh = { notifyDatasetChanged() },
itemClick = { handleConversationClick(it) }
)
conversations_list.adapter = currAdapter
if (areSystemAnimationsEnabled) {
conversations_list.scheduleLayoutAnimation()
}
}
return currAdapter as ConversationsAdapter
}
private fun setupConversations(conversations: ArrayList<Conversation>) {
val hasConversations = conversations.isNotEmpty()
val sortedConversations = conversations.sortedWith(
@ -301,36 +323,32 @@ class MainActivity : SimpleActivity() {
no_conversations_placeholder_2.beGone()
}
val currAdapter = conversations_list.adapter
if (currAdapter == null) {
hideKeyboard()
ConversationsAdapter(this, conversations_list) {
Intent(this, ThreadActivity::class.java).apply {
val conversation = it as Conversation
putExtra(THREAD_ID, conversation.threadId)
putExtra(THREAD_TITLE, conversation.title)
startActivity(this)
}
}.apply {
conversations_list.adapter = this
updateConversations(sortedConversations)
}
if (areSystemAnimationsEnabled) {
conversations_list.scheduleLayoutAnimation()
}
} else {
try {
(currAdapter as ConversationsAdapter).updateConversations(sortedConversations) {
if (currAdapter.currentList.isEmpty()) {
try {
getOrCreateConversationsAdapter().apply {
updateConversations(sortedConversations) {
if (currentList.isEmpty()) {
conversations_fastscroller.beGone()
no_conversations_placeholder.text = getString(R.string.no_conversations_found)
no_conversations_placeholder.beVisible()
no_conversations_placeholder_2.beVisible()
}
}
} catch (ignored: Exception) {
}
} catch (ignored: Exception) {
}
}
@SuppressLint("NotifyDataSetChanged")
private fun notifyDatasetChanged() {
getOrCreateConversationsAdapter().notifyDataSetChanged()
}
private fun handleConversationClick(any: Any) {
Intent(this, ThreadActivity::class.java).apply {
val conversation = any as Conversation
putExtra(THREAD_ID, conversation.threadId)
putExtra(THREAD_TITLE, conversation.title)
startActivity(this)
}
}

View File

@ -1,5 +1,6 @@
package com.simplemobiletools.smsmessenger.adapters
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Typeface
import android.os.Parcelable
@ -31,8 +32,9 @@ import com.simplemobiletools.smsmessenger.models.Conversation
import kotlinx.android.synthetic.main.item_conversation.view.*
class ConversationsAdapter(
activity: SimpleActivity, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit
) : MyRecyclerViewListAdapter<Conversation>(activity, recyclerView, ConversationDiffCallback(), itemClick), RecyclerViewFastScroller.OnPopupTextUpdate {
activity: SimpleActivity, recyclerView: MyRecyclerView, onRefresh: () -> Unit, itemClick: (Any) -> Unit
) : MyRecyclerViewListAdapter<Conversation>(activity, recyclerView, ConversationDiffCallback(), itemClick, onRefresh),
RecyclerViewFastScroller.OnPopupTextUpdate {
private var fontSize = activity.getTextSize()
private var drafts = HashMap<Long, String?>()
@ -40,7 +42,9 @@ class ConversationsAdapter(
init {
setupDragListener(true)
fetchDrafts(drafts)
ensureBackgroundThread {
fetchDrafts(drafts)
}
setHasStableIds(true)
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
@ -314,11 +318,15 @@ class ConversationsAdapter(
}
fun updateDrafts() {
val newDrafts = HashMap<Long, String?>()
fetchDrafts(newDrafts)
if (drafts.hashCode() != newDrafts.hashCode()) {
drafts = newDrafts
notifyDataSetChanged()
ensureBackgroundThread {
val newDrafts = HashMap<Long, String?>()
fetchDrafts(newDrafts)
if (drafts.hashCode() != newDrafts.hashCode()) {
drafts = newDrafts
activity.runOnUiThread {
notifyDataSetChanged()
}
}
}
}

View File

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.graphics.Typeface
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.util.Size
@ -18,7 +19,6 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.FitCenter
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
@ -291,7 +291,20 @@ class ThreadAdapter(
thread_message_body.setLinkTextColor(context.getProperPrimaryColor())
if (!activity.isFinishing && !activity.isDestroyed) {
SimpleContactsHelper(context).loadContactImage(message.senderPhotoUri, thread_message_sender_photo, message.senderName)
val contactLetterIcon = SimpleContactsHelper(context).getContactLetterIcon(message.senderName)
val placeholder = BitmapDrawable(context.resources, contactLetterIcon)
val options = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.error(placeholder)
.centerCrop()
Glide.with(context)
.load(message.senderPhotoUri)
.placeholder(placeholder)
.apply(options)
.apply(RequestOptions.circleCropTransform())
.into(thread_message_sender_photo)
}
}
}
@ -341,7 +354,6 @@ class ThreadAdapter(
var builder = Glide.with(context)
.load(uri)
.transition(DrawableTransitionOptions.withCrossFade())
.apply(options)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {

View File

@ -1,22 +1,13 @@
package com.simplemobiletools.smsmessenger.extensions
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.media.AudioAttributes
import android.media.AudioManager
import android.media.RingtoneManager
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.ContactsContract.PhoneLookup
@ -24,15 +15,15 @@ import android.provider.OpenableColumns
import android.provider.Telephony.*
import android.telephony.SubscriptionManager
import android.text.TextUtils
import androidx.core.app.NotificationCompat
import androidx.core.app.RemoteInput
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import com.klinker.android.send_message.Transaction.getAddressSeparator
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.PhoneNumber
import com.simplemobiletools.commons.models.SimpleContact
import com.simplemobiletools.smsmessenger.R
import com.simplemobiletools.smsmessenger.activities.ThreadActivity
import com.simplemobiletools.smsmessenger.databases.MessagesDatabase
import com.simplemobiletools.smsmessenger.helpers.*
import com.simplemobiletools.smsmessenger.helpers.AttachmentUtils.parseAttachmentNames
@ -41,8 +32,6 @@ import com.simplemobiletools.smsmessenger.interfaces.ConversationsDao
import com.simplemobiletools.smsmessenger.interfaces.MessageAttachmentsDao
import com.simplemobiletools.smsmessenger.interfaces.MessagesDao
import com.simplemobiletools.smsmessenger.models.*
import com.simplemobiletools.smsmessenger.receivers.DirectReplyReceiver
import com.simplemobiletools.smsmessenger.receivers.MarkAsReadReceiver
import me.leolin.shortcutbadger.ShortcutBadger
import java.io.FileNotFoundException
@ -58,6 +47,8 @@ val Context.messageAttachmentsDB: MessageAttachmentsDao get() = getMessagesDB().
val Context.messagesDB: MessagesDao get() = getMessagesDB().MessagesDao()
val Context.notificationHelper get() = NotificationHelper(this)
fun Context.getMessages(
threadId: Long,
getImageResolutions: Boolean,
@ -713,12 +704,12 @@ fun Context.getThreadId(addresses: Set<String>): Long {
}
fun Context.showReceivedMessageNotification(address: String, body: String, threadId: Long, bitmap: Bitmap?) {
val privateCursor = getMyContactsCursor(false, true)
val privateCursor = getMyContactsCursor(favoritesOnly = false, withPhoneNumbersOnly = true)
ensureBackgroundThread {
val senderName = getNameFromAddress(address, privateCursor)
Handler(Looper.getMainLooper()).post {
showMessageNotification(address, body, threadId, bitmap, senderName)
notificationHelper.showMessageNotification(address, body, threadId, bitmap, senderName)
}
}
}
@ -746,130 +737,26 @@ fun Context.getContactFromAddress(address: String, callback: ((contact: SimpleCo
}
}
@SuppressLint("NewApi")
fun Context.showMessageNotification(address: String, body: String, threadId: Long, bitmap: Bitmap?, sender: String) {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
if (isOreoPlus()) {
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
.build()
val name = getString(R.string.channel_received_sms)
val importance = NotificationManager.IMPORTANCE_HIGH
NotificationChannel(NOTIFICATION_CHANNEL, name, importance).apply {
setBypassDnd(false)
enableLights(true)
setSound(soundUri, audioAttributes)
enableVibration(true)
notificationManager.createNotificationChannel(this)
}
fun Context.getNotificationBitmap(photoUri: String): Bitmap? {
val size = resources.getDimension(R.dimen.notification_large_icon_size).toInt()
if (photoUri.isEmpty()) {
return null
}
val intent = Intent(this, ThreadActivity::class.java).apply {
putExtra(THREAD_ID, threadId)
}
val options = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
val pendingIntent = PendingIntent.getActivity(this, threadId.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
val summaryText = getString(R.string.new_message)
val markAsReadIntent = Intent(this, MarkAsReadReceiver::class.java).apply {
action = MARK_AS_READ
putExtra(THREAD_ID, threadId)
}
val markAsReadPendingIntent =
PendingIntent.getBroadcast(this, threadId.hashCode(), markAsReadIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
var replyAction: NotificationCompat.Action? = null
if (isNougatPlus()) {
val replyLabel = getString(R.string.reply)
val remoteInput = RemoteInput.Builder(REPLY)
.setLabel(replyLabel)
.build()
val replyIntent = Intent(this, DirectReplyReceiver::class.java).apply {
putExtra(THREAD_ID, threadId)
putExtra(THREAD_NUMBER, address)
}
val replyPendingIntent =
PendingIntent.getBroadcast(applicationContext, threadId.hashCode(), 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 ?: SimpleContactsHelper(this).getContactLetterIcon(sender)
val builder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL).apply {
when (config.lockScreenVisibilitySetting) {
LOCK_SCREEN_SENDER_MESSAGE -> {
setLargeIcon(largeIcon)
setStyle(getMessagesStyle(notificationManager, threadId, sender, body))
}
LOCK_SCREEN_SENDER -> {
setContentTitle(sender)
setLargeIcon(largeIcon)
setStyle(NotificationCompat.BigTextStyle().setSummaryText(summaryText).bigText(body))
}
}
color = getProperPrimaryColor()
setSmallIcon(R.drawable.ic_messenger)
setContentIntent(pendingIntent)
priority = NotificationCompat.PRIORITY_MAX
setDefaults(Notification.DEFAULT_LIGHTS)
setCategory(Notification.CATEGORY_MESSAGE)
setAutoCancel(true)
setSound(soundUri, AudioManager.STREAM_NOTIFICATION)
}
if (replyAction != null && config.lockScreenVisibilitySetting == LOCK_SCREEN_SENDER_MESSAGE) {
builder.addAction(replyAction)
}
builder.addAction(R.drawable.ic_check_vector, getString(R.string.mark_as_read), markAsReadPendingIntent)
.setChannelId(NOTIFICATION_CHANNEL)
notificationManager.notify(threadId.hashCode(), builder.build())
}
private fun Context.getMessagesStyle(
notificationManager: NotificationManager,
threadId: Long,
sender: String,
body: String
): NotificationCompat.MessagingStyle {
val oldMessages = getOldMessages(notificationManager, threadId)
val messages = NotificationCompat.MessagingStyle(getString(R.string.me))
oldMessages.forEach {
messages.addMessage(it)
}
val currentMessage = NotificationCompat.MessagingStyle.Message(body, System.currentTimeMillis(), sender)
messages.addMessage(currentMessage)
return messages
}
private fun getOldMessages(notificationManager: NotificationManager, threadId: Long): List<NotificationCompat.MessagingStyle.Message> {
if (!isNougatPlus()) {
return arrayListOf()
}
val currentNotification = notificationManager.activeNotifications.find { it.id == threadId.hashCode() }
return if (currentNotification != null) {
val messages = currentNotification.notification.extras.getParcelableArray(NotificationCompat.EXTRA_MESSAGES)
val result = arrayListOf<NotificationCompat.MessagingStyle.Message>()
messages?.forEach {
val bundle = it as Bundle
val sender = bundle.getCharSequence("sender")
val text = bundle.getCharSequence("text")
val time = bundle.getLong("time")
val message = NotificationCompat.MessagingStyle.Message(text, time, sender)
result.add(message)
}
return result
} else {
arrayListOf()
return try {
Glide.with(this)
.asBitmap()
.load(photoUri)
.apply(options)
.apply(RequestOptions.circleCropTransform())
.into(size, size)
.get()
} catch (e: Exception) {
null
}
}

View File

@ -0,0 +1,198 @@
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()
}
}
}

View File

@ -4,58 +4,58 @@ import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import androidx.core.app.RemoteInput
import com.klinker.android.send_message.Transaction
import com.simplemobiletools.commons.extensions.notificationManager
import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.smsmessenger.extensions.*
import com.simplemobiletools.smsmessenger.helpers.REPLY
import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
import com.simplemobiletools.smsmessenger.helpers.THREAD_NUMBER
import com.simplemobiletools.smsmessenger.helpers.getSendMessageSettings
import com.simplemobiletools.smsmessenger.helpers.sendMessage
class DirectReplyReceiver : BroadcastReceiver() {
@SuppressLint("MissingPermission")
override fun onReceive(context: Context, intent: Intent) {
val address = intent.getStringExtra(THREAD_NUMBER)
val threadId = intent.getLongExtra(THREAD_ID, 0L)
var msg = RemoteInput.getResultsFromIntent(intent)?.getCharSequence(REPLY)?.toString() ?: return
var body = RemoteInput.getResultsFromIntent(intent)?.getCharSequence(REPLY)?.toString() ?: return
msg = context.removeDiacriticsIfNeeded(msg)
body = context.removeDiacriticsIfNeeded(body)
val settings = context.getSendMessageSettings()
if (address != null) {
var subscriptionId: Int? = null
val availableSIMs = context.subscriptionManagerCompat().activeSubscriptionInfoList
if ((availableSIMs?.size ?: 0) > 1) {
val currentSIMCardIndex = context.config.getUseSIMIdAtNumber(address)
val wantedId = availableSIMs.getOrNull(currentSIMCardIndex)
if (wantedId != null) {
settings.subscriptionId = wantedId.subscriptionId
subscriptionId = wantedId.subscriptionId
}
}
}
val transaction = Transaction(context, settings)
val message = com.klinker.android.send_message.Message(msg, address)
ensureBackgroundThread {
try {
context.sendMessage(body, listOf(address), subscriptionId, emptyList())
val message = context.getMessages(threadId, getImageResolutions = false, includeScheduledMessages = false, limit = 1).lastOrNull()
if (message != null) {
context.messagesDB.insertOrUpdate(message)
}
} catch (e: Exception) {
context.showErrorToast(e)
}
ensureBackgroundThread {
try {
val smsSentIntent = Intent(context, SmsStatusSentReceiver::class.java)
val deliveredIntent = Intent(context, SmsStatusDeliveredReceiver::class.java)
val photoUri = SimpleContactsHelper(context).getPhotoUriFromPhoneNumber(address)
val bitmap = context.getNotificationBitmap(photoUri)
Handler(Looper.getMainLooper()).post {
context.notificationHelper.showMessageNotification(address, body, threadId, bitmap, sender = null, alertOnlyOnce = true)
}
transaction.setExplicitBroadcastForSentSms(smsSentIntent)
transaction.setExplicitBroadcastForDeliveredSms(deliveredIntent)
transaction.sendNewMessage(message)
} catch (e: Exception) {
context.showErrorToast(e)
context.markThreadMessagesRead(threadId)
context.conversationsDB.markRead(threadId)
}
context.notificationManager.cancel(threadId.hashCode())
context.markThreadMessagesRead(threadId)
context.conversationsDB.markRead(threadId)
}
}
}

View File

@ -3,13 +3,9 @@ package com.simplemobiletools.smsmessenger.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.os.Handler
import android.os.Looper
import android.provider.Telephony
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import com.simplemobiletools.commons.extensions.baseConfig
import com.simplemobiletools.commons.extensions.getMyContactsCursor
import com.simplemobiletools.commons.extensions.isNumberBlocked
@ -17,7 +13,6 @@ import com.simplemobiletools.commons.helpers.SimpleContactsHelper
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.models.PhoneNumber
import com.simplemobiletools.commons.models.SimpleContact
import com.simplemobiletools.smsmessenger.R
import com.simplemobiletools.smsmessenger.extensions.*
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
import com.simplemobiletools.smsmessenger.models.Message
@ -63,7 +58,7 @@ class SmsReceiver : BroadcastReceiver() {
context: Context, address: String, subject: String, body: String, date: Long, read: Int, threadId: Long, type: Int, subscriptionId: Int, status: Int
) {
val photoUri = SimpleContactsHelper(context).getPhotoUriFromPhoneNumber(address)
val bitmap = getPhotoForNotification(photoUri, context)
val bitmap = context.getNotificationBitmap(photoUri)
Handler(Looper.getMainLooper()).post {
if (!context.isNumberBlocked(address)) {
val privateCursor = context.getMyContactsCursor(favoritesOnly = false, withPhoneNumbersOnly = true)
@ -97,27 +92,4 @@ class SmsReceiver : BroadcastReceiver() {
}
}
}
private fun getPhotoForNotification(photoUri: String, context: Context): Bitmap? {
val size = context.resources.getDimension(R.dimen.notification_large_icon_size).toInt()
if (photoUri.isEmpty()) {
return null
}
val options = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
return try {
Glide.with(context)
.asBitmap()
.load(photoUri)
.apply(options)
.apply(RequestOptions.circleCropTransform())
.into(size, size)
.get()
} catch (e: Exception) {
null
}
}
}

View File

@ -1,31 +1,18 @@
package com.simplemobiletools.smsmessenger.receivers
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.media.AudioAttributes
import android.media.AudioManager
import android.media.RingtoneManager
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.provider.Telephony
import androidx.core.app.NotificationCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
import com.klinker.android.send_message.SentReceiver
import com.simplemobiletools.commons.extensions.getMyContactsCursor
import com.simplemobiletools.commons.extensions.getProperPrimaryColor
import com.simplemobiletools.commons.helpers.SimpleContactsHelper
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.helpers.isOreoPlus
import com.simplemobiletools.smsmessenger.R
import com.simplemobiletools.smsmessenger.activities.ThreadActivity
import com.simplemobiletools.smsmessenger.extensions.*
import com.simplemobiletools.smsmessenger.helpers.NOTIFICATION_CHANNEL
import com.simplemobiletools.smsmessenger.helpers.THREAD_ID
import com.simplemobiletools.smsmessenger.helpers.refreshMessages
class SmsStatusSentReceiver : SentReceiver() {
@ -35,11 +22,11 @@ class SmsStatusSentReceiver : SentReceiver() {
val uri = Uri.parse(intent.getStringExtra("message_uri"))
val messageId = uri?.lastPathSegment?.toLong() ?: 0L
ensureBackgroundThread {
val type = if (intent.extras!!.containsKey("errorCode")) {
val type = if (receiverResultCode == Activity.RESULT_OK) {
Telephony.Sms.MESSAGE_TYPE_SENT
} else {
showSendingFailedNotification(context, messageId)
Telephony.Sms.MESSAGE_TYPE_FAILED
} else {
Telephony.Sms.MESSAGE_TYPE_SENT
}
context.updateMessageType(messageId, type)
@ -59,61 +46,16 @@ class SmsStatusSentReceiver : SentReceiver() {
private fun showSendingFailedNotification(context: Context, messageId: Long) {
Handler(Looper.getMainLooper()).post {
val privateCursor = context.getMyContactsCursor(false, true)
if (ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
return@post
}
val privateCursor = context.getMyContactsCursor(favoritesOnly = false, withPhoneNumbersOnly = true)
ensureBackgroundThread {
val address = context.getMessageRecipientAddress(messageId)
val threadId = context.getThreadId(address)
val senderName = context.getNameFromAddress(address, privateCursor)
showNotification(context, senderName, threadId)
val recipientName = context.getNameFromAddress(address, privateCursor)
context.notificationHelper.showSendingFailedNotification(recipientName, threadId)
}
}
}
@SuppressLint("NewApi")
private fun showNotification(context: Context, recipientName: String, threadId: Long) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
if (isOreoPlus()) {
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
.build()
val name = context.getString(R.string.message_not_sent_short)
val importance = NotificationManager.IMPORTANCE_HIGH
NotificationChannel(NOTIFICATION_CHANNEL, name, importance).apply {
setBypassDnd(false)
enableLights(true)
setSound(soundUri, audioAttributes)
enableVibration(true)
notificationManager.createNotificationChannel(this)
}
}
val intent = Intent(context, ThreadActivity::class.java).apply {
putExtra(THREAD_ID, threadId)
}
val pendingIntent = PendingIntent.getActivity(context, threadId.hashCode(), 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(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setDefaults(Notification.DEFAULT_LIGHTS)
.setCategory(Notification.CATEGORY_MESSAGE)
.setAutoCancel(true)
.setSound(soundUri, AudioManager.STREAM_NOTIFICATION)
.setChannelId(NOTIFICATION_CHANNEL)
notificationManager.notify(threadId.hashCode(), builder.build())
}
}