Add message details menu button

Adds a "Properties" menu button in conversation, when one message
is selected, which displays details about the message:
- Type (SMS or MMS)
- Sender phone number (or receiver if it is a sent message)
- Used SIM
- Date sent at
- Date received at (if it is an incoming message)

This closes #19
This commit is contained in:
Ensar Sarajčić 2023-07-10 16:43:07 +02:00
parent 9942fb788a
commit bdd506c96e
56 changed files with 530 additions and 29 deletions

View file

@ -1602,6 +1602,7 @@ class ThreadActivity : SimpleActivity() {
status = STATUS_NONE,
participants = participants,
date = (scheduledDateTime.millis / 1000).toInt(),
dateSent = (scheduledDateTime.millis / 1000).toInt(),
read = false,
threadId = threadId,
isMMS = isMmsMessage(text),

View file

@ -33,6 +33,7 @@ import com.simplemobiletools.smsmessenger.activities.NewConversationActivity
import com.simplemobiletools.smsmessenger.activities.SimpleActivity
import com.simplemobiletools.smsmessenger.activities.ThreadActivity
import com.simplemobiletools.smsmessenger.activities.VCardViewerActivity
import com.simplemobiletools.smsmessenger.dialogs.MessageDetailsDialog
import com.simplemobiletools.smsmessenger.dialogs.SelectTextDialog
import com.simplemobiletools.smsmessenger.extensions.*
import com.simplemobiletools.smsmessenger.helpers.*
@ -82,6 +83,7 @@ class ThreadAdapter(
findItem(R.id.cab_share).isVisible = isOneItemSelected && hasText
findItem(R.id.cab_forward_message).isVisible = isOneItemSelected
findItem(R.id.cab_select_text).isVisible = isOneItemSelected && hasText
findItem(R.id.cab_properties).isVisible = isOneItemSelected
}
}
@ -98,6 +100,7 @@ class ThreadAdapter(
R.id.cab_select_text -> selectText()
R.id.cab_delete -> askConfirmDelete()
R.id.cab_select_all -> selectAll()
R.id.cab_properties -> showMessageDetails()
}
}
@ -184,6 +187,12 @@ class ThreadAdapter(
}
}
private fun showMessageDetails() {
val message = getSelectedItems().firstOrNull() as? Message ?: return
MessageDetailsDialog(activity, message)
}
private fun askConfirmDelete() {
val itemsCnt = selectedKeys.size

View file

@ -17,7 +17,7 @@ import com.simplemobiletools.smsmessenger.models.Conversation
import com.simplemobiletools.smsmessenger.models.Message
import com.simplemobiletools.smsmessenger.models.MessageAttachment
@Database(entities = [Conversation::class, Attachment::class, MessageAttachment::class, Message::class], version = 7)
@Database(entities = [Conversation::class, Attachment::class, MessageAttachment::class, Message::class], version = 8)
@TypeConverters(Converters::class)
abstract class MessagesDatabase : RoomDatabase() {
@ -44,6 +44,7 @@ abstract class MessagesDatabase : RoomDatabase() {
.addMigrations(MIGRATION_4_5)
.addMigrations(MIGRATION_5_6)
.addMigrations(MIGRATION_6_7)
.addMigrations(MIGRATION_7_8)
.build()
}
}
@ -115,5 +116,14 @@ abstract class MessagesDatabase : RoomDatabase() {
}
}
}
private val MIGRATION_7_8 = object : Migration(7, 8) {
override fun migrate(database: SupportSQLiteDatabase) {
database.apply {
execSQL("ALTER TABLE messages ADD COLUMN date_sent INTEGER NOT NULL DEFAULT 0")
execSQL("UPDATE messages SET date_sent = date")
}
}
}
}
}

View file

@ -0,0 +1,72 @@
package com.simplemobiletools.smsmessenger.dialogs
import android.annotation.SuppressLint
import android.telephony.SubscriptionInfo
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.getAlertDialogBuilder
import com.simplemobiletools.commons.extensions.getTimeFormat
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.smsmessenger.R
import com.simplemobiletools.smsmessenger.extensions.config
import com.simplemobiletools.smsmessenger.extensions.subscriptionManagerCompat
import com.simplemobiletools.smsmessenger.models.Message
import kotlinx.android.synthetic.main.dialog_message_details.view.dialog_message_details_text_value
import org.joda.time.DateTime
class MessageDetailsDialog(val activity: BaseSimpleActivity, val message: Message) {
init {
@SuppressLint("MissingPermission")
val availableSIMs = activity.subscriptionManagerCompat().activeSubscriptionInfoList
@SuppressLint("SetTextI18n")
val view = activity.layoutInflater.inflate(R.layout.dialog_message_details, null).apply {
dialog_message_details_text_value.text = """
${activity.getString(R.string.message_details_type)}: ${message.getMessageType()}
${message.getReceiverOrSenderLabel()}: ${message.getReceiverOrSenderPhoneNumbers()}
SIM: ${message.getSIM(availableSIMs)}
${activity.getString(R.string.message_details_sent_at)}: ${message.getSentAt()}
${message.getReceivedAtLine()}
""".trimIndent().trimEnd()
}
activity.getAlertDialogBuilder()
.setPositiveButton(R.string.ok) { _, _ -> }
.apply {
activity.setupDialogStuff(view, this, R.string.message_details)
}
}
private fun Message.getMessageType(): String = if (isMMS) "MMS" else "SMS"
private fun Message.getReceiverOrSenderLabel(): String {
return if (isReceivedMessage()) {
activity.getString(R.string.message_details_sender)
} else {
activity.getString(R.string.message_details_receiver)
}
}
private fun Message.getReceiverOrSenderPhoneNumbers(): String {
return participants.joinToString(", ") { it.phoneNumbers.first().value }
}
private fun Message.getSIM(availableSIMs: List<SubscriptionInfo>): String {
return availableSIMs.firstOrNull { it.subscriptionId == subscriptionId }?.displayName?.toString() ?: activity.getString(R.string.unknown)
}
private fun Message.getSentAt(): String {
return DateTime(dateSent * 1000L).toString(activity.config.dateFormat + " " + activity.getTimeFormat())
}
private fun Message.getReceivedAtLine(): String {
return if (isReceivedMessage()) {
"${activity.getString(R.string.message_details_received_at)}: ${getReceivedAt()}"
} else {
""
}
}
private fun Message.getReceivedAt(): String {
return DateTime(date * 1000L).toString(activity.config.dateFormat + " " + activity.getTimeFormat())
}
}

View file

@ -70,6 +70,7 @@ fun Context.getMessages(
Sms.TYPE,
Sms.ADDRESS,
Sms.DATE,
Sms.DATE_SENT,
Sms.READ,
Sms.THREAD_ID,
Sms.SUBSCRIPTION_ID,
@ -106,6 +107,7 @@ fun Context.getMessages(
val senderName = namePhoto.name
val photoUri = namePhoto.photoUri ?: ""
val date = (cursor.getLongValue(Sms.DATE) / 1000).toInt()
val dateSent = (cursor.getLongValue(Sms.DATE_SENT) / 1000).toInt()
val read = cursor.getIntValue(Sms.READ) == 1
val thread = cursor.getLongValue(Sms.THREAD_ID)
val subscriptionId = cursor.getIntValue(Sms.SUBSCRIPTION_ID)
@ -117,7 +119,23 @@ fun Context.getMessages(
}
val isMMS = false
val message =
Message(id, body, type, status, ArrayList(participants), date, read, thread, isMMS, null, senderNumber, senderName, photoUri, subscriptionId)
Message(
id,
body,
type,
status,
ArrayList(participants),
date,
dateSent,
read,
thread,
isMMS,
null,
senderNumber,
senderName,
photoUri,
subscriptionId
)
messages.add(message)
}
@ -148,6 +166,7 @@ fun Context.getMMS(threadId: Long? = null, getImageResolutions: Boolean = false,
val projection = arrayOf(
Mms._ID,
Mms.DATE,
Mms.DATE_SENT,
Mms.READ,
Mms.MESSAGE_BOX,
Mms.THREAD_ID,
@ -175,6 +194,7 @@ fun Context.getMMS(threadId: Long? = null, getImageResolutions: Boolean = false,
val mmsId = cursor.getLongValue(Mms._ID)
val type = cursor.getIntValue(Mms.MESSAGE_BOX)
val date = cursor.getLongValue(Mms.DATE).toInt()
val dateSent = cursor.getLongValue(Mms.DATE_SENT).toInt()
val read = cursor.getIntValue(Mms.READ) == 1
val threadId = cursor.getLongValue(Mms.THREAD_ID)
val subscriptionId = cursor.getIntValue(Mms.SUBSCRIPTION_ID)
@ -202,7 +222,23 @@ fun Context.getMMS(threadId: Long? = null, getImageResolutions: Boolean = false,
}
val message =
Message(mmsId, body, type, status, participants, date, read, threadId, isMMS, attachment, senderNumber, senderName, senderPhotoUri, subscriptionId)
Message(
mmsId,
body,
type,
status,
participants,
date,
dateSent,
read,
threadId,
isMMS,
attachment,
senderNumber,
senderName,
senderPhotoUri,
subscriptionId
)
messages.add(message)
participants.forEach {
@ -560,13 +596,24 @@ fun Context.getNameAndPhotoFromPhoneNumber(number: String): NamePhoto {
return NamePhoto(number, null)
}
fun Context.insertNewSMS(address: String, subject: String, body: String, date: Long, read: Int, threadId: Long, type: Int, subscriptionId: Int): Long {
fun Context.insertNewSMS(
address: String,
subject: String,
body: String,
date: Long,
dateSent: Long,
read: Int,
threadId: Long,
type: Int,
subscriptionId: Int
): Long {
val uri = Sms.CONTENT_URI
val contentValues = ContentValues().apply {
put(Sms.ADDRESS, address)
put(Sms.SUBJECT, subject)
put(Sms.BODY, body)
put(Sms.DATE, date)
put(Sms.DATE_SENT, dateSent)
put(Sms.READ, read)
put(Sms.THREAD_ID, threadId)
put(Sms.TYPE, type)

View file

@ -14,6 +14,7 @@ data class Message(
@ColumnInfo(name = "status") val status: Int,
@ColumnInfo(name = "participants") val participants: ArrayList<SimpleContact>,
@ColumnInfo(name = "date") val date: Int,
@ColumnInfo(name = "date_sent") val dateSent: Int,
@ColumnInfo(name = "read") val read: Boolean,
@ColumnInfo(name = "thread_id") val threadId: Long,
@ColumnInfo(name = "is_mms") val isMMS: Boolean,
@ -58,6 +59,7 @@ data class Message(
return old.body == new.body &&
old.threadId == new.threadId &&
old.date == new.date &&
old.dateSent == new.dateSent &&
old.isMMS == new.isMMS &&
old.attachment == new.attachment &&
old.senderPhoneNumber == new.senderPhoneNumber &&

View file

@ -24,6 +24,7 @@ class SmsReceiver : BroadcastReceiver() {
var body = ""
var subject = ""
var date = 0L
var dateSent = 0L
var threadId = 0L
var status = Telephony.Sms.STATUS_NONE
val type = Telephony.Sms.MESSAGE_TYPE_INBOX
@ -38,6 +39,7 @@ class SmsReceiver : BroadcastReceiver() {
status = it.status
body += it.messageBody
date = System.currentTimeMillis()
dateSent = it.timestampMillis
threadId = context.getThreadId(address)
}
@ -45,17 +47,27 @@ class SmsReceiver : BroadcastReceiver() {
val simpleContactsHelper = SimpleContactsHelper(context)
simpleContactsHelper.exists(address, privateCursor) { exists ->
if (exists) {
handleMessage(context, address, subject, body, date, read, threadId, type, subscriptionId, status)
handleMessage(context, address, subject, body, date, dateSent, read, threadId, type, subscriptionId, status)
}
}
} else {
handleMessage(context, address, subject, body, date, read, threadId, type, subscriptionId, status)
handleMessage(context, address, subject, body, date, dateSent, 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
context: Context,
address: String,
subject: String,
body: String,
date: Long,
dateSent: Long,
read: Int,
threadId: Long,
type: Int,
subscriptionId: Int,
status: Int
) {
val photoUri = SimpleContactsHelper(context).getPhotoUriFromPhoneNumber(address)
val bitmap = context.getNotificationBitmap(photoUri)
@ -63,7 +75,7 @@ class SmsReceiver : BroadcastReceiver() {
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 newMessageId = context.insertNewSMS(address, subject, body, date, dateSent, read, threadId, type, subscriptionId)
val conversation = context.getConversations(threadId).firstOrNull() ?: return@ensureBackgroundThread
try {
@ -81,6 +93,7 @@ class SmsReceiver : BroadcastReceiver() {
val participant = SimpleContact(0, 0, senderName, photoUri, arrayListOf(phoneNumber), ArrayList(), ArrayList())
val participants = arrayListOf(participant)
val messageDate = (date / 1000).toInt()
val messageSentDate = (dateSent / 1000).toInt()
val message =
Message(
@ -90,6 +103,7 @@ class SmsReceiver : BroadcastReceiver() {
status,
participants,
messageDate,
messageSentDate,
false,
threadId,
false,