fix: reschedule scheduled messages when they are cleared (#654)
* fix: reschedule scheduled messages when they are cleared * docs: remove comment about overdue messages That will be solved in another PR. * fix: address detekt issues * fix: don't clear scheduled message ahead of time * fix: reschedule scheduled messages on startup This recovers the alarms when app was force-stopped. * fix: typo! * fix: another typo! Refs: https://github.com/FossifyOrg/Messages/issues/641
This commit is contained in:
parent
dd4ff67a72
commit
a8eb1956b6
8 changed files with 89 additions and 11 deletions
|
|
@ -11,6 +11,7 @@
|
|||
<uses-permission android:name="android.provider.Telephony.SMS_RECEIVED" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
|
|
@ -242,6 +243,18 @@
|
|||
android:name=".receivers.ScheduledMessageReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.RescheduleAlarmsReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
<action android:name="android.intent.action.TIME_SET" />
|
||||
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import android.provider.ContactsContract
|
|||
import org.fossify.commons.FossifyApp
|
||||
import org.fossify.commons.extensions.hasPermission
|
||||
import org.fossify.commons.helpers.PERMISSION_READ_CONTACTS
|
||||
import org.fossify.commons.helpers.ensureBackgroundThread
|
||||
import org.fossify.messages.extensions.rescheduleAllScheduledMessages
|
||||
import org.fossify.messages.helpers.MessagingCache
|
||||
|
||||
class App : FossifyApp() {
|
||||
|
|
@ -23,10 +25,14 @@ class App : FossifyApp() {
|
|||
).forEach {
|
||||
try {
|
||||
contentResolver.registerContentObserver(it, true, contactsObserver)
|
||||
} catch (_: Exception){
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ensureBackgroundThread {
|
||||
rescheduleAllScheduledMessages()
|
||||
}
|
||||
}
|
||||
|
||||
private val contactsObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import android.os.Bundle
|
|||
import android.provider.Telephony
|
||||
import android.text.TextUtils
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import org.fossify.commons.dialogs.PermissionRequiredDialog
|
||||
import org.fossify.commons.extensions.adjustAlpha
|
||||
import org.fossify.commons.extensions.appLaunched
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ import org.fossify.messages.interfaces.MessagesDao
|
|||
import org.fossify.messages.messaging.MessagingUtils
|
||||
import org.fossify.messages.messaging.MessagingUtils.Companion.ADDRESS_SEPARATOR
|
||||
import org.fossify.messages.messaging.SmsSender
|
||||
import org.fossify.messages.messaging.scheduleMessage
|
||||
import org.fossify.messages.models.Attachment
|
||||
import org.fossify.messages.models.Conversation
|
||||
import org.fossify.messages.models.Draft
|
||||
|
|
@ -77,6 +78,7 @@ import org.fossify.messages.models.NamePhoto
|
|||
import org.fossify.messages.models.RecycleBinMessage
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
import java.io.FileNotFoundException
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
val Context.config: Config
|
||||
get() = Config.newInstance(applicationContext)
|
||||
|
|
@ -1311,13 +1313,13 @@ fun Context.updateScheduledMessagesThreadId(messages: List<Message>, newThreadId
|
|||
|
||||
fun Context.clearExpiredScheduledMessages(threadId: Long, messagesToDelete: List<Message>? = 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Message>
|
||||
|
||||
@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<Message>
|
||||
|
||||
@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<Message>
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue