From 2ff0880cb522267eef25d700fde5420785ab7c91 Mon Sep 17 00:00:00 2001 From: Naveen Date: Tue, 27 Sep 2022 16:08:45 +0530 Subject: [PATCH] Handle sending scheduled messages --- app/src/main/AndroidManifest.xml | 5 + .../smsmessenger/activities/MainActivity.kt | 2 +- .../smsmessenger/activities/ThreadActivity.kt | 160 ++++++++++++++---- .../smsmessenger/adapters/ThreadAdapter.kt | 80 ++++++--- .../smsmessenger/extensions/ArrayList.kt | 6 - .../smsmessenger/extensions/Collections.kt | 2 + .../smsmessenger/extensions/Context.kt | 15 +- .../smsmessenger/extensions/Date.kt | 2 +- .../smsmessenger/extensions/SimpleContact.kt | 8 + .../smsmessenger/helpers/Constants.kt | 1 + .../smsmessenger/helpers/Messaging.kt | 41 ++++- .../receivers/ScheduledMessageReceiver.kt | 47 ++++- app/src/main/res/layout/item_sent_message.xml | 11 ++ app/src/main/res/values/dimens.xml | 1 + 14 files changed, 306 insertions(+), 75 deletions(-) delete mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/ArrayList.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/SimpleContact.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3eea3fbf..b669ba54 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ + @@ -212,6 +213,10 @@ + + - val messages = getMessages(threadId, false) + val messages = getMessages(threadId, getImageResolutions = false, includeScheduledMessages = false) messages.chunked(30).forEach { currentMessages -> messagesDB.insertMessages(*currentMessages.toTypedArray()) } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt index b9575c04..11604f6e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/activities/ThreadActivity.kt @@ -13,6 +13,8 @@ import android.os.Bundle import android.provider.ContactsContract import android.provider.MediaStore import android.provider.Telephony +import android.provider.Telephony.Sms.MESSAGE_TYPE_QUEUED +import android.provider.Telephony.Sms.STATUS_NONE import android.telephony.SmsManager import android.telephony.SmsMessage import android.telephony.SubscriptionInfo @@ -92,6 +94,7 @@ class ThreadActivity : SimpleActivity() { private var oldestMessageDate = -1 private var isScheduledMessage: Boolean = false + private var scheduledMessage: Message? = null private lateinit var scheduledDateTime: DateTime override fun onCreate(savedInstanceState: Bundle?) { @@ -260,8 +263,8 @@ class ThreadActivity : SimpleActivity() { val cachedMessagesCode = messages.clone().hashCode() messages = getMessages(threadId, true) - val hasParticipantWithoutName = participants.any { - it.phoneNumbers.map { it.normalizedNumber }.contains(it.name) + val hasParticipantWithoutName = participants.any { contact -> + contact.phoneNumbers.map { it.normalizedNumber }.contains(contact.name) } try { @@ -327,10 +330,8 @@ class ThreadActivity : SimpleActivity() { val currAdapter = thread_messages_list.adapter if (currAdapter == null) { - ThreadAdapter(this, threadItems, thread_messages_list) { - (it as? ThreadError)?.apply { - thread_type_message.setText(it.messageText) - } + ThreadAdapter(this, threadItems, thread_messages_list) { any -> + handleItemClick(any) }.apply { thread_messages_list.adapter = this } @@ -373,6 +374,13 @@ class ThreadActivity : SimpleActivity() { } } + private fun handleItemClick(any: Any) { + when { + any is Message && any.isScheduled -> showScheduledMessageInfo(any) + any is ThreadError -> thread_type_message.setText(any.messageText) + } + } + private fun fetchNextMessages() { if (messages.isEmpty() || allMessagesFetched || loadingOlderMessages) { return @@ -590,8 +598,7 @@ class ThreadActivity : SimpleActivity() { val defaultSmsSubscriptionId = SmsManager.getDefaultSmsSubscriptionId() val systemPreferredSimIdx = if (defaultSmsSubscriptionId >= 0) { - val defaultSmsSIM = subscriptionManagerCompat().getActiveSubscriptionInfo(defaultSmsSubscriptionId) - availableSIMs.indexOfFirstOrNull { it.subscriptionId == defaultSmsSIM.subscriptionId } + availableSIMs.indexOfFirstOrNull { it.subscriptionId == defaultSmsSubscriptionId } } else { null } @@ -600,13 +607,7 @@ class ThreadActivity : SimpleActivity() { } private fun blockNumber() { - val numbers = ArrayList() - participants.forEach { - it.phoneNumbers.forEach { - numbers.add(it.normalizedNumber) - } - } - + val numbers = participants.getAddresses() val numbersString = TextUtils.join(", ", numbers) val question = String.format(resources.getString(R.string.block_confirmation), numbersString) @@ -939,21 +940,44 @@ class ThreadActivity : SimpleActivity() { text = removeDiacriticsIfNeeded(text) - val addresses = participants - .flatMap { it.phoneNumbers } - .map { it.normalizedNumber } + val subscriptionId = availableSIMCards.getOrNull(currentSIMCardIndex)?.subscriptionId ?: SmsManager.getDefaultSmsSubscriptionId() - val currentSubscriptionId = availableSIMCards.getOrNull(currentSIMCardIndex)?.subscriptionId - val attachments = attachmentSelections.values.map { it.uri } + if (isScheduledMessage) { + sendScheduledMessage(text, subscriptionId) + } else { + sendNormalMessage(text, subscriptionId) + } + } + + private fun sendScheduledMessage(text: String, subscriptionId: Int) { + refreshedSinceSent = false + try { + ensureBackgroundThread { + val messageId = scheduledMessage?.id ?: generateRandomMessageId() + val message = buildScheduledMessage(text, subscriptionId, messageId) + messagesDB.insertOrUpdate(message) + scheduleMessage(message) + } + clearCurrentMessage() + hideScheduleSendUi() + + if (!refreshedSinceSent) { + refreshMessages() + } + } catch (e: Exception) { + showErrorToast(e.localizedMessage ?: getString(R.string.unknown_error_occurred)) + } + } + + private fun sendNormalMessage(text: String, subscriptionId: Int) { + val addresses = participants.getAddresses() + val attachments = attachmentSelections.values + .map { it.uri } try { refreshedSinceSent = false - sendTransactionMessage(text, addresses, currentSubscriptionId, attachments) - - thread_type_message.setText("") - attachmentSelections.clear() - thread_attachments_holder.beGone() - thread_attachments_wrapper.removeAllViews() + sendMessage(text, addresses, subscriptionId, attachments) + clearCurrentMessage() if (!refreshedSinceSent) { refreshMessages() @@ -965,6 +989,13 @@ class ThreadActivity : SimpleActivity() { } } + private fun clearCurrentMessage() { + thread_type_message.setText("") + attachmentSelections.clear() + thread_attachments_holder.beGone() + thread_attachments_wrapper.removeAllViews() + } + // show selected contacts, properly split to new lines when appropriate // based on https://stackoverflow.com/a/13505029/1967672 private fun showSelectedContact(views: ArrayList) { @@ -1115,10 +1146,10 @@ class ThreadActivity : SimpleActivity() { messages.filter { !it.isReceivedMessage() && it.id > lastMaxId }.forEach { latestMessage -> // subscriptionIds seem to be not filled out at sending with multiple SIM cards, so fill it manually if ((subscriptionManagerCompat().activeSubscriptionInfoList?.size ?: 0) > 1) { - val SIMId = availableSIMCards.getOrNull(currentSIMCardIndex)?.subscriptionId - if (SIMId != null) { - updateMessageSubscriptionId(latestMessage.id, SIMId) - latestMessage.subscriptionId = SIMId + val subscriptionId = availableSIMCards.getOrNull(currentSIMCardIndex)?.subscriptionId + if (subscriptionId != null) { + updateMessageSubscriptionId(latestMessage.id, subscriptionId) + latestMessage.subscriptionId = subscriptionId } } @@ -1129,11 +1160,15 @@ class ThreadActivity : SimpleActivity() { setupSIMSelector() } - private fun updateMessageType() { - val text = thread_type_message.text.toString() + private fun isMmsMessage(text: String): Boolean { val isGroupMms = participants.size > 1 && config.sendGroupMessageMMS val isLongMmsMessage = isLongMmsMessage(text) && config.sendLongMessageMMS - val stringId = if (attachmentSelections.isNotEmpty() || isGroupMms || isLongMmsMessage) { + return attachmentSelections.isNotEmpty() || isGroupMms || isLongMmsMessage + } + + private fun updateMessageType() { + val text = thread_type_message.text.toString() + val stringId = if (isMmsMessage(text)) { R.string.mms } else { R.string.sms @@ -1150,6 +1185,27 @@ class ThreadActivity : SimpleActivity() { return File.createTempFile("IMG_", ".jpg", outputDirectory) } + private fun showScheduledMessageInfo(message: Message) { + // todo: maybe show options to edit, delete, and send the message now + editScheduledMessage(message) + } + + private fun editScheduledMessage(message: Message) { + scheduledMessage = message + clearCurrentMessage() + thread_type_message.setText(message.body) + + val messageAttachment = message.attachment + if (messageAttachment != null) { + for (attachment in messageAttachment.attachments) { + addAttachment(attachment.getUri()) + } + } + + scheduledDateTime = DateTime(message.millis()) + showScheduleSendUi() + } + private fun launchScheduleSendDialog(originalDt: DateTime? = null) { ScheduleSendDialog(this, originalDt) { newDt -> if (newDt != null) { @@ -1175,13 +1231,19 @@ class ThreadActivity : SimpleActivity() { applyColorFilter(textColor) setOnClickListener { hideScheduleSendUi() + if (scheduledMessage != null) { + ensureBackgroundThread { + messagesDB.delete(scheduledMessage!!.id) + refreshMessages() + } + } } } } private fun showScheduleSendUi() { isScheduledMessage = true - updateSendButton() + updateSendButtonDrawable() scheduled_message_holder.beVisible() scheduled_message_button.text = scheduledDateTime.humanize(this) } @@ -1189,10 +1251,10 @@ class ThreadActivity : SimpleActivity() { private fun hideScheduleSendUi() { isScheduledMessage = false scheduled_message_holder.beGone() - updateSendButton() + updateSendButtonDrawable() } - private fun updateSendButton() { + private fun updateSendButtonDrawable() { val drawableResId = if (isScheduledMessage) { R.drawable.ic_schedule_send_vector } else { @@ -1203,4 +1265,30 @@ class ThreadActivity : SimpleActivity() { thread_send_message.setCompoundDrawablesWithIntrinsicBounds(null, this, null, null) } } + + private fun buildScheduledMessage(text: String, subscriptionId: Int, messageId: Long): Message { + return Message( + id = messageId, + body = text, + type = MESSAGE_TYPE_QUEUED, + status = STATUS_NONE, + participants = participants, + date = (scheduledDateTime.millis / 1000).toInt(), + read = false, + threadId = threadId, + isMMS = isMmsMessage(text), + attachment = buildMessageAttachment(text, messageId), + senderName = "", + senderPhotoUri = "", + subscriptionId = subscriptionId, + isScheduled = true + ) + } + + private fun buildMessageAttachment(text: String, messageId: Long): MessageAttachment { + val attachments = attachmentSelections.values + .map { Attachment(null, messageId, it.uri.toString(), "*/*", 0, 0, "") } + .toArrayList() + return MessageAttachment(messageId, text, attachments) + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt index 75eb264d..3b845010 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/adapters/ThreadAdapter.kt @@ -4,10 +4,10 @@ import android.annotation.SuppressLint import android.content.ActivityNotFoundException import android.content.Intent import android.graphics.Color +import android.graphics.Typeface import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import android.net.Uri -import android.telephony.SubscriptionManager import android.util.TypedValue import android.view.Menu import android.view.View @@ -40,7 +40,12 @@ import com.simplemobiletools.smsmessenger.models.* import kotlinx.android.synthetic.main.item_attachment_image.view.* import kotlinx.android.synthetic.main.item_attachment_vcard.view.* import kotlinx.android.synthetic.main.item_received_message.view.* +import kotlinx.android.synthetic.main.item_received_message.view.thread_mesage_attachments_holder +import kotlinx.android.synthetic.main.item_received_message.view.thread_message_body +import kotlinx.android.synthetic.main.item_received_message.view.thread_message_holder +import kotlinx.android.synthetic.main.item_received_message.view.thread_message_play_outline import kotlinx.android.synthetic.main.item_received_unknown_attachment.view.* +import kotlinx.android.synthetic.main.item_sent_message.view.* import kotlinx.android.synthetic.main.item_sent_unknown_attachment.view.* import kotlinx.android.synthetic.main.item_thread_date_time.view.* import kotlinx.android.synthetic.main.item_thread_error.view.* @@ -252,29 +257,9 @@ class ThreadAdapter( thread_message_body.beVisibleIf(message.body.isNotEmpty()) if (message.isReceivedMessage()) { - thread_message_sender_photo.beVisible() - thread_message_sender_photo.setOnClickListener { - val contact = message.participants.first() - context.getContactFromAddress(contact.phoneNumbers.first().normalizedNumber) { - if (it != null) { - (activity as ThreadActivity).startContactDetailsIntent(it) - } - } - } - thread_message_body.setTextColor(textColor) - thread_message_body.setLinkTextColor(context.getProperPrimaryColor()) - - if (!activity.isFinishing && !activity.isDestroyed) { - SimpleContactsHelper(context).loadContactImage(message.senderPhotoUri, thread_message_sender_photo, message.senderName) - } + setupReceivedMessageView(view, message) } else { - thread_message_sender_photo?.beGone() - val background = context.getProperPrimaryColor() - thread_message_body.background.applyColorFilter(background) - - val contrastColor = background.getContrastColor() - thread_message_body.setTextColor(contrastColor) - thread_message_body.setLinkTextColor(contrastColor) + setupSentMessageView(view, message) } thread_message_body.setOnLongClickListener { @@ -304,6 +289,55 @@ class ThreadAdapter( } } + private fun setupReceivedMessageView(view: View, message: Message) { + view.apply { + thread_message_sender_photo.beVisible() + thread_message_sender_photo.setOnClickListener { + val contact = message.participants.first() + context.getContactFromAddress(contact.phoneNumbers.first().normalizedNumber) { + if (it != null) { + (activity as ThreadActivity).startContactDetailsIntent(it) + } + } + } + thread_message_body.setTextColor(textColor) + thread_message_body.setLinkTextColor(context.getProperPrimaryColor()) + + if (!activity.isFinishing && !activity.isDestroyed) { + SimpleContactsHelper(context).loadContactImage(message.senderPhotoUri, thread_message_sender_photo, message.senderName) + } + } + } + + private fun setupSentMessageView(view: View, message: Message) { + view.apply { + thread_message_sender_photo?.beGone() + val background = context.getProperPrimaryColor() + thread_message_body.background.applyColorFilter(background) + + val contrastColor = background.getContrastColor() + thread_message_body.setTextColor(contrastColor) + thread_message_body.setLinkTextColor(contrastColor) + + val padding = thread_message_body.paddingStart + if (message.isScheduled) { + thread_message_scheduled_icon.beVisible() + thread_message_scheduled_icon.applyColorFilter(contrastColor) + + thread_message_scheduled_icon.onGlobalLayout { + val rightPadding = padding + thread_message_scheduled_icon.measuredWidth + thread_message_body.setPadding(padding, padding, rightPadding, padding) + } + thread_message_body.typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC) + } else { + thread_message_scheduled_icon.beGone() + + thread_message_body.setPadding(padding, padding, padding, padding) + thread_message_body.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) + } + } + } + private fun setupImageView(holder: ViewHolder, parent: View, message: Message, attachment: Attachment) { val mimetype = attachment.mimetype val uri = attachment.getUri() diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/ArrayList.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/ArrayList.kt deleted file mode 100644 index dfaf3202..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/ArrayList.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.simplemobiletools.smsmessenger.extensions - -import android.text.TextUtils -import com.simplemobiletools.commons.models.SimpleContact - -fun ArrayList.getThreadTitle() = TextUtils.join(", ", map { it.name }.toTypedArray()) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Collections.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Collections.kt index 38e3d1b2..2fdbaac4 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Collections.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Collections.kt @@ -30,3 +30,5 @@ fun Map.toContentValues(): ContentValues { return contentValues } + +fun Collection.toArrayList() = ArrayList(this) diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt index 4dd05681..bfcc129b 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Context.kt @@ -57,7 +57,7 @@ val Context.messageAttachmentsDB: MessageAttachmentsDao get() = getMessagesDB(). val Context.messagesDB: MessagesDao get() = getMessagesDB().MessagesDao() -fun Context.getMessages(threadId: Long, getImageResolutions: Boolean, dateFrom: Int = -1): ArrayList { +fun Context.getMessages(threadId: Long, getImageResolutions: Boolean, dateFrom: Int = -1, includeScheduledMessages: Boolean = true): ArrayList { val uri = Sms.CONTENT_URI val projection = arrayOf( Sms._ID, @@ -116,8 +116,17 @@ fun Context.getMessages(threadId: Long, getImageResolutions: Boolean, dateFrom: } messages.addAll(getMMS(threadId, getImageResolutions, sortOrder)) - messages = messages.filter { it.participants.isNotEmpty() } - .sortedWith(compareBy { it.date }.thenBy { it.id }).toMutableList() as ArrayList + + if (includeScheduledMessages) { + val scheduledMessages = messagesDB.getScheduledThreadMessages(threadId) + messages.addAll(scheduledMessages) + } + + messages = messages + .filter { it.participants.isNotEmpty() } + .filterNot { it.isScheduled && it.millis() < System.currentTimeMillis() } + .sortedWith(compareBy { it.date }.thenBy { it.id }) + .toMutableList() as ArrayList return messages } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Date.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Date.kt index 49182867..50989a6e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Date.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/Date.kt @@ -14,6 +14,6 @@ fun DateTime.humanize(context: Context, now: DateTime = DateTime.now(), pattern: return if (yearOfCentury().get() > now.yearOfCentury().get()) { toString(pattern) } else { - DateUtils.getRelativeDateTimeString(context, millis, DateUtils.MINUTE_IN_MILLIS, DateUtils.DAY_IN_MILLIS, 0).toString() + DateUtils.getRelativeDateTimeString(context, millis, DateUtils.SECOND_IN_MILLIS, DateUtils.DAY_IN_MILLIS, 0).toString() } } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/SimpleContact.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/SimpleContact.kt new file mode 100644 index 00000000..2efa8e26 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/extensions/SimpleContact.kt @@ -0,0 +1,8 @@ +package com.simplemobiletools.smsmessenger.extensions + +import android.text.TextUtils +import com.simplemobiletools.commons.models.SimpleContact + +fun ArrayList.getThreadTitle(): String = TextUtils.join(", ", map { it.name }.toTypedArray()).orEmpty() + +fun ArrayList.getAddresses() = flatMap { it.phoneNumbers }.map { it.normalizedNumber } diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt index f3711dec..15baf492 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Constants.kt @@ -29,6 +29,7 @@ const val IMPORT_SMS = "import_sms" const val IMPORT_MMS = "import_mms" const val WAS_DB_CLEARED = "was_db_cleared_2" const val EXTRA_VCARD_URI = "vcard" +const val SCHEDULED_MESSAGE_ID = "scheduled_message_id" private const val PATH = "com.simplemobiletools.smsmessenger.action." const val MARK_AS_READ = PATH + "mark_as_read" diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Messaging.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Messaging.kt index 2f0ed4c7..8e9be543 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Messaging.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/helpers/Messaging.kt @@ -1,16 +1,28 @@ package com.simplemobiletools.smsmessenger.helpers +import android.app.AlarmManager +import android.app.PendingIntent import android.content.Context import android.content.Intent import android.net.Uri +import android.os.Handler +import android.os.Looper +import androidx.core.app.AlarmManagerCompat import com.klinker.android.send_message.Settings import com.klinker.android.send_message.Transaction import com.klinker.android.send_message.Utils import com.simplemobiletools.commons.extensions.showErrorToast +import com.simplemobiletools.commons.helpers.isMarshmallowPlus import com.simplemobiletools.smsmessenger.R import com.simplemobiletools.smsmessenger.extensions.config +import com.simplemobiletools.smsmessenger.models.Message +import com.simplemobiletools.smsmessenger.receivers.ScheduledMessageReceiver import com.simplemobiletools.smsmessenger.receivers.SmsStatusDeliveredReceiver import com.simplemobiletools.smsmessenger.receivers.SmsStatusSentReceiver +import org.joda.time.DateTime +import org.joda.time.DateTimeZone +import kotlin.math.abs +import kotlin.random.Random fun Context.getSendMessageSettings(): Settings { val settings = Settings() @@ -22,7 +34,7 @@ fun Context.getSendMessageSettings(): Settings { return settings } -fun Context.sendTransactionMessage(text: String, addresses: List, subscriptionId: Int?, attachments: List) { +fun Context.sendMessage(text: String, addresses: List, subscriptionId: Int?, attachments: List) { val settings = getSendMessageSettings() if (subscriptionId != null) { settings.subscriptionId = subscriptionId @@ -50,10 +62,35 @@ fun Context.sendTransactionMessage(text: String, addresses: List, subscr transaction.setExplicitBroadcastForSentSms(smsSentIntent) transaction.setExplicitBroadcastForDeliveredSms(deliveredIntent) - transaction.sendNewMessage(message) + Handler(Looper.getMainLooper()).post { + transaction.sendNewMessage(message) + } +} + +fun Context.scheduleMessage(message: Message) { + val intent = Intent(this, ScheduledMessageReceiver::class.java) + intent.putExtra(THREAD_ID, message.threadId) + intent.putExtra(SCHEDULED_MESSAGE_ID, message.id) + + var flags = PendingIntent.FLAG_UPDATE_CURRENT + if (isMarshmallowPlus()) { + flags = flags or PendingIntent.FLAG_IMMUTABLE + } + val pendingIntent = PendingIntent.getBroadcast(this, message.id.toInt(), intent, flags) + val triggerAtMillis = message.millis() + + val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager + AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent) } fun Context.isLongMmsMessage(text: String): Boolean { val settings = getSendMessageSettings() return Utils.getNumPages(settings, text) > settings.sendLongAsMmsAfter } + +/** Not to be used with real messages persisted in the telephony db. This is for internal use only (e.g. scheduled messages). */ +fun generateRandomMessageId(length: Int = 8): Long { + val millis = DateTime.now(DateTimeZone.UTC).millis + val random = abs(Random(millis).nextLong()) + return random.toString().takeLast(length).toLong() +} diff --git a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/ScheduledMessageReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/ScheduledMessageReceiver.kt index 72aa53d1..e3c41473 100644 --- a/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/ScheduledMessageReceiver.kt +++ b/app/src/main/kotlin/com/simplemobiletools/smsmessenger/receivers/ScheduledMessageReceiver.kt @@ -3,9 +3,50 @@ package com.simplemobiletools.smsmessenger.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.os.PowerManager +import com.simplemobiletools.commons.extensions.showErrorToast +import com.simplemobiletools.commons.helpers.ensureBackgroundThread +import com.simplemobiletools.smsmessenger.R +import com.simplemobiletools.smsmessenger.extensions.getAddresses +import com.simplemobiletools.smsmessenger.extensions.messagesDB +import com.simplemobiletools.smsmessenger.helpers.SCHEDULED_MESSAGE_ID +import com.simplemobiletools.smsmessenger.helpers.THREAD_ID +import com.simplemobiletools.smsmessenger.helpers.refreshMessages +import com.simplemobiletools.smsmessenger.helpers.sendMessage -class ScheduledMessageReceiver: BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - TODO("Not yet implemented") +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) { + return + } + + val addresses = message.participants.getAddresses() + val attachments = message.attachment?.attachments?.mapNotNull { it.getUri() } ?: emptyList() + + try { + context.sendMessage(message.body, addresses, message.subscriptionId, attachments) + context.messagesDB.delete(messageId) + refreshMessages() + } catch (e: Exception) { + context.showErrorToast(e) + } catch (e: Error) { + context.showErrorToast(e.localizedMessage ?: context.getString(R.string.unknown_error_occurred)) + } } } diff --git a/app/src/main/res/layout/item_sent_message.xml b/app/src/main/res/layout/item_sent_message.xml index a5d57e11..def52b64 100644 --- a/app/src/main/res/layout/item_sent_message.xml +++ b/app/src/main/res/layout/item_sent_message.xml @@ -48,4 +48,15 @@ android:textSize="@dimen/normal_text_size" tools:text="Sent message" /> + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index b8a87de9..d6f8e492 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -8,4 +8,5 @@ 15dp 64dp 36sp + 20dp