Auto-download max size

This commit is contained in:
Philipp Heckel 2022-01-11 19:37:34 -05:00
parent 40d8d20cc5
commit 5a6d45d810
10 changed files with 98 additions and 44 deletions

View file

@ -162,14 +162,18 @@ class Repository(private val sharedPrefs: SharedPreferences, private val subscri
return sharedPrefs.getInt(SHARED_PREFS_MIN_PRIORITY, 1) // 1/low means all priorities
}
fun getAutoDownloadEnabled(): Boolean {
val defaultEnabled = Build.VERSION.SDK_INT > Build.VERSION_CODES.P // Need to request permission on older versions
return sharedPrefs.getBoolean(SHARED_PREFS_AUTO_DOWNLOAD_ENABLED, defaultEnabled)
fun getAutoDownloadMaxSize(): Long {
val defaultValue = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
AUTO_DOWNLOAD_NEVER // Need to request permission on older versions
} else {
AUTO_DOWNLOAD_DEFAULT
}
return sharedPrefs.getLong(SHARED_PREFS_AUTO_DOWNLOAD_MAX_SIZE, defaultValue)
}
fun setAutoDownloadEnabled(enabled: Boolean) {
fun setAutoDownloadMaxSize(maxSize: Long) {
sharedPrefs.edit()
.putBoolean(SHARED_PREFS_AUTO_DOWNLOAD_ENABLED, enabled)
.putLong(SHARED_PREFS_AUTO_DOWNLOAD_MAX_SIZE, maxSize)
.apply()
}
@ -303,12 +307,14 @@ class Repository(private val sharedPrefs: SharedPreferences, private val subscri
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_ENABLED = "AutoDownload"
const val SHARED_PREFS_AUTO_DOWNLOAD_MAX_SIZE = "AutoDownload"
const val SHARED_PREFS_BROADCAST_ENABLED = "BroadcastEnabled"
const val SHARED_PREFS_UNIFIED_PUSH_ENABLED = "UnifiedPushEnabled"
const val SHARED_PREFS_UNIFIED_PUSH_BASE_URL = "UnifiedPushBaseURL"
const val PREVIEWS_CACHE_DIR = "Previews"
const val AUTO_DOWNLOAD_NEVER = 0L
const val AUTO_DOWNLOAD_ALWAYS = 1L
const val AUTO_DOWNLOAD_DEFAULT = 1024 * 1024L // Must match a value in values.xml
private const val TAG = "NtfyRepository"
private var instance: Repository? = null

View file

@ -7,6 +7,7 @@ import com.google.gson.Gson
import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.data.Attachment
import io.heckel.ntfy.data.Notification
import io.heckel.ntfy.data.PROGRESS_NONE
import io.heckel.ntfy.util.*
import okhttp3.*
import okhttp3.RequestBody.Companion.toRequestBody
@ -54,11 +55,7 @@ class ApiService {
}
}
fun poll(subscriptionId: Long, baseUrl: String, topic: String): List<Notification> {
return poll(subscriptionId, baseUrl, topic, 0)
}
fun poll(subscriptionId: Long, baseUrl: String, topic: String, since: Long): List<Notification> {
fun poll(subscriptionId: Long, baseUrl: String, topic: String, since: Long = 0L): List<Notification> {
val sinceVal = if (since == 0L) "all" else since.toString()
val url = topicUrlJsonPoll(baseUrl, topic, sinceVal)
Log.d(TAG, "Polling topic $url")

View file

@ -119,6 +119,7 @@ class DownloadWorker(private val context: Context, params: WorkerParameters) : W
} catch (e: Exception) {
Log.w(TAG, "Attachment download failed", e)
// Mark attachment download as failed
val newAttachment = attachment.copy(progress = PROGRESS_FAILED)
val newNotification = notification.copy(attachment = newAttachment)
notifier.update(subscription, newNotification)

View file

@ -2,9 +2,6 @@ package io.heckel.ntfy.msg
import android.content.Context
import android.util.Log
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.workDataOf
import io.heckel.ntfy.data.Notification
import io.heckel.ntfy.data.Repository
import io.heckel.ntfy.data.Subscription
@ -31,7 +28,7 @@ class NotificationDispatcher(val context: Context, val repository: Repository) {
val notify = shouldNotify(subscription, notification, muted)
val broadcast = shouldBroadcast(subscription)
val distribute = shouldDistribute(subscription)
val download = shouldDownload(subscription, notification)
val download = shouldDownload(notification)
if (notify) {
notifier.display(subscription, notification)
}
@ -48,8 +45,21 @@ class NotificationDispatcher(val context: Context, val repository: Repository) {
}
}
private fun shouldDownload(subscription: Subscription, notification: Notification): Boolean {
return notification.attachment != null && repository.getAutoDownloadEnabled()
private fun shouldDownload(notification: Notification): Boolean {
if (notification.attachment == null) {
return false
}
val maxAutoDownloadSize = repository.getAutoDownloadMaxSize()
when (maxAutoDownloadSize) {
Repository.AUTO_DOWNLOAD_ALWAYS -> return true
Repository.AUTO_DOWNLOAD_NEVER -> return false
else -> {
if (notification.attachment.size == null) {
return false
}
return notification.attachment.size <= maxAutoDownloadSize
}
}
}
private fun shouldNotify(subscription: Subscription, notification: Notification, muted: Boolean): Boolean {

View file

@ -153,8 +153,10 @@ class SubscriberService : Service() {
// Start new connections and restart connections (if subscriptions have changed)
instantSubscriptionsByBaseUrl.forEach { (baseUrl, subscriptions) ->
// Do NOT request old messages for new connections; we'll call poll() in MainActivity.
// This is important, so we don't download attachments from old messages, which is not desired.
var since = System.currentTimeMillis()/1000
val connection = connections[baseUrl]
var since = 0L
if (connection != null && !connection.matches(subscriptions.values)) {
since = connection.since()
connections.remove(baseUrl)

View file

@ -20,6 +20,7 @@ import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
import io.heckel.ntfy.data.Repository
import io.heckel.ntfy.util.formatBytes
import io.heckel.ntfy.util.formatDateShort
import io.heckel.ntfy.util.toPriorityString
@ -49,6 +50,8 @@ class SettingsActivity : AppCompatActivity() {
}
class SettingsFragment(val repository: Repository, private val supportFragmentManager: FragmentManager) : PreferenceFragmentCompat() {
private var autoDownloadSelection = repository.getAutoDownloadMaxSize() // Only used for <= Android P, due to permissions request
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.main_preferences, rootKey)
@ -115,27 +118,30 @@ class SettingsActivity : AppCompatActivity() {
// Auto download
val autoDownloadPrefId = context?.getString(R.string.settings_notifications_auto_download_key) ?: return
val autoDownload: SwitchPreference? = findPreference(autoDownloadPrefId)
autoDownload?.isChecked = repository.getAutoDownloadEnabled()
val autoDownload: ListPreference? = findPreference(autoDownloadPrefId)
autoDownload?.value = repository.getAutoDownloadMaxSize().toString()
autoDownload?.preferenceDataStore = object : PreferenceDataStore() {
override fun putBoolean(key: String?, value: Boolean) {
repository.setAutoDownloadEnabled(value)
override fun putString(key: String?, value: String?) {
val maxSize = value?.toLongOrNull() ?:return
repository.setAutoDownloadMaxSize(maxSize)
}
override fun getBoolean(key: String?, defValue: Boolean): Boolean {
return repository.getAutoDownloadEnabled()
override fun getString(key: String?, defValue: String?): String {
return repository.getAutoDownloadMaxSize().toString()
}
}
autoDownload?.summaryProvider = Preference.SummaryProvider<SwitchPreference> { pref ->
if (pref.isChecked) {
getString(R.string.settings_notifications_auto_download_summary_on)
} else {
getString(R.string.settings_notifications_auto_download_summary_off)
autoDownload?.summaryProvider = Preference.SummaryProvider<ListPreference> { pref ->
val maxSize = pref.value.toLongOrNull() ?: repository.getAutoDownloadMaxSize()
when (maxSize) {
Repository.AUTO_DOWNLOAD_NEVER -> getString(R.string.settings_notifications_auto_download_summary_never)
Repository.AUTO_DOWNLOAD_ALWAYS -> getString(R.string.settings_notifications_auto_download_summary_always)
else -> getString(R.string.settings_notifications_auto_download_summary_smaller_than_x, formatBytes(maxSize, decimals = 0))
}
}
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
autoDownload?.setOnPreferenceChangeListener { _, v ->
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_CODE_WRITE_EXTERNAL_STORAGE_PERMISSION_FOR_AUTO_DOWNLOAD)
autoDownloadSelection = v.toString().toLongOrNull() ?: repository.getAutoDownloadMaxSize()
false // If permission is granted, auto-download will be enabled in onRequestPermissionsResult()
} else {
true
@ -222,10 +228,11 @@ class SettingsActivity : AppCompatActivity() {
}
}
fun enableAutoDownload() {
fun setAutoDownload() {
val autoDownloadPrefId = context?.getString(R.string.settings_notifications_auto_download_key) ?: return
val autoDownload: SwitchPreference? = findPreference(autoDownloadPrefId)
autoDownload?.isChecked = true
val autoDownload: ListPreference? = findPreference(autoDownloadPrefId)
autoDownload?.value = autoDownloadSelection.toString()
repository.setAutoDownloadMaxSize(autoDownloadSelection)
}
}
@ -233,15 +240,14 @@ class SettingsActivity : AppCompatActivity() {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_WRITE_EXTERNAL_STORAGE_PERMISSION_FOR_AUTO_DOWNLOAD) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
enableAutoDownload()
repository.setAutoDownloadEnabled(true)
setAutoDownload()
}
}
}
private fun enableAutoDownload() {
private fun setAutoDownload() {
if (!this::fragment.isInitialized) return
fragment.enableAutoDownload()
fragment.setAutoDownload()
}
companion object {

View file

@ -13,6 +13,7 @@ import java.security.SecureRandom
import java.text.DateFormat
import java.text.StringCharacterIterator
import java.util.*
import kotlin.math.abs
fun topicUrl(baseUrl: String, topic: String) = "${baseUrl}/${topic}"
fun topicUrlUp(baseUrl: String, topic: String) = "${baseUrl}/${topic}?up=1" // UnifiedPush
@ -163,8 +164,8 @@ inline fun <T1: Any, T2: Any, R: Any> safeLet(p1: T1?, p2: T2?, block: (T1, T2)-
return if (p1 != null && p2 != null) block(p1, p2) else null
}
fun formatBytes(bytes: Long): String {
val absB = if (bytes == Long.MIN_VALUE) Long.MAX_VALUE else Math.abs(bytes)
fun formatBytes(bytes: Long, decimals: Int = 1): String {
val absB = if (bytes == Long.MIN_VALUE) Long.MAX_VALUE else abs(bytes)
if (absB < 1024) {
return "$bytes B"
}
@ -177,7 +178,7 @@ fun formatBytes(bytes: Long): String {
i -= 10
}
value *= java.lang.Long.signum(bytes).toLong()
return java.lang.String.format("%.1f %cB", value / 1024.0, ci.current())
return java.lang.String.format("%.${decimals}f %cB", value / 1024.0, ci.current())
}
fun supportedImage(mimeType: String?): Boolean {

View file

@ -193,8 +193,17 @@
<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_on">Attachments are automatically downloaded</string>
<string name="settings_notifications_auto_download_summary_off">Attachments are not automatically downloaded</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_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>
<string name="settings_notifications_auto_download_500k">If smaller than 500 KB</string>
<string name="settings_notifications_auto_download_1m">If smaller than 1 MB</string>
<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_unified_push_header">UnifiedPush</string>
<string name="settings_unified_push_header_summary">Allows other apps to use ntfy as a message distributor. Find out more at unifiedpush.org.</string>
<string name="settings_unified_push_enabled_key">UnifiedPushEnabled</string>

View file

@ -14,4 +14,24 @@
<item>4</item>
<item>5</item>
</string-array>
<string-array name="settings_notifications_auto_download_entries">
<item>@string/settings_notifications_auto_download_never</item>
<item>@string/settings_notifications_auto_download_always</item>
<item>@string/settings_notifications_auto_download_100k</item>
<item>@string/settings_notifications_auto_download_500k</item>
<item>@string/settings_notifications_auto_download_1m</item>
<item>@string/settings_notifications_auto_download_5m</item>
<item>@string/settings_notifications_auto_download_10m</item>
<item>@string/settings_notifications_auto_download_50m</item>
</string-array>
<string-array name="settings_notifications_auto_download_values">
<item>0</item>
<item>1</item>
<item>102400</item>
<item>512000</item>
<item>1048576</item>
<item>5242880</item>
<item>10485760</item>
<item>52428800</item>
</string-array>
</resources>

View file

@ -13,10 +13,12 @@
app:entries="@array/settings_notifications_min_priority_entries"
app:entryValues="@array/settings_notifications_min_priority_values"
app:defaultValue="1"/>
<SwitchPreference
<ListPreference
app:key="@string/settings_notifications_auto_download_key"
app:title="@string/settings_notifications_auto_download_title"
app:enabled="true"/>
app:entries="@array/settings_notifications_auto_download_entries"
app:entryValues="@array/settings_notifications_auto_download_values"
app:defaultValue="1"/>
</PreferenceCategory>
<PreferenceCategory
app:title="@string/settings_unified_push_header"