Merge pull request #520 from Naveen3Singh/notification_improvements
Improve and organise notification related code
This commit is contained in:
commit
7ea57de52b
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue