Rename package to org.fossify.messages

This commit is contained in:
Naveen 2024-01-18 01:05:03 +05:30
parent d71db351ca
commit e2f83f49da
No known key found for this signature in database
GPG key ID: 0E155DAD31671DA3
106 changed files with 417 additions and 418 deletions

View file

@ -0,0 +1,31 @@
package org.fossify.messages.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import org.fossify.commons.extensions.notificationManager
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.messages.extensions.conversationsDB
import org.fossify.messages.extensions.deleteMessage
import org.fossify.messages.extensions.updateLastConversationMessage
import org.fossify.messages.extensions.updateUnreadCountBadge
import org.fossify.messages.helpers.IS_MMS
import org.fossify.messages.helpers.MESSAGE_ID
import org.fossify.messages.helpers.THREAD_ID
import org.fossify.messages.helpers.refreshMessages
class DeleteSmsReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val threadId = intent.getLongExtra(THREAD_ID, 0L)
val messageId = intent.getLongExtra(MESSAGE_ID, 0L)
val isMms = intent.getBooleanExtra(IS_MMS, false)
context.notificationManager.cancel(threadId.hashCode())
ensureBackgroundThread {
context.deleteMessage(messageId, isMms)
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
context.updateLastConversationMessage(threadId)
refreshMessages()
}
}
}

View file

@ -0,0 +1,65 @@
package org.fossify.messages.receivers
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 org.fossify.commons.extensions.showErrorToast
import org.fossify.commons.helpers.SimpleContactsHelper
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.messages.extensions.*
import org.fossify.messages.helpers.REPLY
import org.fossify.messages.helpers.THREAD_ID
import org.fossify.messages.helpers.THREAD_NUMBER
import org.fossify.messages.messaging.sendMessageCompat
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 body = RemoteInput.getResultsFromIntent(intent)?.getCharSequence(REPLY)?.toString() ?: return
body = context.removeDiacriticsIfNeeded(body)
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) {
subscriptionId = wantedId.subscriptionId
}
}
ensureBackgroundThread {
var messageId = 0L
try {
context.sendMessageCompat(body, listOf(address), subscriptionId, emptyList())
val message = context.getMessages(threadId, getImageResolutions = false, includeScheduledMessages = false, limit = 1).lastOrNull()
if (message != null) {
context.messagesDB.insertOrUpdate(message)
messageId = message.id
context.updateLastConversationMessage(threadId)
}
} catch (e: Exception) {
context.showErrorToast(e)
}
val photoUri = SimpleContactsHelper(context).getPhotoUriFromPhoneNumber(address)
val bitmap = context.getNotificationBitmap(photoUri)
Handler(Looper.getMainLooper()).post {
context.notificationHelper.showMessageNotification(messageId, address, body, threadId, bitmap, sender = null, alertOnlyOnce = true)
}
context.markThreadMessagesRead(threadId)
context.conversationsDB.markRead(threadId)
}
}
}
}

View file

@ -0,0 +1,30 @@
package org.fossify.messages.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import org.fossify.commons.extensions.notificationManager
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.messages.extensions.conversationsDB
import org.fossify.messages.extensions.markThreadMessagesRead
import org.fossify.messages.extensions.updateUnreadCountBadge
import org.fossify.messages.helpers.MARK_AS_READ
import org.fossify.messages.helpers.THREAD_ID
import org.fossify.messages.helpers.refreshMessages
class MarkAsReadReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
MARK_AS_READ -> {
val threadId = intent.getLongExtra(THREAD_ID, 0L)
context.notificationManager.cancel(threadId.hashCode())
ensureBackgroundThread {
context.markThreadMessagesRead(threadId)
context.conversationsDB.markRead(threadId)
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
refreshMessages()
}
}
}
}
}

View file

@ -0,0 +1,55 @@
package org.fossify.messages.receivers
import android.content.Context
import android.net.Uri
import android.os.Handler
import android.os.Looper
import com.bumptech.glide.Glide
import com.klinker.android.send_message.MmsReceivedReceiver
import org.fossify.commons.extensions.isNumberBlocked
import org.fossify.commons.extensions.normalizePhoneNumber
import org.fossify.commons.extensions.showErrorToast
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.messages.R
import org.fossify.messages.extensions.*
import org.fossify.messages.helpers.refreshMessages
// more info at https://github.com/klinker41/android-smsmms
class MmsReceiver : MmsReceivedReceiver() {
override fun isAddressBlocked(context: Context, address: String): Boolean {
val normalizedAddress = address.normalizePhoneNumber()
return context.isNumberBlocked(normalizedAddress)
}
override fun onMessageReceived(context: Context, messageUri: Uri) {
val mms = context.getLatestMMS() ?: return
val address = mms.getSender()?.phoneNumbers?.first()?.normalizedNumber ?: ""
val size = context.resources.getDimension(R.dimen.notification_large_icon_size).toInt()
ensureBackgroundThread {
val glideBitmap = try {
Glide.with(context)
.asBitmap()
.load(mms.attachment!!.attachments.first().getUri())
.centerCrop()
.into(size, size)
.get()
} catch (e: Exception) {
null
}
Handler(Looper.getMainLooper()).post {
context.showReceivedMessageNotification(mms.id, address, mms.body, mms.threadId, glideBitmap)
val conversation = context.getConversations(mms.threadId).firstOrNull() ?: return@post
ensureBackgroundThread {
context.insertOrUpdateConversation(conversation)
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
refreshMessages()
}
}
}
}
override fun onError(context: Context, error: String) = context.showErrorToast(context.getString(R.string.couldnt_download_mms))
}

View file

@ -0,0 +1,62 @@
package org.fossify.messages.receivers
import android.app.Activity
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.database.sqlite.SQLiteException
import android.net.Uri
import android.provider.Telephony
import android.widget.Toast
import org.fossify.commons.extensions.showErrorToast
import org.fossify.commons.extensions.toast
import org.fossify.messages.R
import org.fossify.messages.extensions.deleteMessage
import org.fossify.messages.helpers.refreshMessages
import java.io.File
/** Handles updating databases and states when a MMS message is sent. */
class MmsSentReceiver : SendStatusReceiver() {
override fun updateAndroidDatabase(context: Context, intent: Intent, receiverResultCode: Int) {
val uri = Uri.parse(intent.getStringExtra(EXTRA_CONTENT_URI))
val originalResentMessageId = intent.getLongExtra(EXTRA_ORIGINAL_RESENT_MESSAGE_ID, -1L)
val messageBox = if (receiverResultCode == Activity.RESULT_OK) {
Telephony.Mms.MESSAGE_BOX_SENT
} else {
val msg = context.getString(R.string.unknown_error_occurred_sending_message, receiverResultCode)
context.toast(msg = msg, length = Toast.LENGTH_LONG)
Telephony.Mms.MESSAGE_BOX_FAILED
}
val values = ContentValues(1).apply {
put(Telephony.Mms.MESSAGE_BOX, messageBox)
}
try {
context.contentResolver.update(uri, values, null, null)
} catch (e: SQLiteException) {
context.showErrorToast(e)
}
// In case of resent message, delete original to prevent duplication
if (originalResentMessageId != -1L) {
context.deleteMessage(originalResentMessageId, true)
}
val filePath = intent.getStringExtra(EXTRA_FILE_PATH)
if (filePath != null) {
File(filePath).delete()
}
}
override fun updateAppDatabase(context: Context, intent: Intent, receiverResultCode: Int) {
refreshMessages()
}
companion object {
private const val EXTRA_CONTENT_URI = "content_uri"
private const val EXTRA_FILE_PATH = "file_path"
const val EXTRA_ORIGINAL_RESENT_MESSAGE_ID = "original_message_id"
}
}

View file

@ -0,0 +1,61 @@
package org.fossify.messages.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import org.fossify.commons.extensions.showErrorToast
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.messages.extensions.conversationsDB
import org.fossify.messages.extensions.deleteScheduledMessage
import org.fossify.messages.extensions.getAddresses
import org.fossify.messages.extensions.messagesDB
import org.fossify.messages.helpers.SCHEDULED_MESSAGE_ID
import org.fossify.messages.helpers.THREAD_ID
import org.fossify.messages.helpers.refreshMessages
import org.fossify.messages.messaging.sendMessageCompat
class ScheduledMessageReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
val wakelock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "simple.messenger:scheduled.message.receiver")
wakelock.acquire(3000)
ensureBackgroundThread {
handleIntent(context, intent)
}
}
private fun handleIntent(context: Context, intent: Intent) {
val threadId = intent.getLongExtra(THREAD_ID, 0L)
val messageId = intent.getLongExtra(SCHEDULED_MESSAGE_ID, 0L)
val message = try {
context.messagesDB.getScheduledMessageWithId(threadId, messageId)
} catch (e: Exception) {
e.printStackTrace()
return
}
val addresses = message.participants.getAddresses()
val attachments = message.attachment?.attachments ?: emptyList()
try {
Handler(Looper.getMainLooper()).post {
context.sendMessageCompat(message.body, addresses, message.subscriptionId, attachments)
}
// delete temporary conversation and message as it's already persisted to the telephony db now
context.deleteScheduledMessage(messageId)
context.conversationsDB.deleteThreadId(messageId)
refreshMessages()
} catch (e: Exception) {
context.showErrorToast(e)
} catch (e: Error) {
context.showErrorToast(e.localizedMessage ?: context.getString(org.fossify.commons.R.string.unknown_error_occurred))
}
}
}

View file

@ -0,0 +1,33 @@
package org.fossify.messages.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import org.fossify.commons.helpers.ensureBackgroundThread
abstract class SendStatusReceiver : BroadcastReceiver() {
// Updates the status of the message in the internal database
abstract fun updateAndroidDatabase(context: Context, intent: Intent, receiverResultCode: Int)
// allows the implementer to update the status of the message in their database
abstract fun updateAppDatabase(context: Context, intent: Intent, receiverResultCode: Int)
override fun onReceive(context: Context, intent: Intent) {
val resultCode = resultCode
ensureBackgroundThread {
updateAndroidDatabase(context, intent, resultCode)
updateAppDatabase(context, intent, resultCode)
}
}
companion object {
const val SMS_SENT_ACTION = "org.fossify.org.fossify.messages.receiver.SMS_SENT"
const val SMS_DELIVERED_ACTION = "org.fossify.org.fossify.messages.receiver.SMS_DELIVERED"
// Defined by platform, but no constant provided. See docs for SmsManager.sendTextMessage.
const val EXTRA_ERROR_CODE = "errorCode"
const val EXTRA_SUB_ID = "subId"
const val NO_ERROR_CODE = -1
}
}

View file

