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