Delete attachments for expired notifications regularly
This commit is contained in:
parent
a77d1a0f3a
commit
c55693f9cf
6 changed files with 71 additions and 19 deletions
|
@ -290,6 +290,9 @@ interface NotificationDao {
|
|||
@Query("SELECT id FROM notification WHERE subscriptionId = :subscriptionId") // Includes deleted
|
||||
fun listIds(subscriptionId: Long): List<String>
|
||||
|
||||
@Query("SELECT * FROM notification WHERE deleted = 1 AND attachment_contentUri <> ''")
|
||||
fun listDeletedWithAttachments(): List<Notification>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
fun add(notification: Notification)
|
||||
|
||||
|
|
|
@ -88,6 +88,10 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
|
|||
return notificationDao.list()
|
||||
}
|
||||
|
||||
fun getDeletedNotificationsWithAttachments(): List<Notification> {
|
||||
return notificationDao.listDeletedWithAttachments()
|
||||
}
|
||||
|
||||
fun getNotificationsLiveData(subscriptionId: Long): LiveData<List<Notification>> {
|
||||
return notificationDao.listFlow(subscriptionId).asLiveData()
|
||||
}
|
||||
|
|
|
@ -355,7 +355,10 @@ class DetailAdapter(private val activity: Activity, private val repository: Repo
|
|||
val resolver = context.applicationContext.contentResolver
|
||||
val deleted = resolver.delete(contentUri, null, null) > 0
|
||||
if (!deleted) throw Exception("no rows deleted")
|
||||
val newAttachment = attachment.copy(progress = PROGRESS_DELETED)
|
||||
val newAttachment = attachment.copy(
|
||||
contentUri = null,
|
||||
progress = PROGRESS_DELETED
|
||||
)
|
||||
val newNotification = notification.copy(attachment = newAttachment)
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
repository.updateNotification(newNotification)
|
||||
|
|
|
@ -663,7 +663,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
|
|||
// (same as the JobScheduler API), but in practice 15 doesn't work. Using 16 here.
|
||||
// Thanks to varunon9 (https://gist.github.com/varunon9/f2beec0a743c96708eb0ef971a9ff9cd) for this!
|
||||
|
||||
const val POLL_WORKER_INTERVAL_MINUTES = 2 * 60L
|
||||
const val POLL_WORKER_INTERVAL_MINUTES = 60L
|
||||
const val DELETE_WORKER_INTERVAL_MINUTES = 8 * 60L
|
||||
const val SERVICE_START_WORKER_INTERVAL_MINUTES = 3 * 60L
|
||||
}
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
package io.heckel.ntfy.work
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import io.heckel.ntfy.BuildConfig
|
||||
import io.heckel.ntfy.db.PROGRESS_DELETED
|
||||
import io.heckel.ntfy.db.Repository
|
||||
import io.heckel.ntfy.ui.DetailAdapter
|
||||
import io.heckel.ntfy.util.Log
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* Deletes notifications marked for deletion and attachments for deleted notifications.
|
||||
*/
|
||||
class DeleteWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
|
||||
// IMPORTANT:
|
||||
// Every time the worker is changed, the periodic work has to be REPLACEd.
|
||||
|
@ -20,27 +26,58 @@ class DeleteWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx
|
|||
|
||||
override suspend fun doWork(): Result {
|
||||
return withContext(Dispatchers.IO) {
|
||||
Log.d(TAG, "Deleting expired notifications")
|
||||
val repository = Repository.getInstance(applicationContext)
|
||||
val deleteAfterSeconds = repository.getAutoDeleteSeconds()
|
||||
if (deleteAfterSeconds == Repository.AUTO_DELETE_NEVER) {
|
||||
Log.d(TAG, "Not deleting any notifications; global setting set to NEVER")
|
||||
return@withContext Result.success()
|
||||
}
|
||||
|
||||
// Mark as deleted
|
||||
val markDeletedOlderThanTimestamp = (System.currentTimeMillis()/1000) - deleteAfterSeconds
|
||||
Log.d(TAG, "Marking notifications older than $markDeletedOlderThanTimestamp as deleted")
|
||||
repository.markAsDeletedIfOlderThan(markDeletedOlderThanTimestamp)
|
||||
|
||||
// Hard delete
|
||||
val deleteOlderThanTimestamp = (System.currentTimeMillis()/1000) - HARD_DELETE_AFTER_SECONDS
|
||||
Log.d(TAG, "Hard deleting notifications older than $markDeletedOlderThanTimestamp")
|
||||
repository.removeNotificationsIfOlderThan(deleteOlderThanTimestamp)
|
||||
deleteExpiredAttachments() // Before notifications, so we will also catch manually deleted notifications
|
||||
deleteExpiredNotifications()
|
||||
return@withContext Result.success()
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteExpiredAttachments() {
|
||||
Log.d(TAG, "Deleting attachments for deleted notifications")
|
||||
val resolver = applicationContext.contentResolver
|
||||
val repository = Repository.getInstance(applicationContext)
|
||||
val notifications = repository.getDeletedNotificationsWithAttachments()
|
||||
notifications.forEach { notification ->
|
||||
try {
|
||||
val attachment = notification.attachment ?: return
|
||||
val contentUri = Uri.parse(attachment.contentUri ?: return)
|
||||
Log.d(TAG, "Deleting attachment for notification ${notification.id}: ${attachment.contentUri} (${attachment.name})")
|
||||
val deleted = resolver.delete(contentUri, null, null) > 0
|
||||
if (!deleted) {
|
||||
Log.w(TAG, "Unable to delete attachment for notification ${notification.id}")
|
||||
}
|
||||
val newAttachment = attachment.copy(
|
||||
contentUri = null,
|
||||
progress = PROGRESS_DELETED
|
||||
)
|
||||
val newNotification = notification.copy(attachment = newAttachment)
|
||||
repository.updateNotification(newNotification)
|
||||
} catch (e: Exception) {
|
||||
Log.w(DetailAdapter.TAG, "Failed to delete attachment for notification: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteExpiredNotifications() {
|
||||
Log.d(TAG, "Deleting expired notifications")
|
||||
val repository = Repository.getInstance(applicationContext)
|
||||
val deleteAfterSeconds = repository.getAutoDeleteSeconds()
|
||||
if (deleteAfterSeconds == Repository.AUTO_DELETE_NEVER) {
|
||||
Log.d(TAG, "Not deleting any notifications; global setting set to NEVER")
|
||||
return
|
||||
}
|
||||
|
||||
// Mark as deleted
|
||||
val markDeletedOlderThanTimestamp = (System.currentTimeMillis()/1000) - deleteAfterSeconds
|
||||
Log.d(TAG, "Marking notifications older than $markDeletedOlderThanTimestamp as deleted")
|
||||
repository.markAsDeletedIfOlderThan(markDeletedOlderThanTimestamp)
|
||||
|
||||
// Hard delete
|
||||
val deleteOlderThanTimestamp = (System.currentTimeMillis()/1000) - HARD_DELETE_AFTER_SECONDS
|
||||
Log.d(TAG, "Hard deleting notifications older than $markDeletedOlderThanTimestamp")
|
||||
repository.removeNotificationsIfOlderThan(deleteOlderThanTimestamp)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val VERSION = BuildConfig.VERSION_CODE
|
||||
const val TAG = "NtfyDeleteWorker"
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
Features:
|
||||
* Download attachments to cache folder (#181)
|
||||
* Regularly delete attachments for deleted notifications (#142)
|
||||
|
||||
Bugs:
|
||||
* IllegalStateException: Failed to build unique file (#177, thanks to @Fallenbagel for reporting)
|
||||
|
||||
Thanks:
|
||||
* Many thanks to @cmeis, @Fallenbagel, @J117 and @rogeliodh for input on the new attachment logic, and for
|
||||
testing the release
|
||||
|
|
Loading…
Reference in a new issue