@ -0,0 +1,135 @@
package org.fossify.messages.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.provider.Telephony
import org.fossify.commons.extensions.baseConfig
import org.fossify.commons.extensions.getMyContactsCursor
import org.fossify.commons.extensions.isNumberBlocked
import org.fossify.commons.helpers.SimpleContactsHelper
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.commons.models.PhoneNumber
import org.fossify.commons.models.SimpleContact
import org.fossify.messages.extensions.*
import org.fossify.messages.helpers.refreshMessages
import org.fossify.messages.models.Message
class SmsReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
var address = ""
var body = ""
var subject = ""
var date = 0L
var threadId = 0L
var status = Telephony.Sms.STATUS_NONE
val type = Telephony.Sms.MESSAGE_TYPE_INBOX
val read = 0
val subscriptionId = intent.getIntExtra("subscription", -1)
val privateCursor = context.getMyContactsCursor(false, true)
ensureBackgroundThread {
messages.forEach {
address = it.originatingAddress ?: ""
subject = it.pseudoSubject
status = it.status
body += it.messageBody
date = System.currentTimeMillis()
threadId = context.getThreadId(address)
}
if (context.baseConfig.blockUnknownNumbers) {
val simpleContactsHelper = SimpleContactsHelper(context)
simpleContactsHelper.exists(address, privateCursor) { exists ->
if (exists) {
handleMessage(context, address, subject, body, date, read, threadId, type, subscriptionId, status)
}
}
} else {
handleMessage(context, address, subject, body, date, read, threadId, type, subscriptionId, status)
}
}
}
private fun handleMessage(
context: Context,
address: String,
subject: String,
body: String,
date: Long,
read: Int,
threadId: Long,
type: Int,
subscriptionId: Int,
status: Int
) {
if (isMessageFilteredOut(context, body)) {
return
}
val photoUri = SimpleContactsHelper(context).getPhotoUriFromPhoneNumber(address)
val bitmap = context.getNotificationBitmap(photoUri)
Handler(Looper.getMainLooper()).post {
if (!context.isNumberBlocked(address)) {
val privateCursor = context.getMyContactsCursor(favoritesOnly = false, withPhoneNumbersOnly = true)
ensureBackgroundThread {
val newMessageId = context.insertNewSMS(address, subject, body, date, read, threadId, type, subscriptionId)
val conversation = context.getConversations(threadId).firstOrNull() ?: return@ensureBackgroundThread
try {
context.insertOrUpdateConversation(conversation)
} catch (ignored: Exception) {
}
try {
context.updateUnreadCountBadge(context.conversationsDB.getUnreadConversations())
} catch (ignored: Exception) {
}
val senderName = context.getNameFromAddress(address, privateCursor)
val phoneNumber = PhoneNumber(address, 0, "", address)
val participant = SimpleContact(0, 0, senderName, photoUri, arrayListOf(phoneNumber), ArrayList(), ArrayList())
val participants = arrayListOf(participant)
val messageDate = (date / 1000).toInt()
val message =
Message(
newMessageId,
body,
type,
status,
participants,
messageDate,
false,
threadId,
false,
null,
address,
senderName,
photoUri,
subscriptionId
)
context.messagesDB.insertOrUpdate(message)
if (context.config.isArchiveAvailable) {
context.updateConversationArchivedStatus(threadId, false)
}
refreshMessages()
context.showReceivedMessageNotification(newMessageId, address, body, threadId, bitmap)
}
}
}
}
private fun isMessageFilteredOut(context: Context, body: String): Boolean {
for (blockedKeyword in context.config.blockedKeywords) {
if (body.contains(blockedKeyword, ignoreCase = true)) {
return true
}
}
return false
}
}

View file

@ -0,0 +1,100 @@
package org.fossify.messages.receivers
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.Telephony.Sms
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.messages.extensions.messagesDB
import org.fossify.messages.extensions.messagingUtils
import org.fossify.messages.helpers.refreshMessages
/** Handles updating databases and states when a sent SMS message is delivered. */
class SmsStatusDeliveredReceiver : SendStatusReceiver() {
private var status: Int = Sms.Sent.STATUS_NONE
override fun updateAndroidDatabase(context: Context, intent: Intent, receiverResultCode: Int) {
val messageUri: Uri? = intent.data
val smsMessage = context.messagingUtils.getSmsMessageFromDeliveryReport(intent) ?: return
try {
val format = intent.getStringExtra("format")
status = smsMessage.status
// Simple matching up CDMA status with GSM status.
if ("3gpp2" == format) {
val errorClass = status shr 24 and 0x03
val statusCode = status shr 16 and 0x3f
status = when (errorClass) {
0 -> {
if (statusCode == 0x02 /*STATUS_DELIVERED*/) {
Sms.STATUS_COMPLETE
} else {
Sms.STATUS_PENDING
}
}
2 -> {
// TODO: Need to check whether SC still trying to deliver the SMS to destination and will send the report again?
Sms.STATUS_PENDING
}
3 -> {
Sms.STATUS_FAILED
}
else -> {
Sms.STATUS_PENDING
}
}
}
} catch (e: NullPointerException) {
// Sometimes, SmsMessage.mWrappedSmsMessage is null causing NPE when we access
// the methods on it although the SmsMessage itself is not null.
return
}
updateSmsStatusAndDateSent(context, messageUri, System.currentTimeMillis())
}
private fun updateSmsStatusAndDateSent(context: Context, messageUri: Uri?, timeSentInMillis: Long = -1L) {
val resolver = context.contentResolver
val values = ContentValues().apply {
if (status != Sms.Sent.STATUS_NONE) {
put(Sms.Sent.STATUS, status)
}
put(Sms.Sent.DATE_SENT, timeSentInMillis)
}
if (messageUri != null) {
resolver.update(messageUri, values, null, null)
} else {
// mark latest sms as delivered, need to check if this is still necessary (or reliable)
val cursor = resolver.query(Sms.Sent.CONTENT_URI, null, null, null, "date desc")
cursor?.use {
if (cursor.moveToFirst()) {
@SuppressLint("Range")
val id = cursor.getString(cursor.getColumnIndex(Sms.Sent._ID))
val selection = "${Sms._ID} = ?"
val selectionArgs = arrayOf(id.toString())
resolver.update(Sms.Sent.CONTENT_URI, values, selection, selectionArgs)
}
}
}
}
override fun updateAppDatabase(context: Context, intent: Intent, receiverResultCode: Int) {
val messageUri: Uri? = intent.data
if (messageUri != null) {
val messageId = messageUri.lastPathSegment?.toLong() ?: 0L
ensureBackgroundThread {
if (status != Sms.Sent.STATUS_NONE) {
context.messagesDB.updateStatus(messageId, status)
}
refreshMessages()
}
}
}
}

View file

@ -0,0 +1,69 @@
package org.fossify.messages.receivers
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.provider.Telephony.Sms
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
import org.fossify.commons.extensions.getMyContactsCursor
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.messages.extensions.*
import org.fossify.messages.helpers.refreshMessages
/** Handles updating databases and states when a SMS message is sent. */
class SmsStatusSentReceiver : SendStatusReceiver() {
override fun updateAndroidDatabase(context: Context, intent: Intent, receiverResultCode: Int) {
val messageUri: Uri? = intent.data
val resultCode = resultCode
val messagingUtils = context.messagingUtils
val type = if (resultCode == Activity.RESULT_OK) {
Sms.MESSAGE_TYPE_SENT
} else {
Sms.MESSAGE_TYPE_FAILED
}
messagingUtils.updateSmsMessageSendingStatus(messageUri, type)
messagingUtils.maybeShowErrorToast(
resultCode = resultCode,
errorCode = intent.getIntExtra(EXTRA_ERROR_CODE, NO_ERROR_CODE)
)
}
override fun updateAppDatabase(context: Context, intent: Intent, receiverResultCode: Int) {
val messageUri = intent.data
if (messageUri != null) {
val messageId = messageUri.lastPathSegment?.toLong() ?: 0L
ensureBackgroundThread {
val type = if (receiverResultCode == Activity.RESULT_OK) {
Sms.MESSAGE_TYPE_SENT
} else {
showSendingFailedNotification(context, messageId)
Sms.MESSAGE_TYPE_FAILED
}
context.messagesDB.updateType(messageId, type)
refreshMessages()
}
}
}
private fun showSendingFailedNotification(context: Context, messageId: Long) {
Handler(Looper.getMainLooper()).post {
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 recipientName = context.getNameFromAddress(address, privateCursor)
context.notificationHelper.showSendingFailedNotification(recipientName, threadId)
}
}
}
}