diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45386e28..6fbe151f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed missing notifications in some cases ([#159])
- Fixed incorrect blocking of MMS messages in some rare cases ([#644])
- Fixed issue with importing alphanumeric blocked numbers ([#282])
+- Fixed issue where scheduled messages were not sent after a reboot or app updates ([#641])
## [1.7.0] - 2025-12-16
### Added
@@ -206,6 +207,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#209]: https://github.com/FossifyOrg/Messages/issues/209
[#217]: https://github.com/FossifyOrg/Messages/issues/217
[#225]: https://github.com/FossifyOrg/Messages/issues/225
+[#232]: https://github.com/FossifyOrg/Messages/issues/232
[#234]: https://github.com/FossifyOrg/Messages/issues/234
[#243]: https://github.com/FossifyOrg/Messages/issues/243
[#262]: https://github.com/FossifyOrg/Messages/issues/262
@@ -231,6 +233,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#574]: https://github.com/FossifyOrg/Messages/issues/574
[#600]: https://github.com/FossifyOrg/Messages/issues/600
[#610]: https://github.com/FossifyOrg/Messages/issues/610
+[#641]: https://github.com/FossifyOrg/Messages/issues/641
[#644]: https://github.com/FossifyOrg/Messages/issues/644
[#651]: https://github.com/FossifyOrg/Messages/issues/651
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 326db512..2fa856dd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -11,6 +11,7 @@
+
@@ -242,6 +243,18 @@
android:name=".receivers.ScheduledMessageReceiver"
android:exported="false" />
+
+
+
+
+
+
+
+
+
, newThreadId
fun Context.clearExpiredScheduledMessages(threadId: Long, messagesToDelete: List? = null) {
val messages = messagesToDelete ?: messagesDB.getScheduledThreadMessages(threadId)
- val now = System.currentTimeMillis() + 500L
+ val cutoff = System.currentTimeMillis() - 1.minutes.inWholeMilliseconds
try {
- messages.filter { it.isScheduled && it.millis() < now }.forEach { msg ->
+ messages.filter { it.isScheduled && it.millis() < cutoff }.forEach { msg ->
messagesDB.delete(msg.id)
}
- if (messages.filterNot { it.isScheduled && it.millis() < now }.isEmpty()) {
+ if (messages.filterNot { it.isScheduled && it.millis() < cutoff }.isEmpty()) {
// delete empty temporary thread
val conversation = conversationsDB.getConversationWithThreadId(threadId)
if (conversation != null && conversation.isScheduled) {
@@ -1330,6 +1332,18 @@ fun Context.clearExpiredScheduledMessages(threadId: Long, messagesToDelete: List
}
}
+fun Context.rescheduleAllScheduledMessages() {
+ val scheduledMessages = try {
+ messagesDB.getAllScheduledMessages()
+ } catch (_: Exception) {
+ return
+ }
+
+ scheduledMessages.forEach { message ->
+ runCatching { scheduleMessage(message) }
+ }
+}
+
fun Context.getDefaultKeyboardHeight(): Int {
return resources.getDimensionPixelSize(R.dimen.default_keyboard_height)
}
diff --git a/app/src/main/kotlin/org/fossify/messages/interfaces/MessagesDao.kt b/app/src/main/kotlin/org/fossify/messages/interfaces/MessagesDao.kt
index f97efe3e..f8a37a10 100644
--- a/app/src/main/kotlin/org/fossify/messages/interfaces/MessagesDao.kt
+++ b/app/src/main/kotlin/org/fossify/messages/interfaces/MessagesDao.kt
@@ -1,6 +1,11 @@
+@file:Suppress("MaxLineLength")
package org.fossify.messages.interfaces
-import androidx.room.*
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Transaction
import org.fossify.messages.models.Message
import org.fossify.messages.models.RecycleBinMessage
@@ -24,6 +29,9 @@ interface MessagesDao {
@Query("SELECT messages.* FROM messages LEFT OUTER JOIN recycle_bin_messages ON messages.id = recycle_bin_messages.id WHERE recycle_bin_messages.id IS NOT NULL")
fun getAllRecycleBinMessages(): List
+ @Query("SELECT messages.* FROM messages LEFT OUTER JOIN recycle_bin_messages ON messages.id = recycle_bin_messages.id WHERE recycle_bin_messages.id IS NULL AND is_scheduled = 1")
+ fun getAllScheduledMessages(): List
+
@Query("SELECT messages.* FROM messages LEFT OUTER JOIN recycle_bin_messages ON messages.id = recycle_bin_messages.id WHERE recycle_bin_messages.id IS NOT NULL AND recycle_bin_messages.deleted_ts < :timestamp")
fun getOldRecycleBinMessages(timestamp: Long): List
diff --git a/app/src/main/kotlin/org/fossify/messages/receivers/RescheduleAlarmsReceiver.kt b/app/src/main/kotlin/org/fossify/messages/receivers/RescheduleAlarmsReceiver.kt
new file mode 100644
index 00000000..33040518
--- /dev/null
+++ b/app/src/main/kotlin/org/fossify/messages/receivers/RescheduleAlarmsReceiver.kt
@@ -0,0 +1,20 @@
+package org.fossify.messages.receivers
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import org.fossify.commons.helpers.ensureBackgroundThread
+import org.fossify.messages.extensions.rescheduleAllScheduledMessages
+
+/**
+ * Reschedules alarms after boot/package updates.
+ */
+class RescheduleAlarmsReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val pendingResult = goAsync()
+ ensureBackgroundThread {
+ context.rescheduleAllScheduledMessages()
+ pendingResult.finish()
+ }
+ }
+}
diff --git a/app/src/main/kotlin/org/fossify/messages/receivers/ScheduledMessageReceiver.kt b/app/src/main/kotlin/org/fossify/messages/receivers/ScheduledMessageReceiver.kt
index 8764c4fc..5aab2d2d 100644
--- a/app/src/main/kotlin/org/fossify/messages/receivers/ScheduledMessageReceiver.kt
+++ b/app/src/main/kotlin/org/fossify/messages/receivers/ScheduledMessageReceiver.kt
@@ -17,17 +17,30 @@ import org.fossify.messages.helpers.THREAD_ID
import org.fossify.messages.helpers.refreshConversations
import org.fossify.messages.helpers.refreshMessages
import org.fossify.messages.messaging.sendMessageCompat
+import kotlin.time.Duration.Companion.minutes
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)
-
+ val wakelock = powerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK,
+ "simple.messenger:scheduled.message.receiver"
+ )
+ wakelock.acquire(1.minutes.inWholeMilliseconds)
+ val pendingResult = goAsync()
ensureBackgroundThread {
- handleIntent(context, intent)
+ try {
+ handleIntent(context, intent)
+ } finally {
+ try {
+ if (wakelock.isHeld) wakelock.release()
+ } catch (_: Exception) {
+ }
+
+ pendingResult.finish()
+ }
}
}
@@ -57,7 +70,9 @@ class ScheduledMessageReceiver : BroadcastReceiver() {
} catch (e: Exception) {
context.showErrorToast(e)
} catch (e: Error) {
- context.showErrorToast(e.localizedMessage ?: context.getString(org.fossify.commons.R.string.unknown_error_occurred))
+ context.showErrorToast(
+ e.localizedMessage ?: context.getString(org.fossify.commons.R.string.unknown_error_occurred)
+ )
}
}
}