Implement auto-delete of notifications, closes #71

This commit is contained in:
Philipp Heckel 2022-02-09 16:20:24 -05:00
parent 42188d5152
commit d44358f75c
31 changed files with 227 additions and 50 deletions

View file

@ -12,8 +12,8 @@ android {
minSdkVersion 21
targetSdkVersion 31
versionCode 21
versionName "1.8.1"
versionCode 22
versionName "1.9.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -4,7 +4,7 @@ import android.app.Application
import android.content.Context
import io.heckel.ntfy.db.Database
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
class Application : Application() {
private val database by lazy {

View file

@ -297,9 +297,15 @@ interface NotificationDao {
@Query("UPDATE notification SET deleted = 1 WHERE subscriptionId = :subscriptionId")
fun markAllAsDeleted(subscriptionId: Long)
@Query("UPDATE notification SET deleted = 1 WHERE timestamp < :olderThanTimestamp")
fun markAsDeletedIfOlderThan(olderThanTimestamp: Long)
@Query("UPDATE notification SET deleted = 0 WHERE id = :notificationId")
fun undelete(notificationId: String)
@Query("DELETE FROM notification WHERE timestamp < :olderThanTimestamp")
fun removeIfOlderThan(olderThanTimestamp: Long)
@Query("DELETE FROM notification WHERE subscriptionId = :subscriptionId")
fun removeAll(subscriptionId: Long)
}

View file

@ -1,13 +1,12 @@
package io.heckel.ntfy.db
import android.app.Activity
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import androidx.annotation.WorkerThread
import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.*
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong
@ -116,20 +115,26 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
notificationDao.update(notification)
}
fun markAsDeleted(notificationId: String) {
notificationDao.markAsDeleted(notificationId)
}
fun undeleteNotification(notificationId: String) {
notificationDao.undelete(notificationId)
}
fun markAsDeleted(notificationId: String) {
notificationDao.markAsDeleted(notificationId)
}
fun markAllAsDeleted(subscriptionId: Long) {
notificationDao.markAllAsDeleted(subscriptionId)
}
@Suppress("RedundantSuspendModifier")
@WorkerThread
fun markAsDeletedIfOlderThan(olderThanTimestamp: Long) {
notificationDao.markAsDeletedIfOlderThan(olderThanTimestamp)
}
fun removeNotificationsIfOlderThan(olderThanTimestamp: Long) {
notificationDao.removeIfOlderThan(olderThanTimestamp)
}
fun removeAllNotifications(subscriptionId: Long) {
notificationDao.removeAll(subscriptionId)
}
@ -164,6 +169,16 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
.apply()
}
fun getDeleteWorkerVersion(): Int {
return sharedPrefs.getInt(SHARED_PREFS_DELETE_WORKER_VERSION, 0)
}
fun setDeleteWorkerVersion(version: Int) {
sharedPrefs.edit()
.putInt(SHARED_PREFS_DELETE_WORKER_VERSION, version)
.apply()
}
fun getAutoRestartWorkerVersion(): Int {
return sharedPrefs.getInt(SHARED_PREFS_AUTO_RESTART_WORKER_VERSION, 0)
}
@ -205,6 +220,16 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
.apply()
}
fun getAutoDeleteSeconds(): Long {
return sharedPrefs.getLong(SHARED_PREFS_AUTO_DELETE_SECONDS, AUTO_DELETE_DEFAULT_SECONDS)
}
fun setAutoDeleteSeconds(seconds: Long) {
sharedPrefs.edit()
.putLong(SHARED_PREFS_AUTO_DELETE_SECONDS, seconds)
.apply()
}
fun setDarkMode(mode: Int) {
if (mode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) {
sharedPrefs.edit()
@ -384,11 +409,12 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
companion object {
const val SHARED_PREFS_ID = "MainPreferences"
const val SHARED_PREFS_POLL_WORKER_VERSION = "PollWorkerVersion"
const val SHARED_PREFS_DELETE_WORKER_VERSION = "DeleteWorkerVersion"
const val SHARED_PREFS_AUTO_RESTART_WORKER_VERSION = "AutoRestartWorkerVersion"
const val SHARED_PREFS_MUTED_UNTIL_TIMESTAMP = "MutedUntil"
const val SHARED_PREFS_MIN_PRIORITY = "MinPriority"
const val SHARED_PREFS_AUTO_DOWNLOAD_MAX_SIZE = "AutoDownload"
const val SHARED_PREFS_WAKELOCK_ENABLED = "WakelockEnabled"
const val SHARED_PREFS_AUTO_DELETE_SECONDS = "AutoDelete"
const val SHARED_PREFS_CONNECTION_PROTOCOL = "ConnectionProtocol"
const val SHARED_PREFS_DARK_MODE = "DarkMode"
const val SHARED_PREFS_BROADCAST_ENABLED = "BroadcastEnabled"
@ -401,9 +427,19 @@ class Repository(private val sharedPrefs: SharedPreferences, private val databas
const val MUTED_UNTIL_FOREVER = 1L
const val MUTED_UNTIL_TOMORROW = 2L
const val AUTO_DOWNLOAD_NEVER = 0L
private const val ONE_MB = 1024 * 1024L
const val AUTO_DOWNLOAD_NEVER = 0L // Values must match values.xml
const val AUTO_DOWNLOAD_ALWAYS = 1L
const val AUTO_DOWNLOAD_DEFAULT = 1024 * 1024L // Must match a value in values.xml
const val AUTO_DOWNLOAD_DEFAULT = ONE_MB
private const val ONE_DAY_SECONDS = 24 * 60 * 60L
const val AUTO_DELETE_NEVER = 0L // Values must match values.xml
const val AUTO_DELETE_ONE_DAY_SECONDS = ONE_DAY_SECONDS
const val AUTO_DELETE_THREE_DAYS_SECONDS = 3 * ONE_DAY_SECONDS
const val AUTO_DELETE_ONE_WEEK_SECONDS = 7 * ONE_DAY_SECONDS
const val AUTO_DELETE_ONE_MONTH_SECONDS = 30 * ONE_DAY_SECONDS
const val AUTO_DELETE_THREE_MONTHS_SECONDS = 90 * ONE_DAY_SECONDS
const val AUTO_DELETE_DEFAULT_SECONDS = AUTO_DELETE_ONE_MONTH_SECONDS
const val CONNECTION_PROTOCOL_JSONHTTP = "jsonhttp"
const val CONNECTION_PROTOCOL_WS = "ws"

View file

@ -4,7 +4,7 @@ import android.os.Build
import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.db.Notification
import io.heckel.ntfy.db.User
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.util.topicUrl
import io.heckel.ntfy.util.topicUrlAuth
import io.heckel.ntfy.util.topicUrlJson

View file

@ -6,7 +6,7 @@ import io.heckel.ntfy.R
import io.heckel.ntfy.db.Notification
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.util.joinTagsMap
import io.heckel.ntfy.util.splitTags
import kotlinx.coroutines.Dispatchers

View file

@ -5,7 +5,7 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.workDataOf
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
/**
* Download attachment in the background via WorkManager

View file

@ -17,7 +17,7 @@ import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
import io.heckel.ntfy.db.*
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.util.queryFilename
import okhttp3.OkHttpClient
import okhttp3.Request

View file

@ -4,7 +4,7 @@ import android.content.Context
import io.heckel.ntfy.db.Notification
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.up.Distributor
import io.heckel.ntfy.util.safeLet

View file

@ -11,7 +11,7 @@ import androidx.core.content.ContextCompat
import io.heckel.ntfy.R
import io.heckel.ntfy.db.*
import io.heckel.ntfy.db.Notification
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.ui.Colors
import io.heckel.ntfy.ui.DetailActivity
import io.heckel.ntfy.ui.MainActivity

View file

@ -1,7 +1,7 @@
package io.heckel.ntfy.service
import io.heckel.ntfy.db.*
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.util.topicUrl
import kotlinx.coroutines.*

View file

@ -16,7 +16,7 @@ import io.heckel.ntfy.app.Application
import io.heckel.ntfy.db.ConnectionState
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.NotificationDispatcher
import io.heckel.ntfy.ui.Colors

View file

@ -5,7 +5,7 @@ import android.content.Intent
import androidx.core.content.ContextCompat
import androidx.work.*
import io.heckel.ntfy.app.Application
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
/**
* This class only manages the SubscriberService, i.e. it starts or stops it.

View file

@ -5,7 +5,7 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
import io.heckel.ntfy.db.*
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService.Companion.requestBuilder
import io.heckel.ntfy.msg.NotificationParser
import io.heckel.ntfy.util.topicShortUrl

View file

@ -20,7 +20,7 @@ import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.User
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.util.topicUrl
import kotlinx.coroutines.Dispatchers

View file

@ -27,7 +27,7 @@ import io.heckel.ntfy.app.Application
import io.heckel.ntfy.db.Notification
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.firebase.FirebaseMessenger
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.NotificationService
import io.heckel.ntfy.service.SubscriberServiceManager

View file

@ -20,7 +20,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.stfalcon.imageviewer.StfalconImageViewer
import io.heckel.ntfy.R
import io.heckel.ntfy.db.*
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.DownloadManager
import io.heckel.ntfy.util.*
import kotlinx.coroutines.Dispatchers

View file

@ -7,7 +7,7 @@ import androidx.preference.PreferenceFragmentCompat
import io.heckel.ntfy.R
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.service.SubscriberServiceManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

View file

@ -30,12 +30,13 @@ import io.heckel.ntfy.app.Application
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.firebase.FirebaseMessenger
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.NotificationDispatcher
import io.heckel.ntfy.service.SubscriberService
import io.heckel.ntfy.service.SubscriberServiceManager
import io.heckel.ntfy.util.*
import io.heckel.ntfy.work.DeleteWorker
import io.heckel.ntfy.work.PollWorker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@ -158,8 +159,9 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
AppCompatDelegate.setDefaultNightMode(repository.getDarkMode())
// Background things
startPeriodicPollWorker()
startPeriodicServiceRestartWorker()
schedulePeriodicPollWorker()
schedulePeriodicServiceRestartWorker()
schedulePeriodicDeleteWorker()
}
override fun onResume() {
@ -178,7 +180,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
Log.d(TAG, "Battery: ignoring optimizations = $ignoringOptimizations (we want this to be true); instant subscriptions = $hasInstantSubscriptions; remind time reached = $batteryRemindTimeReached; banner = $showBanner")
}
private fun startPeriodicPollWorker() {
private fun schedulePeriodicPollWorker() {
val workerVersion = repository.getPollWorkerVersion()
val workPolicy = if (workerVersion == PollWorker.VERSION) {
Log.d(TAG, "Poll worker version matches: choosing KEEP as existing work policy")
@ -200,7 +202,26 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
workManager!!.enqueueUniquePeriodicWork(PollWorker.WORK_NAME_PERIODIC_ALL, workPolicy, work)
}
private fun startPeriodicServiceRestartWorker() {
private fun schedulePeriodicDeleteWorker() {
val workerVersion = repository.getDeleteWorkerVersion()
val workPolicy = if (workerVersion == DeleteWorker.VERSION) {
Log.d(TAG, "Delete worker version matches: choosing KEEP as existing work policy")
ExistingPeriodicWorkPolicy.KEEP
} else {
Log.d(TAG, "Delete worker version DOES NOT MATCH: choosing REPLACE as existing work policy")
repository.setDeleteWorkerVersion(DeleteWorker.VERSION)
ExistingPeriodicWorkPolicy.REPLACE
}
val work = PeriodicWorkRequestBuilder<DeleteWorker>(DELETE_WORKER_INTERVAL_MINUTES, TimeUnit.MINUTES)
.addTag(DeleteWorker.TAG)
.addTag(DeleteWorker.WORK_NAME_PERIODIC_ALL)
.build()
Log.d(TAG, "Delete worker: Scheduling period work every $DELETE_WORKER_INTERVAL_MINUTES minutes")
workManager!!.enqueueUniquePeriodicWork(DeleteWorker.WORK_NAME_PERIODIC_ALL, workPolicy, work)
}
private fun schedulePeriodicServiceRestartWorker() {
val workerVersion = repository.getAutoRestartWorkerVersion()
val workPolicy = if (workerVersion == SubscriberService.SERVICE_START_WORKER_VERSION) {
Log.d(TAG, "ServiceStartWorker version matches: choosing KEEP as existing work policy")
@ -598,6 +619,7 @@ class MainActivity : AppCompatActivity(), ActionMode.Callback, AddFragment.Subsc
// Thanks to varunon9 (https://gist.github.com/varunon9/f2beec0a743c96708eb0ef971a9ff9cd) for this!
const val POLL_WORKER_INTERVAL_MINUTES = 2 * 60L
const val DELETE_WORKER_INTERVAL_MINUTES = 8 * 60L
const val SERVICE_START_WORKER_INTERVAL_MINUTES = 3 * 60L
}
}

View file

@ -5,7 +5,6 @@ import android.app.AlertDialog
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
@ -25,7 +24,7 @@ import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.User
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.service.SubscriberServiceManager
import io.heckel.ntfy.util.formatBytes
import io.heckel.ntfy.util.formatDateShort
@ -234,6 +233,32 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere
}
}
// Auto delete
val autoDeletePrefId = context?.getString(R.string.settings_notifications_auto_delete_key) ?: return
val autoDelete: ListPreference? = findPreference(autoDeletePrefId)
autoDelete?.value = repository.getAutoDeleteSeconds().toString()
autoDelete?.preferenceDataStore = object : PreferenceDataStore() {
override fun putString(key: String?, value: String?) {
val seconds = value?.toLongOrNull() ?:return
repository.setAutoDeleteSeconds(seconds)
}
override fun getString(key: String?, defValue: String?): String {
return repository.getAutoDeleteSeconds().toString()
}
}
autoDelete?.summaryProvider = Preference.SummaryProvider<ListPreference> { pref ->
val seconds = pref.value.toLongOrNull() ?: repository.getAutoDeleteSeconds()
when (seconds) {
Repository.AUTO_DELETE_NEVER -> getString(R.string.settings_notifications_auto_delete_summary_never)
Repository.AUTO_DELETE_ONE_DAY_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_one_day)
Repository.AUTO_DELETE_THREE_DAYS_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_three_days)
Repository.AUTO_DELETE_ONE_WEEK_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_one_week)
Repository.AUTO_DELETE_ONE_MONTH_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_one_month)
Repository.AUTO_DELETE_THREE_MONTHS_SECONDS -> getString(R.string.settings_notifications_auto_delete_summary_three_months)
else -> getString(R.string.settings_notifications_auto_delete_summary_one_month) // Must match default const
}
}
// Dark mode
val darkModePrefId = context?.getString(R.string.settings_appearance_dark_mode_key) ?: return
val darkMode: ListPreference? = findPreference(darkModePrefId)

View file

@ -5,7 +5,7 @@ import android.content.Intent
import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.service.SubscriberServiceManager
import io.heckel.ntfy.util.randomString
import io.heckel.ntfy.util.topicUrlUp

View file

@ -2,7 +2,7 @@ package io.heckel.ntfy.up
import android.content.Context
import android.content.Intent
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
/**
* This is the UnifiedPush distributor, an amalgamation of messages to be sent as part of the spec.

View file

@ -1,4 +1,4 @@
package io.heckel.ntfy.log
package io.heckel.ntfy.util
import android.content.Context
import android.os.Build

View file

@ -0,0 +1,52 @@
package io.heckel.ntfy.work
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class DeleteWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
// IMPORTANT:
// Every time the worker is changed, the periodic work has to be REPLACEd.
// This is facilitated in the MainActivity using the VERSION below.
init {
Log.init(ctx) // Init in all entrypoints
}
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)
return@withContext Result.success()
}
}
companion object {
const val VERSION = BuildConfig.VERSION_CODE
const val TAG = "NtfyDeleteWorker"
const val WORK_NAME_PERIODIC_ALL = "NtfyDeleteWorkerPeriodic" // Do not change
private const val ONE_DAY_SECONDS = 24 * 60 * 60L
const val HARD_DELETE_AFTER_SECONDS = 4 * 30 * ONE_DAY_SECONDS // 4 months
}
}

View file

@ -4,17 +4,16 @@ import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.db.Database
import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.NotificationDispatcher
import io.heckel.ntfy.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.random.Random
class PollWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
// IMPORTANT WARNING:
// IMPORTANT:
// Every time the worker is changed, the periodic work has to be REPLACEd.
// This is facilitated in the MainActivity using the VERSION below.
@ -25,9 +24,7 @@ class PollWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx,
override suspend fun doWork(): Result {
return withContext(Dispatchers.IO) {
Log.d(TAG, "Polling for new notifications")
val database = Database.getInstance(applicationContext)
val sharedPrefs = applicationContext.getSharedPreferences(Repository.SHARED_PREFS_ID, Context.MODE_PRIVATE)
val repository = Repository.getInstance(sharedPrefs, database)
val repository = Repository.getInstance(applicationContext)
val dispatcher = NotificationDispatcher(applicationContext, repository)
val api = ApiService()

View file

@ -227,10 +227,10 @@
<string name="settings_notifications_min_priority_high">High priority and higher</string>
<string name="settings_notifications_min_priority_max">Only max priority</string>
<string name="settings_notifications_auto_download_key">AutoDownload</string>
<string name="settings_notifications_auto_download_title">Auto download attachments</string>
<string name="settings_notifications_auto_download_summary_always">Attachments are always downloaded automatically</string>
<string name="settings_notifications_auto_download_summary_never">Attachments are never downloaded automatically</string>
<string name="settings_notifications_auto_download_summary_smaller_than_x">Attachments up to %1$s are downloaded automatically</string>
<string name="settings_notifications_auto_download_title">Download attachments</string>
<string name="settings_notifications_auto_download_summary_always">Automatically download all attachments</string>
<string name="settings_notifications_auto_download_summary_never">Never download attachments automatically</string>
<string name="settings_notifications_auto_download_summary_smaller_than_x">Auto-download attachments up to %1$s</string>
<string name="settings_notifications_auto_download_never">Never download automatically</string>
<string name="settings_notifications_auto_download_always">Always download automatically</string>
<string name="settings_notifications_auto_download_100k">If smaller than 100 KB</string>
@ -239,6 +239,20 @@
<string name="settings_notifications_auto_download_5m">If smaller than 5 MB</string>
<string name="settings_notifications_auto_download_10m">If smaller than 10 MB</string>
<string name="settings_notifications_auto_download_50m">If smaller than 50 MB</string>
<string name="settings_notifications_auto_delete_key">AutoDelete</string>
<string name="settings_notifications_auto_delete_title">Delete notifications</string>
<string name="settings_notifications_auto_delete_summary_never">Never automatically delete notifications</string>
<string name="settings_notifications_auto_delete_summary_one_day">Auto-delete notifications after one day</string>
<string name="settings_notifications_auto_delete_summary_three_days">Auto-delete notifications after 3 days</string>
<string name="settings_notifications_auto_delete_summary_one_week">Auto-delete notifications after one week</string>
<string name="settings_notifications_auto_delete_summary_one_month">Auto-delete notifications after one month</string>
<string name="settings_notifications_auto_delete_summary_three_months">Auto-delete notifications after 3 months</string>
<string name="settings_notifications_auto_delete_never">Never</string>
<string name="settings_notifications_auto_delete_one_day">After one day</string>
<string name="settings_notifications_auto_delete_three_days">After 3 days</string>
<string name="settings_notifications_auto_delete_one_week">After one week</string>
<string name="settings_notifications_auto_delete_one_month">After one month</string>
<string name="settings_notifications_auto_delete_three_months">After 3 months</string>
<string name="settings_appearance_header">Appearance</string>
<string name="settings_appearance_dark_mode_key">DarkMode</string>
<string name="settings_appearance_dark_mode_title">Dark mode</string>

View file

@ -53,6 +53,22 @@
<item>10485760</item>
<item>52428800</item>
</string-array>
<string-array name="settings_notifications_auto_delete_entries">
<item>@string/settings_notifications_auto_delete_never</item>
<item>@string/settings_notifications_auto_delete_one_day</item>
<item>@string/settings_notifications_auto_delete_three_days</item>
<item>@string/settings_notifications_auto_delete_one_week</item>
<item>@string/settings_notifications_auto_delete_one_month</item>
<item>@string/settings_notifications_auto_delete_three_months</item>
</string-array>
<string-array name="settings_notifications_auto_delete_values">
<item>0</item>
<item>86400</item>
<item>259200</item>
<item>604800</item>
<item>2592000</item>
<item>7776000</item>
</string-array>
<string-array name="settings_advanced_connection_protocol_entries">
<item>@string/settings_advanced_connection_protocol_entry_jsonhttp</item>
<item>@string/settings_advanced_connection_protocol_entry_ws</item>

View file

@ -21,6 +21,12 @@
app:entries="@array/settings_notifications_auto_download_entries"
app:entryValues="@array/settings_notifications_auto_download_values"
app:defaultValue="1"/>
<ListPreference
app:key="@string/settings_notifications_auto_delete_key"
app:title="@string/settings_notifications_auto_delete_title"
app:entries="@array/settings_notifications_auto_delete_entries"
app:entryValues="@array/settings_notifications_auto_delete_values"
app:defaultValue="2592000"/>
</PreferenceCategory>
<PreferenceCategory
app:title="@string/settings_appearance_header"

View file

@ -1,7 +1,7 @@
package io.heckel.ntfy.firebase
import com.google.firebase.messaging.FirebaseMessaging
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
class FirebaseMessenger {
fun subscribe(topic: String) {

View file

@ -9,7 +9,7 @@ import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
import io.heckel.ntfy.db.Attachment
import io.heckel.ntfy.db.Notification
import io.heckel.ntfy.log.Log
import io.heckel.ntfy.util.Log
import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.MESSAGE_ENCODING_BASE64
import io.heckel.ntfy.msg.NotificationDispatcher

View file

@ -0,0 +1,3 @@
Features:
* Dark theme: Improvements around style and contrast (#119, thanks @kzshantonu for reporting)
* Automatically delete notifications (#71, thanks @arjan-s for reporting)