Merge branch 'main' of github.com:binwiederhier/ntfy-android into disable-up

This commit is contained in:
Philipp Heckel 2026-04-07 09:23:46 -04:00
commit 41912441cd
43 changed files with 723 additions and 104 deletions

View file

@ -3,6 +3,7 @@
<!-- Permissions --> <!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <!-- To check network availability before showing connection alerts -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <!-- For instant delivery foregrounds service --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <!-- For instant delivery foregrounds service -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/> <!-- For instant delivery foregrounds service on SDK >= 34 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/> <!-- For instant delivery foregrounds service on SDK >= 34 -->
<uses-permission android:name="android.permission.WAKE_LOCK"/> <!-- To keep foreground service awake; soon not needed anymore --> <uses-permission android:name="android.permission.WAKE_LOCK"/> <!-- To keep foreground service awake; soon not needed anymore -->
@ -131,14 +132,15 @@
android:enabled="true" android:enabled="true"
android:exported="false"/> android:exported="false"/>
<!-- Broadcast receiver for connection alert notification actions (dismiss, snooze, never show again) --> <!-- Broadcast receiver for connection alert notification actions (swipe-dismiss, snooze, never) -->
<receiver <receiver
android:name=".service.SubscriberService$ConnectionAlertBroadcastReceiver" android:name=".service.SubscriberService$ConnectionAlertBroadcastReceiver"
android:enabled="true" android:enabled="true"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="io.heckel.ntfy.CONNECTION_ALERT_DISMISS"/> <action android:name="io.heckel.ntfy.CONNECTION_ALERT_DISMISS"/>
<action android:name="io.heckel.ntfy.CONNECTION_ALERT_SNOOZE"/> <action android:name="io.heckel.ntfy.CONNECTION_ALERT_SNOOZE_SHORT"/>
<action android:name="io.heckel.ntfy.CONNECTION_ALERT_SNOOZE_LONG"/>
<action android:name="io.heckel.ntfy.CONNECTION_ALERT_NEVER"/> <action android:name="io.heckel.ntfy.CONNECTION_ALERT_NEVER"/>
</intent-filter> </intent-filter>
</receiver> </receiver>

View file

@ -1,8 +1,11 @@
package io.heckel.ntfy.app package io.heckel.ntfy.app
import android.app.Application import android.app.Application
import android.net.ConnectivityManager
import android.net.Network
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import io.heckel.ntfy.db.Repository import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.service.SubscriberServiceManager
import io.heckel.ntfy.util.Log import io.heckel.ntfy.util.Log
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -24,5 +27,18 @@ class Application : Application() {
if (repository.getDynamicColorsEnabled()) { if (repository.getDynamicColorsEnabled()) {
DynamicColors.applyToActivitiesIfAvailable(this) DynamicColors.applyToActivitiesIfAvailable(this)
} }
registerNetworkCallback()
}
private fun registerNetworkCallback() {
val connectivityManager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
SubscriberServiceManager.refresh(this@Application)
}
override fun onLost(network: Network) {
SubscriberServiceManager.refresh(this@Application)
}
})
} }
} }

View file

@ -90,6 +90,9 @@ class Backuper(val context: Context) {
if (settings.mutedUntil != null) { if (settings.mutedUntil != null) {
repository.setGlobalMutedUntil(settings.mutedUntil) repository.setGlobalMutedUntil(settings.mutedUntil)
} }
if (settings.connectionAlertSeconds != null) {
repository.setConnectionAlertSeconds(settings.connectionAlertSeconds)
}
if (settings.lastSharedTopics != null) { if (settings.lastSharedTopics != null) {
settings.lastSharedTopics.forEach { repository.addLastShareTopic(it) } settings.lastSharedTopics.forEach { repository.addLastShareTopic(it) }
} }
@ -278,6 +281,7 @@ class Backuper(val context: Context) {
recordLogs = repository.getRecordLogs(), recordLogs = repository.getRecordLogs(),
defaultBaseUrl = repository.getDefaultBaseUrl() ?: "", defaultBaseUrl = repository.getDefaultBaseUrl() ?: "",
mutedUntil = repository.getGlobalMutedUntil(), mutedUntil = repository.getGlobalMutedUntil(),
connectionAlertSeconds = repository.getConnectionAlertSeconds(),
lastSharedTopics = repository.getLastShareTopics() lastSharedTopics = repository.getLastShareTopics()
) )
} }
@ -421,6 +425,7 @@ data class Settings(
val recordLogs: Boolean?, val recordLogs: Boolean?,
val defaultBaseUrl: String?, val defaultBaseUrl: String?,
val mutedUntil: Long?, val mutedUntil: Long?,
val connectionAlertSeconds: Long?,
val lastSharedTopics: List<String>?, val lastSharedTopics: List<String>?,
) )

View file

@ -575,6 +575,9 @@ interface SubscriptionDao {
""") """)
fun getLastNotificationId(subscriptionIds: Collection<Long>): String? fun getLastNotificationId(subscriptionIds: Collection<Long>): String?
@Query("UPDATE subscription SET icon = :icon WHERE id = :subscriptionId")
fun updateSubscriptionIcon(subscriptionId: Long, icon: String?)
@Query("DELETE FROM subscription WHERE id = :subscriptionId") @Query("DELETE FROM subscription WHERE id = :subscriptionId")
fun remove(subscriptionId: Long) fun remove(subscriptionId: Long)
} }

View file

@ -92,6 +92,10 @@ class Repository(private val sharedPrefs: SharedPreferences, database: Database)
subscriptionDao.update(subscription) subscriptionDao.update(subscription)
} }
fun updateSubscriptionIcon(subscriptionId: Long, icon: String?) {
subscriptionDao.updateSubscriptionIcon(subscriptionId, icon)
}
@Suppress("RedundantSuspendModifier") @Suppress("RedundantSuspendModifier")
@WorkerThread @WorkerThread
suspend fun removeSubscription(subscription: Subscription) { suspend fun removeSubscription(subscription: Subscription) {
@ -440,13 +444,24 @@ class Repository(private val sharedPrefs: SharedPreferences, database: Database)
} }
} }
fun getConnectionAlertSnoozeUntil(): Long { fun getConnectionAlertSeconds(): Long {
return sharedPrefs.getLong(SHARED_PREFS_CONNECTION_ALERT_SNOOZE_UNTIL, CONNECTION_ALERT_SNOOZE_UNTIL_DEFAULT) return sharedPrefs.getLong(SHARED_PREFS_CONNECTION_ALERT_SECONDS, CONNECTION_ALERT_DEFAULT)
} }
fun setConnectionAlertSnoozeUntil(timeMillis: Long) { fun setConnectionAlertSeconds(seconds: Long) {
sharedPrefs.edit { sharedPrefs.edit {
putLong(SHARED_PREFS_CONNECTION_ALERT_SNOOZE_UNTIL, timeMillis) putLong(SHARED_PREFS_CONNECTION_ALERT_SECONDS, seconds)
putLong(SHARED_PREFS_CONNECTION_ALERT_SNOOZE_UNTIL_TIME, 0L)
}
}
fun getConnectionAlertSnoozeUntilTime(): Long {
return sharedPrefs.getLong(SHARED_PREFS_CONNECTION_ALERT_SNOOZE_UNTIL_TIME, 0L)
}
fun setConnectionAlertSnoozeUntilTime(timeMillis: Long) {
sharedPrefs.edit {
putLong(SHARED_PREFS_CONNECTION_ALERT_SNOOZE_UNTIL_TIME, timeMillis)
} }
} }
@ -608,6 +623,11 @@ class Repository(private val sharedPrefs: SharedPreferences, database: Database)
return connectionDetails.toMap() return connectionDetails.toMap()
} }
fun clearConnectionDetails() {
connectionDetails.clear()
connectionDetailsLiveData.postValue(emptyMap())
}
fun getConnectionForceReconnectVersion(baseUrl: String): Long { fun getConnectionForceReconnectVersion(baseUrl: String): Long {
return connectionForceReconnectVersions[baseUrl] ?: 0L return connectionForceReconnectVersions[baseUrl] ?: 0L
} }
@ -634,14 +654,13 @@ class Repository(private val sharedPrefs: SharedPreferences, database: Database)
const val SHARED_PREFS_INSISTENT_MAX_PRIORITY_ENABLED = "InsistentMaxPriority" const val SHARED_PREFS_INSISTENT_MAX_PRIORITY_ENABLED = "InsistentMaxPriority"
const val SHARED_PREFS_RECORD_LOGS_ENABLED = "RecordLogs" const val SHARED_PREFS_RECORD_LOGS_ENABLED = "RecordLogs"
const val SHARED_PREFS_MESSAGE_BAR_ENABLED = "MessageBarEnabled" const val SHARED_PREFS_MESSAGE_BAR_ENABLED = "MessageBarEnabled"
const val SHARED_PREFS_BATTERY_OPTIMIZATIONS_REMIND_TIME = "BatteryOptimizationsRemindTime" const val SHARED_PREFS_BATTERY_OPTIMIZATIONS_REMIND_TIME = "BatteryOptimizationsRemindTime" // Timestamp as millis
const val SHARED_PREFS_WEBSOCKET_REMIND_TIME = "JsonStreamRemindTime" // "Use WebSocket" banner (used to be JSON stream deprecation banner) const val SHARED_PREFS_WEBSOCKET_REMIND_TIME = "JsonStreamRemindTime" // "Use WebSocket" banner (used to be JSON stream deprecation banner), timestamp as millis
const val SHARED_PREFS_WEBSOCKET_RECONNECT_REMIND_TIME = "WebSocketReconnectRemindTime" const val SHARED_PREFS_WEBSOCKET_RECONNECT_REMIND_TIME = "WebSocketReconnectRemindTime" // Timestamp as millis
const val SHARED_PREFS_UNIFIED_PUSH_BASE_URL = "UnifiedPushBaseURL" // Legacy key required for migration to DefaultBaseURL const val SHARED_PREFS_UNIFIED_PUSH_BASE_URL = "UnifiedPushBaseURL" // Legacy key required for migration to DefaultBaseURL
const val SHARED_PREFS_DEFAULT_BASE_URL = "DefaultBaseURL" const val SHARED_PREFS_DEFAULT_BASE_URL = "DefaultBaseURL"
const val SHARED_PREFS_CONNECTION_ALERT_SNOOZE_UNTIL = "ConnectionAlertSnoozeUntil" const val SHARED_PREFS_CONNECTION_ALERT_SECONDS = "ConnectionAlertSeconds"
const val CONNECTION_ALERT_SNOOZE_UNTIL_DEFAULT = 0L const val SHARED_PREFS_CONNECTION_ALERT_SNOOZE_UNTIL_TIME = "ConnectionAlertSnoozeUntilTime" // Timestamp in millis
const val CONNECTION_ALERT_NEVER_SHOW = Long.MAX_VALUE
const val SHARED_PREFS_LAST_TOPICS = "LastTopics" const val SHARED_PREFS_LAST_TOPICS = "LastTopics"
private const val LAST_TOPICS_COUNT = 3 private const val LAST_TOPICS_COUNT = 3
@ -653,12 +672,14 @@ class Repository(private val sharedPrefs: SharedPreferences, database: Database)
const val MUTED_UNTIL_FOREVER = 1L const val MUTED_UNTIL_FOREVER = 1L
const val MUTED_UNTIL_TOMORROW = 2L const val MUTED_UNTIL_TOMORROW = 2L
private const val ONE_MB = 1024 * 1024L private const val ONE_MB_BYTES = 1024 * 1024L
const val AUTO_DOWNLOAD_NEVER = 0L // Values must match values.xml const val AUTO_DOWNLOAD_NEVER = 0L // Values must match values.xml
const val AUTO_DOWNLOAD_ALWAYS = 1L const val AUTO_DOWNLOAD_ALWAYS = 1L
const val AUTO_DOWNLOAD_DEFAULT = ONE_MB const val AUTO_DOWNLOAD_DEFAULT = ONE_MB_BYTES
private const val ONE_HOUR_SECONDS = 60 * 60L
private const val ONE_DAY_SECONDS = 24 * ONE_HOUR_SECONDS
private const val ONE_DAY_SECONDS = 24 * 60 * 60L
const val AUTO_DELETE_USE_GLOBAL = -1L // Values must match values.xml const val AUTO_DELETE_USE_GLOBAL = -1L // Values must match values.xml
const val AUTO_DELETE_NEVER = 0L const val AUTO_DELETE_NEVER = 0L
const val AUTO_DELETE_ONE_DAY_SECONDS = ONE_DAY_SECONDS const val AUTO_DELETE_ONE_DAY_SECONDS = ONE_DAY_SECONDS
@ -671,6 +692,14 @@ class Repository(private val sharedPrefs: SharedPreferences, database: Database)
const val INSISTENT_MAX_PRIORITY_USE_GLOBAL = -1 // Values must match values.xml const val INSISTENT_MAX_PRIORITY_USE_GLOBAL = -1 // Values must match values.xml
const val INSISTENT_MAX_PRIORITY_ENABLED = 1 // 0 = Disabled (but not needed in code) const val INSISTENT_MAX_PRIORITY_ENABLED = 1 // 0 = Disabled (but not needed in code)
const val CONNECTION_ALERT_NEVER = 0L
const val CONNECTION_ALERT_FIVE_MINUTES_SECONDS = 5 * 60L
const val CONNECTION_ALERT_FIFTEEN_MINUTES_SECONDS = 15 * 60L
const val CONNECTION_ALERT_ONE_HOUR_SECONDS = ONE_HOUR_SECONDS
const val CONNECTION_ALERT_THREE_HOURS_SECONDS = 3 * ONE_HOUR_SECONDS
const val CONNECTION_ALERT_TWELVE_HOURS_SECONDS = 12 * ONE_HOUR_SECONDS
const val CONNECTION_ALERT_DEFAULT = CONNECTION_ALERT_NEVER
const val CONNECTION_PROTOCOL_JSONHTTP = "jsonhttp" const val CONNECTION_PROTOCOL_JSONHTTP = "jsonhttp"
const val CONNECTION_PROTOCOL_WS = "ws" const val CONNECTION_PROTOCOL_WS = "ws"

View file

@ -24,12 +24,12 @@ import io.heckel.ntfy.db.Repository
import io.heckel.ntfy.db.Subscription import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.msg.ApiService import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.NotificationDispatcher import io.heckel.ntfy.msg.NotificationDispatcher
import io.heckel.ntfy.msg.NotificationService
import io.heckel.ntfy.util.PRIORITY_HIGH
import io.heckel.ntfy.ui.Colors import io.heckel.ntfy.ui.Colors
import io.heckel.ntfy.ui.MainActivity import io.heckel.ntfy.ui.MainActivity
import io.heckel.ntfy.util.HttpUtil import io.heckel.ntfy.util.HttpUtil
import io.heckel.ntfy.util.Log import io.heckel.ntfy.util.Log
import io.heckel.ntfy.util.isNetworkAvailable
import io.heckel.ntfy.util.shortUrl
import io.heckel.ntfy.util.topicUrl import io.heckel.ntfy.util.topicUrl
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -316,22 +316,28 @@ class SubscriberService : Service() {
} }
private fun maybeShowConnectionAlert() { private fun maybeShowConnectionAlert() {
val now = System.currentTimeMillis() val thresholdSeconds = repository.getConnectionAlertSeconds()
if (thresholdSeconds <= 0L) return
// Check snooze / never-show-again // Don't show alert if the device has no network connectivity (e.g. airplane mode)
val snoozeUntil = repository.getConnectionAlertSnoozeUntil() if (!isNetworkAvailable(this)) return
if (snoozeUntil == Repository.CONNECTION_ALERT_NEVER_SHOW) return
// Check snooze
val now = System.currentTimeMillis()
val snoozeUntil = repository.getConnectionAlertSnoozeUntilTime()
if (snoozeUntil > now) return if (snoozeUntil > now) return
// Check if any connection has been in error for 15+ minutes // Check if any connection has been in error for longer than the threshold
val thresholdMillis = thresholdSeconds * 1000L
val allDetails = repository.getConnectionDetails() val allDetails = repository.getConnectionDetails()
val disconnectedUrls = allDetails.filter { (_, details) -> val disconnectedUrls = allDetails.filter { (_, details) ->
details.hasError() && details.firstErrorTime > 0L && details.hasError() && details.firstErrorTime > 0L &&
(now - details.firstErrorTime) >= CONNECTION_ALERT_THRESHOLD_MILLIS (now - details.firstErrorTime) >= thresholdMillis
}.keys }.keys
if (disconnectedUrls.isNotEmpty()) { if (disconnectedUrls.isNotEmpty()) {
showConnectionAlertNotification(disconnectedUrls) val thresholdMinutes = (thresholdSeconds / 60).toInt()
showConnectionAlertNotification(disconnectedUrls, thresholdMinutes)
} }
} }
@ -345,47 +351,40 @@ class SubscriberService : Service() {
} }
} }
private fun showConnectionAlertNotification(disconnectedUrls: Set<String>) { private fun showConnectionAlertNotification(disconnectedUrls: Set<String>, thresholdMinutes: Int) {
val text = if (disconnectedUrls.size == 1) { val text = if (disconnectedUrls.size == 1) {
getString(R.string.connection_alert_text_one, disconnectedUrls.first(), CONNECTION_ALERT_THRESHOLD_MINUTES) getString(R.string.connection_alert_text_one, shortUrl(disconnectedUrls.first()), thresholdMinutes)
} else { } else {
getString(R.string.connection_alert_text_multiple, disconnectedUrls.size, CONNECTION_ALERT_THRESHOLD_MINUTES) getString(R.string.connection_alert_text_multiple, disconnectedUrls.size, thresholdMinutes)
} }
val dismissIntent = Intent(this, ConnectionAlertBroadcastReceiver::class.java).apply {
action = CONNECTION_ALERT_ACTION_DISMISS
}
val dismissPendingIntent = PendingIntent.getBroadcast(this, 0, dismissIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val snoozeIntent = Intent(this, ConnectionAlertBroadcastReceiver::class.java).apply {
action = CONNECTION_ALERT_ACTION_SNOOZE
}
val snoozePendingIntent = PendingIntent.getBroadcast(this, 1, snoozeIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val neverIntent = Intent(this, ConnectionAlertBroadcastReceiver::class.java).apply {
action = CONNECTION_ALERT_ACTION_NEVER
}
val neverPendingIntent = PendingIntent.getBroadcast(this, 2, neverIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val contentIntent = PendingIntent.getActivity(this, 0, val contentIntent = PendingIntent.getActivity(this, 0,
Intent(this, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE) Intent(this, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE)
val snoozeShortIntent = PendingIntent.getBroadcast(this, 0,
Intent(this, ConnectionAlertBroadcastReceiver::class.java).apply { action = CONNECTION_ALERT_ACTION_SNOOZE_SHORT },
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val snoozeIntent = PendingIntent.getBroadcast(this, 0,
Intent(this, ConnectionAlertBroadcastReceiver::class.java).apply { action = CONNECTION_ALERT_ACTION_SNOOZE_LONG },
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val neverAlertIntent = PendingIntent.getBroadcast(this, 0,
Intent(this, ConnectionAlertBroadcastReceiver::class.java).apply { action = CONNECTION_ALERT_ACTION_NEVER },
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val deleteIntent = PendingIntent.getBroadcast(this, 0,
Intent(this, ConnectionAlertBroadcastReceiver::class.java).apply { action = CONNECTION_ALERT_ACTION_DISMISS },
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val channelId = NotificationService(this).toChannelId(NotificationService.DEFAULT_GROUP, PRIORITY_HIGH) val notification = NotificationCompat.Builder(this, NOTIFICATION_CONNECTION_ALERT_CHANNEL_ID)
val notification = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_notification) .setSmallIcon(R.drawable.ic_notification)
.setColor(Colors.notificationIcon(this)) .setColor(Colors.notificationIcon(this))
.setContentTitle(getString(R.string.connection_alert_title)) .setContentTitle(getString(R.string.connection_alert_title))
.setContentText(text) .setContentText(text)
.setStyle(NotificationCompat.BigTextStyle().bigText(text)) .setStyle(NotificationCompat.BigTextStyle().bigText(text))
.setContentIntent(contentIntent) .setContentIntent(contentIntent)
.setAutoCancel(true)
.setOnlyAlertOnce(true) .setOnlyAlertOnce(true)
.addAction(NotificationCompat.Action.Builder(0, getString(R.string.connection_alert_action_dismiss), dismissPendingIntent).build()) .setDeleteIntent(deleteIntent)
.addAction(NotificationCompat.Action.Builder(0, getString(R.string.connection_alert_action_snooze), snoozePendingIntent).build()) .addAction(NotificationCompat.Action.Builder(0, getString(R.string.connection_alert_action_snooze, CONNECTION_ALERT_SNOOZE_SHORT_HOURS), snoozeShortIntent).build())
.addAction(NotificationCompat.Action.Builder(0, getString(R.string.connection_alert_action_never), neverPendingIntent).build()) .addAction(NotificationCompat.Action.Builder(0, getString(R.string.connection_alert_action_snooze, CONNECTION_ALERT_SNOOZE_LONG_HOURS), snoozeIntent).build())
.addAction(NotificationCompat.Action.Builder(0, getString(R.string.connection_alert_action_never), neverAlertIntent).build())
.build() .build()
Log.d(TAG, "Showing connection alert notification") Log.d(TAG, "Showing connection alert notification")
@ -440,6 +439,9 @@ class SubscriberService : Service() {
it it
} }
notificationManager.createNotificationChannel(channel) notificationManager.createNotificationChannel(channel)
val connectionAlertChannelName = getString(R.string.channel_connection_alert_name)
val connectionAlertChannel = NotificationChannel(NOTIFICATION_CONNECTION_ALERT_CHANNEL_ID, connectionAlertChannelName, NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(connectionAlertChannel)
return notificationManager return notificationManager
} }
@ -500,22 +502,19 @@ class SubscriberService : Service() {
Log.d(TAG, "ConnectionAlertBroadcastReceiver: action=${intent.action}") Log.d(TAG, "ConnectionAlertBroadcastReceiver: action=${intent.action}")
val repository = Repository.getInstance(context) val repository = Repository.getInstance(context)
val notificationManager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager val notificationManager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
when (intent.action) { when (intent.action) {
CONNECTION_ALERT_ACTION_DISMISS -> { CONNECTION_ALERT_ACTION_DISMISS, CONNECTION_ALERT_ACTION_SNOOZE_SHORT -> {
notificationManager.cancel(NOTIFICATION_CONNECTION_ALERT_ID) repository.setConnectionAlertSnoozeUntilTime(System.currentTimeMillis() + CONNECTION_ALERT_SNOOZE_SHORT_MILLIS)
} }
CONNECTION_ALERT_ACTION_SNOOZE -> { CONNECTION_ALERT_ACTION_SNOOZE_LONG -> {
repository.setConnectionAlertSnoozeUntil( repository.setConnectionAlertSnoozeUntilTime(System.currentTimeMillis() + CONNECTION_ALERT_SNOOZE_LONG_MILLIS)
System.currentTimeMillis() + CONNECTION_ALERT_SNOOZE_DURATION_MILLIS
)
notificationManager.cancel(NOTIFICATION_CONNECTION_ALERT_ID)
} }
CONNECTION_ALERT_ACTION_NEVER -> { CONNECTION_ALERT_ACTION_NEVER -> {
repository.setConnectionAlertSnoozeUntil(Repository.CONNECTION_ALERT_NEVER_SHOW) repository.setConnectionAlertSeconds(Repository.CONNECTION_ALERT_NEVER)
notificationManager.cancel(NOTIFICATION_CONNECTION_ALERT_ID)
} }
else -> return
} }
notificationManager.cancel(NOTIFICATION_CONNECTION_ALERT_ID)
} }
} }
@ -529,18 +528,24 @@ class SubscriberService : Service() {
const val SERVICE_START_WORKER_VERSION = BuildConfig.VERSION_CODE const val SERVICE_START_WORKER_VERSION = BuildConfig.VERSION_CODE
const val SERVICE_START_WORKER_WORK_NAME_PERIODIC = "NtfyAutoRestartWorkerPeriodic" // Do not change! const val SERVICE_START_WORKER_WORK_NAME_PERIODIC = "NtfyAutoRestartWorkerPeriodic" // Do not change!
private const val ONE_HOUR_SECONDS = 60 * 60L
private const val ONE_HOUR_MILLIS = ONE_HOUR_SECONDS * 1000L
private const val WAKE_LOCK_TAG = "SubscriberService:lock" private const val WAKE_LOCK_TAG = "SubscriberService:lock"
private const val NOTIFICATION_CHANNEL_ID = "ntfy-subscriber" private const val NOTIFICATION_CHANNEL_ID = "ntfy-subscriber"
private const val NOTIFICATION_CONNECTION_ALERT_CHANNEL_ID = "ntfy-connection-alert"
private const val NOTIFICATION_GROUP_ID = "io.heckel.ntfy.NOTIFICATION_GROUP_SERVICE" private const val NOTIFICATION_GROUP_ID = "io.heckel.ntfy.NOTIFICATION_GROUP_SERVICE"
private const val NOTIFICATION_SERVICE_ID = 2586 private const val NOTIFICATION_SERVICE_ID = 2586
private const val NOTIFICATION_RECEIVED_WAKELOCK_TIMEOUT_MILLIS = 10 * 60 * 1000L /*10 minutes*/ private const val NOTIFICATION_RECEIVED_WAKELOCK_TIMEOUT_MILLIS = 10 * 60 * 1000L /*10 minutes*/
private const val NOTIFICATION_CONNECTION_ALERT_ID = 2587 const val NOTIFICATION_CONNECTION_ALERT_ID = 2587
private const val CONNECTION_ALERT_THRESHOLD_MINUTES = 15 private const val CONNECTION_ALERT_SNOOZE_SHORT_HOURS = 1
private const val CONNECTION_ALERT_THRESHOLD_MILLIS = CONNECTION_ALERT_THRESHOLD_MINUTES * 60 * 1000L private const val CONNECTION_ALERT_SNOOZE_SHORT_MILLIS = CONNECTION_ALERT_SNOOZE_SHORT_HOURS * ONE_HOUR_MILLIS
private const val CONNECTION_ALERT_SNOOZE_DURATION_MILLIS = 60 * 60 * 1000L /*1 hour*/ private const val CONNECTION_ALERT_SNOOZE_LONG_HOURS = 8
private const val CONNECTION_ALERT_SNOOZE_LONG_MILLIS = CONNECTION_ALERT_SNOOZE_LONG_HOURS * ONE_HOUR_MILLIS
private const val CONNECTION_ALERT_ACTION_DISMISS = "io.heckel.ntfy.CONNECTION_ALERT_DISMISS" private const val CONNECTION_ALERT_ACTION_DISMISS = "io.heckel.ntfy.CONNECTION_ALERT_DISMISS"
private const val CONNECTION_ALERT_ACTION_SNOOZE = "io.heckel.ntfy.CONNECTION_ALERT_SNOOZE" private const val CONNECTION_ALERT_ACTION_SNOOZE_SHORT = "io.heckel.ntfy.CONNECTION_ALERT_SNOOZE_SHORT"
private const val CONNECTION_ALERT_ACTION_SNOOZE_LONG = "io.heckel.ntfy.CONNECTION_ALERT_SNOOZE_LONG"
private const val CONNECTION_ALERT_ACTION_NEVER = "io.heckel.ntfy.CONNECTION_ALERT_NEVER" private const val CONNECTION_ALERT_ACTION_NEVER = "io.heckel.ntfy.CONNECTION_ALERT_NEVER"
} }
} }

View file

@ -1,11 +1,13 @@
package io.heckel.ntfy.service package io.heckel.ntfy.service
import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.work.* import androidx.work.*
import io.heckel.ntfy.app.Application import io.heckel.ntfy.app.Application
import io.heckel.ntfy.util.Log import io.heckel.ntfy.util.Log
import io.heckel.ntfy.util.isNetworkAvailable
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -40,8 +42,9 @@ class SubscriberServiceManager(private val context: Context) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val app = context.applicationContext as Application val app = context.applicationContext as Application
val subscriptionIdsWithInstantStatus = app.repository.getSubscriptionIdsWithInstantStatus() val subscriptionIdsWithInstantStatus = app.repository.getSubscriptionIdsWithInstantStatus()
val hasNetwork = isNetworkAvailable(context)
val instantSubscriptions = subscriptionIdsWithInstantStatus.toList().filter { (_, instant) -> instant }.size val instantSubscriptions = subscriptionIdsWithInstantStatus.toList().filter { (_, instant) -> instant }.size
if (instantSubscriptions > 0) { if (instantSubscriptions > 0 && hasNetwork) {
// We have instant subscriptions, start the service // We have instant subscriptions, start the service
Log.d(TAG, "ServiceStartWorker: Starting foreground service (work ID: ${id})") Log.d(TAG, "ServiceStartWorker: Starting foreground service (work ID: ${id})")
Intent(context, SubscriberService::class.java).also { Intent(context, SubscriberService::class.java).also {
@ -56,9 +59,14 @@ class SubscriberServiceManager(private val context: Context) {
} }
} }
} else { } else {
// No instant subscriptions, stop the service using stopService() // No instant subscriptions (or no network), stop the service using stopService()
// This avoids ForegroundServiceDidNotStartInTimeException, see #1520 // This avoids ForegroundServiceDidNotStartInTimeException, see #1520
Log.d(TAG, "ServiceStartWorker: Stopping service (work ID: ${id})") Log.d(TAG, "ServiceStartWorker: Stopping service (work ID: ${id})")
if (!hasNetwork) {
app.repository.clearConnectionDetails()
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(SubscriberService.NOTIFICATION_CONNECTION_ALERT_ID)
}
Intent(context, SubscriberService::class.java).also { Intent(context, SubscriberService::class.java).also {
context.stopService(it) context.stopService(it)
} }

View file

@ -120,7 +120,7 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope:
messageView.autoLinkMask = 0 messageView.autoLinkMask = 0
markwon.setMarkdown(messageView, message.toString()) markwon.setMarkdown(messageView, message.toString())
} else { } else {
messageView.autoLinkMask = Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES or Linkify.PHONE_NUMBERS messageView.autoLinkMask = Linkify.WEB_URLS
messageView.text = message messageView.text = message
} }
messageView.movementMethod = BetterLinkMovementMethod.getInstance() messageView.movementMethod = BetterLinkMovementMethod.getInstance()

View file

@ -486,7 +486,7 @@ class DetailSettingsActivity : AppCompatActivity() {
} }
private fun createUri(): Uri? { private fun createUri(): Uri? {
val dir = File(requireContext().cacheDir, SUBSCRIPTION_ICONS) val dir = File(requireContext().filesDir, SUBSCRIPTION_ICONS)
if (!dir.exists() && !dir.mkdirs()) { if (!dir.exists() && !dir.mkdirs()) {
return null return null
} }
@ -526,7 +526,6 @@ class DetailSettingsActivity : AppCompatActivity() {
companion object { companion object {
private const val TAG = "NtfyDetailSettingsActiv" private const val TAG = "NtfyDetailSettingsActiv"
private const val SUBSCRIPTION_ICONS = "subscriptionIcons"
private const val SUBSCRIPTION_ICON_MAX_SIZE_BYTES = 4194304 private const val SUBSCRIPTION_ICON_MAX_SIZE_BYTES = 4194304
private const val SUBSCRIPTION_ICON_MAX_WIDTH = 2048 private const val SUBSCRIPTION_ICON_MAX_WIDTH = 2048
private const val SUBSCRIPTION_ICON_MAX_HEIGHT = 2048 private const val SUBSCRIPTION_ICON_MAX_HEIGHT = 2048

View file

@ -6,8 +6,11 @@ import android.animation.AnimatorListenerAdapter
import android.app.AlarmManager import android.app.AlarmManager
import android.app.AlertDialog import android.app.AlertDialog
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.Network
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
@ -58,11 +61,13 @@ import io.heckel.ntfy.msg.Poller
import io.heckel.ntfy.service.SubscriberService import io.heckel.ntfy.service.SubscriberService
import io.heckel.ntfy.service.SubscriberServiceManager import io.heckel.ntfy.service.SubscriberServiceManager
import io.heckel.ntfy.util.Log import io.heckel.ntfy.util.Log
import io.heckel.ntfy.util.SUBSCRIPTION_ICONS
import io.heckel.ntfy.util.dangerButton import io.heckel.ntfy.util.dangerButton
import io.heckel.ntfy.util.displayName import io.heckel.ntfy.util.displayName
import io.heckel.ntfy.util.formatDateShort import io.heckel.ntfy.util.formatDateShort
import io.heckel.ntfy.util.isDarkThemeOn import io.heckel.ntfy.util.isDarkThemeOn
import io.heckel.ntfy.util.isIgnoringBatteryOptimizations import io.heckel.ntfy.util.isIgnoringBatteryOptimizations
import io.heckel.ntfy.util.isNetworkAvailable
import io.heckel.ntfy.util.maybeSplitTopicUrl import io.heckel.ntfy.util.maybeSplitTopicUrl
import io.heckel.ntfy.util.randomSubscriptionId import io.heckel.ntfy.util.randomSubscriptionId
import io.heckel.ntfy.util.shortUrl import io.heckel.ntfy.util.shortUrl
@ -72,8 +77,10 @@ import io.heckel.ntfy.work.PollWorker
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File
import java.util.Date import java.util.Date
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import androidx.core.content.FileProvider
import androidx.core.view.size import androidx.core.view.size
import androidx.core.view.get import androidx.core.view.get
import androidx.core.net.toUri import androidx.core.net.toUri
@ -95,6 +102,7 @@ class MainActivity : AppCompatActivity(), AddFragment.SubscribeListener, Notific
private lateinit var fab: FloatingActionButton private lateinit var fab: FloatingActionButton
// Other stuff // Other stuff
private var networkCallback: ConnectivityManager.NetworkCallback? = null
private var workManager: WorkManager? = null // Context-dependent private var workManager: WorkManager? = null // Context-dependent
private var dispatcher: NotificationDispatcher? = null // Context-dependent private var dispatcher: NotificationDispatcher? = null // Context-dependent
private var appBaseUrl: String? = null // Context-dependent private var appBaseUrl: String? = null // Context-dependent
@ -342,6 +350,19 @@ class MainActivity : AppCompatActivity(), AddFragment.SubscribeListener, Notific
} }
} }
// Network state banner
showHideNoNetworkBanner()
val connectivityManager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
runOnUiThread { showHideNoNetworkBanner() }
}
override fun onLost(network: Network) {
runOnUiThread { showHideNoNetworkBanner() }
}
}
connectivityManager.registerDefaultNetworkCallback(networkCallback!!)
// Hide links that lead to payments, see https://github.com/binwiederhier/ntfy/issues/1463 // Hide links that lead to payments, see https://github.com/binwiederhier/ntfy/issues/1463
val howToLink = findViewById<TextView>(R.id.main_how_to_link) val howToLink = findViewById<TextView>(R.id.main_how_to_link)
howToLink.isVisible = BuildConfig.PAYMENT_LINKS_AVAILABLE howToLink.isVisible = BuildConfig.PAYMENT_LINKS_AVAILABLE
@ -362,6 +383,9 @@ class MainActivity : AppCompatActivity(), AddFragment.SubscribeListener, Notific
// Permissions // Permissions
maybeRequestNotificationPermission() maybeRequestNotificationPermission()
// FIXME 2026-05-04: Remove this migration after 1 month
migrateSubscriptionIconsFromCache()
} }
private fun maybeRequestNotificationPermission() { private fun maybeRequestNotificationPermission() {
@ -377,6 +401,7 @@ class MainActivity : AppCompatActivity(), AddFragment.SubscribeListener, Notific
super.onResume() super.onResume()
showHideNotificationMenuItems() showHideNotificationMenuItems()
showHideConnectionErrorMenuItem(repository.getConnectionDetails()) showHideConnectionErrorMenuItem(repository.getConnectionDetails())
showHideNoNetworkBanner()
redrawList() redrawList()
} }
@ -425,6 +450,22 @@ class MainActivity : AppCompatActivity(), AddFragment.SubscribeListener, Notific
} }
} }
private fun showHideNoNetworkBanner() {
val banner = findViewById<View>(R.id.main_banner_no_network)
banner.visibility = if (isNetworkAvailable(this)) View.GONE else View.VISIBLE
}
override fun onDestroy() {
super.onDestroy()
try {
val connectivityManager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
networkCallback?.let { connectivityManager.unregisterNetworkCallback(it) }
} catch (e: Exception) {
Log.d(TAG, "Failed to unregister network callback: ${e.message}")
}
networkCallback = null
}
private fun schedulePeriodicPollWorker() { private fun schedulePeriodicPollWorker() {
val workerVersion = repository.getPollWorkerVersion() val workerVersion = repository.getPollWorkerVersion()
val workPolicy = if (workerVersion == PollWorker.VERSION) { val workPolicy = if (workerVersion == PollWorker.VERSION) {
@ -859,6 +900,38 @@ class MainActivity : AppCompatActivity(), AddFragment.SubscribeListener, Notific
adapter.notifyItemRangeChanged(0, adapter.currentList.size) adapter.notifyItemRangeChanged(0, adapter.currentList.size)
} }
// FIXME 2026-05-04: Remove this migration after 1 month
private fun migrateSubscriptionIconsFromCache() {
lifecycleScope.launch(Dispatchers.IO) {
delay(5_000) // 5 seconds
try {
val oldDir = File(cacheDir, SUBSCRIPTION_ICONS)
if (!oldDir.exists() || !oldDir.isDirectory) return@launch
val newDir = File(filesDir, SUBSCRIPTION_ICONS)
if (!newDir.exists()) newDir.mkdirs()
oldDir.listFiles()?.forEach { oldFile ->
val newFile = File(newDir, oldFile.name)
if (newFile.exists()) {
oldFile.delete()
return@forEach
}
if (oldFile.renameTo(newFile)) {
val subscriptionId = oldFile.name.toLongOrNull() ?: return@forEach
val newUri = FileProvider.getUriForFile(
this@MainActivity,
BuildConfig.APPLICATION_ID + ".provider",
newFile
)
repository.updateSubscriptionIcon(subscriptionId, newUri.toString())
}
}
oldDir.delete()
} catch (e: Exception) {
Log.w(TAG, "Failed to migrate subscription icons", e)
}
}
}
companion object { companion object {
const val TAG = "NtfyMainActivity" const val TAG = "NtfyMainActivity"
const val EXTRA_SUBSCRIPTION_ID = "subscriptionId" const val EXTRA_SUBSCRIPTION_ID = "subscriptionId"

View file

@ -10,9 +10,7 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM import android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM
import android.text.TextUtils
import android.view.View import android.view.View
import android.widget.Button
import android.widget.Toast import android.widget.Toast
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@ -43,7 +41,6 @@ import kotlinx.coroutines.launch
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit
/** /**
* Main settings * Main settings
@ -328,6 +325,31 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere
} }
} }
// Connection alert
val connectionAlertPrefId = context?.getString(R.string.settings_advanced_connection_alert_key) ?: return
val connectionAlert: ListPreference? = findPreference(connectionAlertPrefId)
connectionAlert?.value = repository.getConnectionAlertSeconds().toString()
connectionAlert?.preferenceDataStore = object : PreferenceDataStore() {
override fun putString(key: String?, value: String?) {
val seconds = value?.toLongOrNull() ?:return
repository.setConnectionAlertSeconds(seconds)
}
override fun getString(key: String?, defValue: String?): String {
return repository.getConnectionAlertSeconds().toString()
}
}
connectionAlert?.summaryProvider = Preference.SummaryProvider<ListPreference> { pref ->
when (pref.value.toLongOrNull() ?: repository.getConnectionAlertSeconds()) {
Repository.CONNECTION_ALERT_NEVER -> getString(R.string.settings_advanced_connection_alert_summary_never)
Repository.CONNECTION_ALERT_FIVE_MINUTES_SECONDS -> getString(R.string.settings_advanced_connection_alert_summary_five_minutes)
Repository.CONNECTION_ALERT_FIFTEEN_MINUTES_SECONDS -> getString(R.string.settings_advanced_connection_alert_summary_fifteen_minutes)
Repository.CONNECTION_ALERT_ONE_HOUR_SECONDS -> getString(R.string.settings_advanced_connection_alert_summary_one_hour)
Repository.CONNECTION_ALERT_THREE_HOURS_SECONDS -> getString(R.string.settings_advanced_connection_alert_summary_three_hours)
Repository.CONNECTION_ALERT_TWELVE_HOURS_SECONDS -> getString(R.string.settings_advanced_connection_alert_summary_twelve_hours)
else -> getString(R.string.settings_advanced_connection_alert_summary_never) // Must match default const
}
}
// Dark mode // Dark mode
val darkModePrefId = context?.getString(R.string.settings_general_dark_mode_key) ?: return val darkModePrefId = context?.getString(R.string.settings_general_dark_mode_key) ?: return
val darkMode: ListPreference? = findPreference(darkModePrefId) val darkMode: ListPreference? = findPreference(darkModePrefId)

View file

@ -1,6 +1,7 @@
package io.heckel.ntfy.util package io.heckel.ntfy.util
const val ANDROID_APP_MIME_TYPE = "application/vnd.android.package-archive" const val ANDROID_APP_MIME_TYPE = "application/vnd.android.package-archive"
const val SUBSCRIPTION_ICONS = "subscriptionIcons"
const val PRIORITY_MIN = 1 const val PRIORITY_MIN = 1
const val PRIORITY_LOW = 2 const val PRIORITY_LOW = 2

View file

@ -10,6 +10,7 @@ import android.content.res.Resources
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.drawable.RippleDrawable import android.graphics.drawable.RippleDrawable
import android.net.ConnectivityManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.PowerManager import android.os.PowerManager
@ -546,3 +547,10 @@ fun deriveNotificationId(baseUrl: String, topic: String, sequenceId: String): In
val hash = composite.hashCode() val hash = composite.hashCode()
return if (hash == 0 || hash == Int.MIN_VALUE) 1 else abs(hash) return if (hash == 0 || hash == Int.MIN_VALUE) 1 else abs(hash)
} }
// We check for any active network, not specifically for internet connectivity,
// because the ntfy server may be on a LAN without internet access.
fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return connectivityManager.activeNetwork != null
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M22.99,9C19.15,5.16 13.8,3.76 8.84,4.78l2.52,2.52c3.47,-0.17 6.99,1.05 9.63,3.7l2,-2zM18.99,13c-1.29,-1.29 -2.84,-2.13 -4.49,-2.56l3.53,3.53 0.96,-0.97zM2,3.05L5.07,6.1C3.6,6.82 2.22,7.78 1,9l2,2c1.02,-1.02 2.17,-1.78 3.38,-2.31L9.03,11.34c-1.14,0.4 -2.2,1.05 -3.04,1.95l2,2c0.73,-0.73 1.63,-1.19 2.58,-1.4l3.32,3.32L12.01,21l2,-2 -0.76,-0.76 4.78,4.78 1.41,-1.41L3.41,1.64 2,3.05z"
android:fillColor="#757575"/>
</vector>

View file

@ -232,6 +232,28 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<FrameLayout
android:id="@+id/main_banner_no_network"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorSurfaceVariant"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/main_banner_websocket_reconnect">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/main_banner_no_network_text"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:textColor="?attr/colorOnSurfaceVariant"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:drawableStart="@drawable/ic_wifi_off_gray_24dp"
android:drawablePadding="4dp" />
</FrameLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/main_subscriptions_list_container" android:id="@+id/main_subscriptions_list_container"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -239,7 +261,7 @@
android:visibility="visible" android:visibility="visible"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/main_banner_websocket_reconnect"> app:layout_constraintTop_toBottomOf="@id/main_banner_no_network">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/main_subscriptions_list" android:id="@+id/main_subscriptions_list"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -60,7 +60,7 @@
android:id="@+id/detail_item_message_text" android:id="@+id/detail_item_message_text"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:autoLink="web|phone|email" android:autoLink="web"
app:layout_constraintTop_toBottomOf="@id/detail_item_title_text" app:layout_constraintTop_toBottomOf="@id/detail_item_title_text"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="12dp" app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="12dp"
app:layout_constraintBottom_toTopOf="@id/detail_item_attachment_image" app:layout_constraintEnd_toStartOf="@id/detail_item_icon" android:layout_marginEnd="6dp"/> app:layout_constraintBottom_toTopOf="@id/detail_item_attachment_image" app:layout_constraintEnd_toStartOf="@id/detail_item_icon" android:layout_marginEnd="6dp"/>

View file

@ -471,4 +471,9 @@
<string name="detail_menu_search_hint">Търсене в известията</string> <string name="detail_menu_search_hint">Търсене в известията</string>
<string name="common_button_reset_to_default">Подразбирани настройки</string> <string name="common_button_reset_to_default">Подразбирани настройки</string>
<string name="default_server_dialog_url_error_invalid">Въведете адрес на услуга, напр. https://ntfy.example.com</string> <string name="default_server_dialog_url_error_invalid">Въведете адрес на услуга, напр. https://ntfy.example.com</string>
<string name="connection_alert_title">Връзката е загубена</string>
<string name="connection_alert_text_one">От най-малко %2$d минути няма връзка с %1$s</string>
<string name="connection_alert_text_multiple">От най-малко %2$d минути няма връзка със сървърите на %1$d</string>
<string name="connection_alert_action_snooze">Отлагане с %1$dч</string>
<string name="connection_alert_action_never">Да не се показва повече</string>
</resources> </resources>

View file

@ -103,7 +103,7 @@
<string name="add_dialog_base_urls_dropdown_clear">Esborra URL de servei</string> <string name="add_dialog_base_urls_dropdown_clear">Esborra URL de servei</string>
<string name="detail_no_notifications_text">Encara no has rebut cap notificació en aquest tema.</string> <string name="detail_no_notifications_text">Encara no has rebut cap notificació en aquest tema.</string>
<string name="detail_how_to_intro">Per enviar notificacions en aquest tema, fes PUT o POST a la URL del tema.</string> <string name="detail_how_to_intro">Per enviar notificacions en aquest tema, fes PUT o POST a la URL del tema.</string>
<string name="detail_how_to_example">Exemple (usant curl):<br></br><tt>$ curl -d \"Hola\" %1$s</tt></string> <string name="detail_how_to_example">Exemple (usant curl):<br/><tt>$ curl -d \"Hola\" %1$s</tt></string>
<string name="main_unified_push_toast">Aquesta subscripció és gestionada per %1$s via UnifiedPush</string> <string name="main_unified_push_toast">Aquesta subscripció és gestionada per %1$s via UnifiedPush</string>
<string name="main_how_to_link">Instruccions detallades disponibles a ntfy.sh i a la documentació.</string> <string name="main_how_to_link">Instruccions detallades disponibles a ntfy.sh i a la documentació.</string>
<string name="main_banner_websocket_text">WebSockets és el mètode recomanat per connectar-te al teu servidor. Pot millorar el rendiment de la bateria, però pot requerir <a href="https://ntfy.sh/docs/config/#nginxapache2caddy">configuració addicional al teu proxy</a>. Aquesta opció pot ser habil·litada a Configuració.</string> <string name="main_banner_websocket_text">WebSockets és el mètode recomanat per connectar-te al teu servidor. Pot millorar el rendiment de la bateria, però pot requerir <a href="https://ntfy.sh/docs/config/#nginxapache2caddy">configuració addicional al teu proxy</a>. Aquesta opció pot ser habil·litada a Configuració.</string>

View file

@ -4,7 +4,7 @@
<string name="common_priority_default_name">Standard-Priorität</string> <string name="common_priority_default_name">Standard-Priorität</string>
<string name="common_priority_high_name">Hohe Priorität</string> <string name="common_priority_high_name">Hohe Priorität</string>
<string name="common_priority_max_name">Höchste Priorität</string> <string name="common_priority_max_name">Höchste Priorität</string>
<string name="channel_subscriber_service_name">Abo Service</string> <string name="channel_subscriber_service_name">Hintergrunddienst</string>
<string name="common_priority_min_name">Niedrigste Priorität</string> <string name="common_priority_min_name">Niedrigste Priorität</string>
<string name="channel_subscriber_notification_title">Warte auf eingehende Benachrichtigungen</string> <string name="channel_subscriber_notification_title">Warte auf eingehende Benachrichtigungen</string>
<string name="channel_subscriber_notification_instant_text_one">Ein Sofortnachrichten-Thema abonniert</string> <string name="channel_subscriber_notification_instant_text_one">Ein Sofortnachrichten-Thema abonniert</string>
@ -471,4 +471,24 @@
<string name="detail_menu_search_hint">In Benachrichtigungen suchen</string> <string name="detail_menu_search_hint">In Benachrichtigungen suchen</string>
<string name="default_server_dialog_url_error_invalid">Eine gültige Service-URL eingeben, z. B. https://ntfy.example.com</string> <string name="default_server_dialog_url_error_invalid">Eine gültige Service-URL eingeben, z. B. https://ntfy.example.com</string>
<string name="detail_menu_search">Benachrichtigungen suchen</string> <string name="detail_menu_search">Benachrichtigungen suchen</string>
<string name="connection_alert_title">Verbindung verloren</string>
<string name="connection_alert_text_one">Es konnte länger als %2$d Minuten lang keine Verbindung zu %1$s hergestellt werden. Prüfe deine Netzwerkverbindung.</string>
<string name="connection_alert_text_multiple">Es konnte länger als %2$d Minuten lang keine Verbindung zu %1$d Servern hergestellt werden. Prüfe deine Netzwerkverbindung.</string>
<string name="connection_alert_action_snooze">%1$d Std. schlummern</string>
<string name="connection_alert_action_never">Niemals zeigen</string>
<string name="channel_connection_alert_name">Verbindungswarnungen</string>
<string name="main_banner_no_network_text">Du bist offline</string>
<string name="settings_advanced_connection_alert_title">Warnung, wenn die Verbindung getrennt wird</string>
<string name="settings_advanced_connection_alert_summary_never">Niemals benachrichtigen, wenn der ntfy-Server nicht erreichbar ist</string>
<string name="settings_advanced_connection_alert_summary_five_minutes">Benachrichtigen, wenn der ntfy-Server länger als 5 Minuten nicht erreichbar ist</string>
<string name="settings_advanced_connection_alert_summary_fifteen_minutes">Benachrichtigen, wenn der ntfy-Server länger als 15 Minuten nicht erreichbar ist</string>
<string name="settings_advanced_connection_alert_summary_one_hour">Benachrichtige, wenn der ntfy-Server länger als 1 Stunde nicht erreichbar ist</string>
<string name="settings_advanced_connection_alert_summary_three_hours">Benachrichtigen, wenn der ntfy-Server länger als 3 Stunden nicht erreichbar ist</string>
<string name="settings_advanced_connection_alert_summary_twelve_hours">Benachrichtigen, wenn der ntfy-Server länger als 12 Stunden nicht erreichbar ist</string>
<string name="settings_advanced_connection_alert_never">Niemals</string>
<string name="settings_advanced_connection_alert_five_minutes">Nach 5 Minuten</string>
<string name="settings_advanced_connection_alert_fifteen_minutes">Nach 15 Minuten</string>
<string name="settings_advanced_connection_alert_one_hour">Nach 1 Stunde</string>
<string name="settings_advanced_connection_alert_three_hours">Nach 3 Stunden</string>
<string name="settings_advanced_connection_alert_twelve_hours">Nach 12 Stunden</string>
</resources> </resources>

View file

@ -141,8 +141,8 @@
<string name="settings_notifications_auto_download_summary_smaller_than_x">Descargar automáticamente archivos adjuntos de hasta %1$s</string> <string name="settings_notifications_auto_download_summary_smaller_than_x">Descargar automáticamente archivos adjuntos de hasta %1$s</string>
<string name="settings_notifications_auto_download_never">Nunca descargar nada automáticamente</string> <string name="settings_notifications_auto_download_never">Nunca descargar nada automáticamente</string>
<string name="settings_notifications_auto_download_always">Siempre descargar todo automáticamente</string> <string name="settings_notifications_auto_download_always">Siempre descargar todo automáticamente</string>
<string name="settings_notifications_auto_download_100k">Si es más pequeño que 100 KB</string> <string name="settings_notifications_auto_download_100k">Si es inferior a 100 kB</string>
<string name="settings_notifications_auto_download_500k">Si es más pequeño que 500 KB</string> <string name="settings_notifications_auto_download_500k">Si es inferior a 500 kB</string>
<string name="settings_notifications_auto_download_5m">Si es más pequeño que 5 MB</string> <string name="settings_notifications_auto_download_5m">Si es más pequeño que 5 MB</string>
<string name="settings_notifications_auto_delete_title">Eliminar notificaciones</string> <string name="settings_notifications_auto_delete_title">Eliminar notificaciones</string>
<string name="settings_notifications_auto_delete_summary_never">Nunca eliminar automáticamente las notificaciones</string> <string name="settings_notifications_auto_delete_summary_never">Nunca eliminar automáticamente las notificaciones</string>
@ -342,11 +342,11 @@
<string name="main_banner_websocket_reconnect_button_dismiss">Descartar</string> <string name="main_banner_websocket_reconnect_button_dismiss">Descartar</string>
<string name="main_banner_websocket_reconnect_button_enable_now">Conceder ahora</string> <string name="main_banner_websocket_reconnect_button_enable_now">Conceder ahora</string>
<string name="settings_advanced_exact_alarms_title">Alarmas exactas</string> <string name="settings_advanced_exact_alarms_title">Alarmas exactas</string>
<string name="settings_advanced_exact_alarms_true">ntfy puede programar alarmas exactas (utilizadas para reconectar WebSockets en segundo plano)</string> <string name="settings_advanced_exact_alarms_true">ntfy puede programar alarmas exactas. Las alarmas exactas son requeridas para reconectar WebSockets en segundo plano. Haga clic para revocar este permiso.</string>
<string name="settings_advanced_exact_alarms_false">ntfy no puede programar alarmas exactas. Haga clic para conceder el permiso ahora</string> <string name="settings_advanced_exact_alarms_false">ntfy no puede programar alarmas exactas. Las alarmas exactas son requeridas para reconectar WebSockets en segundo plano. Haga clic para conceder el permiso.</string>
<string name="publish_dialog_title">Publicar a %1$s</string> <string name="publish_dialog_title">Publicar a %1$s</string>
<string name="publish_dialog_title_hint">Título</string> <string name="publish_dialog_title_hint">Título</string>
<string name="publish_dialog_title_placeholder">p.e. Alguien está en la puerta</string> <string name="publish_dialog_title_placeholder">ej. Alguien está en la puerta</string>
<string name="publish_dialog_message_hint">Mensaje</string> <string name="publish_dialog_message_hint">Mensaje</string>
<string name="publish_dialog_tags_hint">Etiquetas</string> <string name="publish_dialog_tags_hint">Etiquetas</string>
<string name="publish_dialog_priority_hint">Prioridad</string> <string name="publish_dialog_priority_hint">Prioridad</string>
@ -407,6 +407,73 @@
<string name="settings_advanced_certificates_summary">Añadir certificados a la lista de confiados y administrar certificados de cliente para mTLS</string> <string name="settings_advanced_certificates_summary">Añadir certificados a la lista de confiados y administrar certificados de cliente para mTLS</string>
<string name="settings_advanced_certificates_trusted_header">Certificados confiados</string> <string name="settings_advanced_certificates_trusted_header">Certificados confiados</string>
<string name="common_service_url_placeholder">ej. https://ntfy.example.com</string> <string name="common_service_url_placeholder">ej. https://ntfy.example.com</string>
<string name="common_certificate_subject">Subject</string> <string name="common_certificate_subject">Asunto</string>
<string name="main_menu_connection_error">Error de conexión</string> <string name="main_menu_connection_error">Error de conexión</string>
<string name="common_button_reset_to_default">Restaurar valores por defecto</string>
<string name="connection_alert_title">Conexión perdida</string>
<string name="connection_alert_text_one">No se pudo conectar a %1$s por más de %2$d minutos</string>
<string name="connection_alert_text_multiple">No se pudo conectar a %1$d servidores por más de %2$d minutos</string>
<string name="connection_alert_action_snooze">Suspender %1$dh</string>
<string name="connection_alert_action_never">Nunca mostrar</string>
<string name="detail_no_search_results">Su búsqueda no tuvo ningún resultado</string>
<string name="detail_menu_search">Buscar notificaciones</string>
<string name="detail_menu_search_hint">Buscar en notificaciones</string>
<string name="publish_dialog_tags_placeholder">ej. alerta, peligro</string>
<string name="publish_dialog_chip_email">Email</string>
<string name="publish_dialog_chip_markdown">Markdown</string>
<string name="publish_dialog_email_hint">Email</string>
<string name="publish_dialog_email_placeholder">ej. phil@example.com</string>
<string name="publish_dialog_delay_placeholder">ej. 30m, 1h, today 9pm (Siempre en inglés)</string>
<string name="publish_dialog_attach_url_placeholder">ej. https://example.com/flowers.jpg</string>
<string name="publish_dialog_attach_filename_placeholder">ej. ilies.jpg</string>
<string name="publish_dialog_phone_call_placeholder">ej. +1234567890</string>
<string name="connection_error_dialog_title">Error de conexión</string>
<string name="connection_error_dialog_message">Hubo un problema al conectar a %1$s. La aplicación continuará intentando reconectar en segundo plano.</string>
<string name="connection_error_dialog_connection_refused">Conexión rechazada. Puede que el servidor esté desconectado o la dirección sea incorrecta.</string>
<string name="connection_error_dialog_websocket_not_supported">WebSocket no soportado. Puede que el servidor no soporte conexiones WebSocket, o que la dirección sea incorrecta.</string>
<string name="connection_error_dialog_not_authorized">No autorizado. El servidor devolvió una respuesta HTTP 401/403. Por favor, compruebe su usuario y contraseña.</string>
<string name="connection_error_dialog_retry_now">Reintentar ahora</string>
<string name="connection_error_dialog_retry_countdown">Reintentando en %1$ds…</string>
<string name="connection_error_dialog_retrying">Reintentando…</string>
<string name="settings_advanced_certificates_trusted_item_summary">Certificado fijado, emitido por %1$s, expira %2$s, usado para conexiones a %3$s</string>
<string name="settings_advanced_certificates_trusted_item_summary_expired">Certificado fijado, emitido por %1$s, expirado, usado para conexiones a %2$s</string>
<string name="settings_advanced_certificates_trusted_add_title">Añadir un certificado de confianza</string>
<string name="settings_advanced_certificates_trusted_add_summary">Importa un certificado en el almacén de confianza (PEM). Al conectarse al servidor ntfy, se confiará en este certificado.</string>
<string name="settings_advanced_certificates_client_header">Certificados de cliente (mTLS)</string>
<string name="settings_advanced_certificates_client_item_summary">Certificado de cliente, emitido por %1$s, expira %2$s, usado para conectar a %3$s</string>
<string name="settings_advanced_certificates_client_item_summary_expired">Certificado de cliente, emitido por %1$s, expirado, usado para conectar a %2$s</string>
<string name="settings_advanced_certificates_client_add_title">Añadir un certificado de cliente</string>
<string name="settings_advanced_certificates_client_add_summary">Importar certificado para autenticación TLS mutua (PKCS#12). Este certificado se utilizará al conectarse al servidor.</string>
<string name="settings_advanced_certificates_error_invalid_cert">Archivo de certificado no válido</string>
<string name="settings_advanced_certificates_error_invalid_p12">Archivo PKCS#12 inválido</string>
<string name="user_dialog_base_url_error_authorization_header_exists">No se puede añadir el usuario si se ha configurado una cabecera Authorization personalizada para este servidor</string>
<string name="default_server_dialog_url_error_invalid">Introduce una URL del servicio válida, ej. (https://ntfy.example.com)</string>
<string name="custom_headers_dialog_title_add">Añadir encabezado personalizado</string>
<string name="custom_headers_dialog_title_edit">Editar encabezado personalizado</string>
<string name="custom_headers_dialog_name_hint">Nombre de la cabecera (ej. CF-Access-Client-Id)</string>
<string name="custom_headers_dialog_value_hint">Valor de la cabecera (ej. 9f3c2e4a1b2d4e)</string>
<string name="custom_headers_dialog_description_add">Añade una cabecera HTTP personalizada que se enviará con cada solicitud al servidor especificado.</string>
<string name="custom_headers_dialog_description_edit">Puedes editar el nombre o el valor de la cabecera seleccionada, o eliminarla.</string>
<string name="custom_headers_dialog_error_invalid_name">El nombre de la cabecera contiene caracteres no válidos</string>
<string name="trusted_certificate_dialog_title">Detalles del certificado</string>
<string name="trusted_certificate_dialog_title_unknown">Advertencia de seguridad</string>
<string name="trusted_certificate_dialog_title_add">Añadir certificado de confianza</string>
<string name="trusted_certificate_dialog_security_title">Tu conexión no es privada</string>
<string name="trusted_certificate_dialog_security_description">El certificado del servidor no es de confianza. Es posible que atacantes estén intentando robar tu información. No continúes a menos que sepas por qué este certificado no es de confianza.</string>
<string name="trusted_certificate_dialog_description_add">Has seleccionado un archivo de certificado. Revisa los detalles a continuación antes de añadirlo a tus certificados de confianza.</string>
<string name="trusted_certificate_dialog_description_view">Este certificado se utiliza para las conexiones a la URL del servicio indicada abajo. Has añadido esta excepción manualmente.</string>
<string name="trusted_certificate_dialog_description_page1">Introduce la URL del servicio a la que debe fijarse este certificado. El certificado solo será de confianza para esta URL.</string>
<string name="trusted_certificate_dialog_expired_warning">Advertencia: Este certificado ha caducado.</string>
<string name="trusted_certificate_dialog_not_yet_valid_warning">Advertencia: Este certificado aún no es válido.</string>
<string name="trusted_certificate_dialog_error_invalid_url">URL no válida</string>
<string name="trusted_certificate_dialog_error_parse">No se puede cargar el certificado: %1$s</string>
<string name="trusted_certificate_dialog_button_trust">Confiar</string>
<string name="client_certificate_dialog_title">Certificado de cliente</string>
<string name="client_certificate_dialog_title_add">Añadir certificado de cliente</string>
<string name="client_certificate_dialog_description_page1">Introduce la URL del servicio para la que debe usarse este certificado y la contraseña del archivo PKCS#12.</string>
<string name="client_certificate_dialog_description_page2">Revisa los detalles del certificado y guarda para añadir este certificado de cliente. Este certificado se utilizará para autenticarse con el servidor.</string>
<string name="client_certificate_dialog_password_hint">Contraseña</string>
<string name="client_certificate_dialog_error_wrong_password">Contraseña incorrecta o archivo PKCS#12 no válido</string>
<string name="client_certificate_dialog_error_invalid_p12_password">Contraseña no válida o archivo PKCS#12 dañado</string>
<string name="client_certificate_dialog_error_invalid_url">URL del servicio no válida</string>
</resources> </resources>

View file

@ -461,4 +461,9 @@
<string name="detail_menu_search_hint">Otsi teavitustest</string> <string name="detail_menu_search_hint">Otsi teavitustest</string>
<string name="common_button_reset_to_default">Taasta algväärtused</string> <string name="common_button_reset_to_default">Taasta algväärtused</string>
<string name="default_server_dialog_url_error_invalid">Sisesta korrektne teenuse võrguaadress, nt https://ntfy.toredomeen.com</string> <string name="default_server_dialog_url_error_invalid">Sisesta korrektne teenuse võrguaadress, nt https://ntfy.toredomeen.com</string>
<string name="connection_alert_title">Ühendus on katkenud</string>
<string name="connection_alert_text_one">Ühendus %1$s teenusega toimib vaid %2$d minuti(t)</string>
<string name="connection_alert_text_multiple">Ühendus %1$d serveriga toimib vaid %2$d minuti(t)</string>
<string name="connection_alert_action_snooze">Tukasta %1$dt</string>
<string name="connection_alert_action_never">Ära näita iialgi</string>
</resources> </resources>

View file

@ -347,10 +347,10 @@
<string name="settings_general_dynamic_colors_title">Couleurs dynamiques</string> <string name="settings_general_dynamic_colors_title">Couleurs dynamiques</string>
<string name="settings_general_dynamic_colors_summary_disabled">Utiliser les couleurs de thème ntfy</string> <string name="settings_general_dynamic_colors_summary_disabled">Utiliser les couleurs de thème ntfy</string>
<string name="settings_general_dynamic_colors_summary_enabled">Utiliser les couleurs dynamiques du système</string> <string name="settings_general_dynamic_colors_summary_enabled">Utiliser les couleurs dynamiques du système</string>
<string name="publish_dialog_title_placeholder">e.g. Quelqu\'un est à la porte</string> <string name="publish_dialog_title_placeholder">par ex. Quelqu\'un est à la porte</string>
<string name="publish_dialog_message_hint">Message</string> <string name="publish_dialog_message_hint">Message</string>
<string name="publish_dialog_tags_hint">Étiquettes</string> <string name="publish_dialog_tags_hint">Étiquettes</string>
<string name="publish_dialog_tags_placeholder">e.g. warning, skull</string> <string name="publish_dialog_tags_placeholder">par ex. attention, crâne</string>
<string name="publish_dialog_priority_hint">Priorité</string> <string name="publish_dialog_priority_hint">Priorité</string>
<string name="publish_dialog_button_publish">Publier</string> <string name="publish_dialog_button_publish">Publier</string>
<string name="publish_dialog_error_sending">Impossible de publier le message : %1$s</string> <string name="publish_dialog_error_sending">Impossible de publier le message : %1$s</string>
@ -390,7 +390,7 @@
<string name="settings_general_message_bar_title">Afficher la barre de message</string> <string name="settings_general_message_bar_title">Afficher la barre de message</string>
<string name="settings_general_message_bar_summary_enabled">Barre de message affichée en bas du sujet</string> <string name="settings_general_message_bar_summary_enabled">Barre de message affichée en bas du sujet</string>
<string name="settings_general_message_bar_summary_disabled">Bouton Publier affiché en bas du sujet</string> <string name="settings_general_message_bar_summary_disabled">Bouton Publier affiché en bas du sujet</string>
<string name="settings_advanced_custom_headers_title">En-têtes HTTP personnalisés</string> <string name="settings_advanced_custom_headers_title">En-têtes personnalisés</string>
<string name="settings_advanced_custom_headers_summary">Définissez des en-têtes HTTP personnalisés qui seront envoyés avec chaque demande, par exemple, si votre serveur ntfy se trouve derrière un proxy ou un tunnel authentifié.</string> <string name="settings_advanced_custom_headers_summary">Définissez des en-têtes HTTP personnalisés qui seront envoyés avec chaque demande, par exemple, si votre serveur ntfy se trouve derrière un proxy ou un tunnel authentifié.</string>
<string name="settings_advanced_custom_headers_prefs_header_add">Ajouter un en-tête</string> <string name="settings_advanced_custom_headers_prefs_header_add">Ajouter un en-tête</string>
<string name="settings_advanced_custom_headers_prefs_header_add_title">Ajouter un en-tête pour un serveur</string> <string name="settings_advanced_custom_headers_prefs_header_add_title">Ajouter un en-tête pour un serveur</string>
@ -471,4 +471,9 @@
<string name="detail_menu_search_hint">Rechercher dans les notifications</string> <string name="detail_menu_search_hint">Rechercher dans les notifications</string>
<string name="common_button_reset_to_default">Réinitialiser par défaut</string> <string name="common_button_reset_to_default">Réinitialiser par défaut</string>
<string name="default_server_dialog_url_error_invalid">Entrez une URL de service valide, par ex. https://ntfy.example.com</string> <string name="default_server_dialog_url_error_invalid">Entrez une URL de service valide, par ex. https://ntfy.example.com</string>
<string name="connection_alert_title">Connexion perdue</string>
<string name="connection_alert_text_one">Impossible de se connecter à %1$s depuis plus de %2$d minutes</string>
<string name="connection_alert_text_multiple">Impossible de se connecter à %1$d serveurs depuis plus de %2$d minutes</string>
<string name="connection_alert_action_snooze">Sourdine %1$dh</string>
<string name="connection_alert_action_never">Ne jamais montrer</string>
</resources> </resources>

View file

@ -85,7 +85,7 @@
<string name="detail_how_to_link">विस्तृत निर्देश ntfy.sh पर और डॉक्स में उपलब्ध हैं।</string> <string name="detail_how_to_link">विस्तृत निर्देश ntfy.sh पर और डॉक्स में उपलब्ध हैं।</string>
<string name="detail_clear_dialog_permanently_delete">स्थायी रूप से हटाएं</string> <string name="detail_clear_dialog_permanently_delete">स्थायी रूप से हटाएं</string>
<string name="detail_how_to_intro">इस विषय पर सूचनाएं भेजने के लिए, बस विषय URL पर PUT या POST करें।</string> <string name="detail_how_to_intro">इस विषय पर सूचनाएं भेजने के लिए, बस विषय URL पर PUT या POST करें।</string>
<string name="detail_how_to_example">उदाहरण (curl का उपयोग करके):<br></br><tt>$curl -d \"नमस्कार\"%1$s</tt></string> <string name="detail_how_to_example">उदाहरण (curl का उपयोग करके):<br/><tt>$curl -d \"नमस्कार\"%1$s</tt></string>
<string name="detail_clear_dialog_message">इस विषय की सभी सूचनाएँ हटाएँ?</string> <string name="detail_clear_dialog_message">इस विषय की सभी सूचनाएँ हटाएँ?</string>
<string name="main_banner_websocket_text">WebSockets पर स्विच करना आपके सर्वर से कनेक्ट करने का अनुशंसित तरीका है, और बैटरी जीवन में सुधार कर सकता है, लेकिन आपके <a href="https://ntfy.sh/docs/config/#nginxapache2caddy">proxy में अतिरिक्त कॉन्फ़िगरेशन</a> की आवश्यकता हो सकती है। इसे सेटिंग्स में टॉगल किया जा सकता है।</string> <string name="main_banner_websocket_text">WebSockets पर स्विच करना आपके सर्वर से कनेक्ट करने का अनुशंसित तरीका है, और बैटरी जीवन में सुधार कर सकता है, लेकिन आपके <a href="https://ntfy.sh/docs/config/#nginxapache2caddy">proxy में अतिरिक्त कॉन्फ़िगरेशन</a> की आवश्यकता हो सकती है। इसे सेटिंग्स में टॉगल किया जा सकता है।</string>
<string name="detail_test_message_error_unauthorized_anon">संदेश नहीं भेजा जा सकता: अनाम प्रकाशन की अनुमति नहीं है।</string> <string name="detail_test_message_error_unauthorized_anon">संदेश नहीं भेजा जा सकता: अनाम प्रकाशन की अनुमति नहीं है।</string>

View file

@ -471,4 +471,9 @@
<string name="detail_menu_search_hint">通知内容を検索します</string> <string name="detail_menu_search_hint">通知内容を検索します</string>
<string name="common_button_reset_to_default">既定にリセット</string> <string name="common_button_reset_to_default">既定にリセット</string>
<string name="default_server_dialog_url_error_invalid">有効なサービスURLを入力してください、例: https://ntfy.example.com</string> <string name="default_server_dialog_url_error_invalid">有効なサービスURLを入力してください、例: https://ntfy.example.com</string>
<string name="connection_alert_title">接続が切断されました</string>
<string name="connection_alert_text_one">%2$d 分以上 %1$s に接続できませんでした</string>
<string name="connection_alert_text_multiple">%1$d 個のサーバーに %2$d 分以上接続できませんでした</string>
<string name="connection_alert_action_snooze">%1$d時間後に再通知</string>
<string name="connection_alert_action_never">再表示しない</string>
</resources> </resources>

View file

@ -61,7 +61,7 @@
<string name="add_dialog_instant_delivery_description">Forsikrer at meldinger blir levert umiddelbart, selv når enheten er inaktiv.</string> <string name="add_dialog_instant_delivery_description">Forsikrer at meldinger blir levert umiddelbart, selv når enheten er inaktiv.</string>
<string name="add_dialog_login_description">Dette emnet krever at du logger inn. Skriv inn et brukernavn og passord.</string> <string name="add_dialog_login_description">Dette emnet krever at du logger inn. Skriv inn et brukernavn og passord.</string>
<string name="detail_how_to_intro">For å sende merknader til dette emnet kan du utføre PUT eller POST til emne-nettadressen.</string> <string name="detail_how_to_intro">For å sende merknader til dette emnet kan du utføre PUT eller POST til emne-nettadressen.</string>
<string name="detail_how_to_example">Eksempel (ved bruk av curl):<br></br><tt>$ curl -d \"Hei\" %1$s</tt></string> <string name="detail_how_to_example">Eksempel (ved bruk av curl):<br/><tt>$ curl -d \"Hei\" %1$s</tt></string>
<string name="detail_item_snack_deleted">Merknad slettet</string> <string name="detail_item_snack_deleted">Merknad slettet</string>
<string name="detail_item_menu_cancel">Avbryt nedlasting</string> <string name="detail_item_menu_cancel">Avbryt nedlasting</string>
<string name="detail_item_menu_save_file">Lagre fil</string> <string name="detail_item_menu_save_file">Lagre fil</string>

View file

@ -364,4 +364,64 @@
<string name="publish_dialog_error_sending">Fout bij publiceren: %1$s</string> <string name="publish_dialog_error_sending">Fout bij publiceren: %1$s</string>
<string name="publish_dialog_error_server">Kon bericht niet publiceren: %1$s (code %2$d)</string> <string name="publish_dialog_error_server">Kon bericht niet publiceren: %1$s (code %2$d)</string>
<string name="publish_dialog_message_published">Bericht gepubliceerd</string> <string name="publish_dialog_message_published">Bericht gepubliceerd</string>
<string name="common_button_reset_to_default">Terug naar standaardinstellingen</string>
<string name="common_certificate_valid_from">Geldig vanaf</string>
<string name="common_certificate_issuer">Uitgever</string>
<string name="common_certificate_fingerprint">SHA-256 vingerafdruk</string>
<string name="common_certificate_subject">Onderwerp</string>
<string name="client_certificate_dialog_error_invalid_url">Onjuiste server URL</string>
<string name="client_certificate_dialog_description_page1">Vul de server URL in waar dit certificaat voor moet worden gebruikt, en het wachtwoord voor het PKCS#12 bestand.</string>
<string name="client_certificate_dialog_description_page2">Beoordeel de certificaat instellingen en sla op om het certificaat toe te voegen. Dit certificaat zal worden gebruikt om te authenticeren met de server.</string>
<string name="client_certificate_dialog_password_hint">Wachtwoord</string>
<string name="client_certificate_dialog_error_wrong_password">Onjuist wachtwoord of onjuist PKCS#12 bestand</string>
<string name="client_certificate_dialog_error_invalid_p12_password">Onjuist wachtwoord of beschadigd PKCS#12 bestand</string>
<string name="connection_alert_title">Verbinding verbroken</string>
<string name="connection_alert_text_one">Kan geen verbinding maken met %1$s voor meer dan %2$d minuten</string>
<string name="connection_alert_text_multiple">Kan geen verbinding maken met %1$d servers voor meer dan %2$d minuten</string>
<string name="connection_alert_action_snooze">Sluimer voor %1$d uur</string>
<string name="connection_alert_action_never">Nooit laten zien</string>
<string name="publish_dialog_title">Publieer naar %1$s</string>
<string name="publish_dialog_uploading">Uploaden: %1$s (%2$s / %3$s)</string>
<string name="publish_dialog_upload_cancelled">Upload geannuleerd</string>
<string name="publish_dialog_chip_title">Titel</string>
<string name="publish_dialog_chip_tags">Labels</string>
<string name="publish_dialog_chip_priority">Prioriteit</string>
<string name="publish_dialog_chip_click_url">Klik URL</string>
<string name="publish_dialog_chip_email">Email</string>
<string name="publish_dialog_chip_delay">Vertraging</string>
<string name="publish_dialog_chip_markdown">Markdown</string>
<string name="publish_dialog_chip_attach_url">Insluiten via URL</string>
<string name="publish_dialog_chip_attach_file">Bestand insluiten</string>
<string name="publish_dialog_chip_phone_call">Telefoongesprek</string>
<string name="publish_dialog_click_url_hint">Klik URL</string>
<string name="publish_dialog_click_url_placeholder">bijvoorbeeld https://example.com/alerts/1234</string>
<string name="publish_dialog_email_hint">Email</string>
<string name="publish_dialog_email_placeholder">bijvoorbeeld jan@jansen.nl</string>
<string name="publish_dialog_delay_hint">Vertraging bezorging</string>
<string name="publish_dialog_attach_url_hint">Attachment URL</string>
<string name="publish_dialog_attach_url_placeholder">bijvoorbeeld https://example.com/flowers.jpg</string>
<string name="publish_dialog_attach_filename_hint">Attachment bestandsnaam</string>
<string name="publish_dialog_attach_filename_placeholder">bijvoorbeeld lilies.jpg</string>
<string name="publish_dialog_phone_call_hint">Telefoongesprek</string>
<string name="publish_dialog_phone_call_placeholder">bijvoorbeel +1234567890</string>
<string name="publish_dialog_docs_text">Bekijk de <a href="https://docs.ntfy.sh/publish/">documentatie</a> voor voorbeelden en een uitgebreide beschrijving van alle publiceer-functies.</string>
<string name="message_bar_hint">Berichtenbalk</string>
<string name="message_bar_publish_button_description">Publiceer bericht</string>
<string name="message_bar_expand_button_description">Meer opties</string>
<string name="connection_error_dialog_title">Fout bij verbinden</string>
<string name="connection_error_dialog_message">Er was een probleem bij het verbinden met %1$s. De app zal blijven proberen in de achtergrond.</string>
<string name="connection_error_dialog_connection_refused">Verbinding geweigerd. De server is mogelijk niet bereikbaar of het adres is onjuist.</string>
<string name="connection_error_dialog_websocket_not_supported">WebSocket niet ondersteund. De server ondersteund mogelijk geen WebSochet, of het adres is onjuist.</string>
<string name="connection_error_dialog_not_authorized">Niet toegestaan. De server gaf een HTTP 401/403 antwoord. Controleer uw gebruikersnaam en/of wachtwoord.</string>
<string name="connection_error_dialog_retry_now">Probeer opnieuw</string>
<string name="connection_error_dialog_retry_countdown">Nieuwe poging in %1$d…</string>
<string name="connection_error_dialog_retrying">Opnieuw proberen…</string>
<string name="settings_general_language_title">Taal</string>
<string name="settings_general_language_summary_system">Systeemstandaarden worden gebruikt</string>
<string name="settings_general_language_system_default">Systeemstandaarden</string>
<string name="settings_general_dynamic_colors_title">Dynamische kleuren</string>
<string name="settings_general_dynamic_colors_summary_enabled">Dynamische systeemkleuren worden gebruikt</string>
<string name="settings_general_dynamic_colors_summary_disabled">Ntfy thema kleuren worden gebruikt</string>
<string name="settings_general_message_bar_title">Toon berichtenbalk</string>
<string name="settings_general_message_bar_summary_enabled">Berichtenbalk getoond onderaan onderwerpweergave</string>
</resources> </resources>

View file

@ -341,4 +341,135 @@
<string name="settings_advanced_exact_alarms_title">Alarmes exatos</string> <string name="settings_advanced_exact_alarms_title">Alarmes exatos</string>
<string name="settings_advanced_exact_alarms_true">O ntfy pode agendar alarmes exatos, eles são usados para reconectar WebSockets em segundo plano. Clique para revogar a permissão.</string> <string name="settings_advanced_exact_alarms_true">O ntfy pode agendar alarmes exatos, eles são usados para reconectar WebSockets em segundo plano. Clique para revogar a permissão.</string>
<string name="settings_advanced_exact_alarms_false">O ntfy não pode agendar alarmes exatos, eles são usados para reconectar WebSockets em segundo plano. Clique para conceder a permissão.</string> <string name="settings_advanced_exact_alarms_false">O ntfy não pode agendar alarmes exatos, eles são usados para reconectar WebSockets em segundo plano. Clique para conceder a permissão.</string>
<string name="common_button_next">Próximo</string>
<string name="common_button_add">Adicionar</string>
<string name="common_button_delete">Apagar</string>
<string name="common_button_reset_to_default">Restaurar Padrões</string>
<string name="common_service_url_placeholder">ex. https://ntfy.example.com</string>
<string name="common_certificate_subject">Dono</string>
<string name="common_certificate_issuer">Emissor</string>
<string name="common_certificate_fingerprint">Assinatura SHA-256</string>
<string name="common_certificate_valid_from">Válido de</string>
<string name="common_certificate_valid_until">Válido até</string>
<string name="common_certificate_added_toast">Certificado adicionado</string>
<string name="common_certificate_deleted_toast">Certificado removido</string>
<string name="connection_alert_title">Notificação de alerta de conexão</string>
<string name="connection_alert_text_one">Não foi possível conectar à %1$s por mais de %2$d minutos</string>
<string name="connection_alert_text_multiple">Não foi possível conectar aos servidores %1$d por mais de %2$d minutos</string>
<string name="connection_alert_action_snooze">Adiar %1$dh</string>
<string name="connection_alert_action_never">Nunca mostrar</string>
<string name="main_menu_connection_error">Erro de conexão</string>
<string name="add_dialog_error_ssl_untrusted">Certificado do servidor não confiável</string>
<string name="detail_no_search_results">Sua busca não retornou resultados</string>
<string name="detail_menu_search">Notificações de pesquisa</string>
<string name="detail_menu_search_hint">Buscar em notificações</string>
<string name="publish_dialog_title">Publicar em %1$s</string>
<string name="publish_dialog_title_hint">Título</string>
<string name="publish_dialog_title_placeholder">ex. Tem alguém na porta</string>
<string name="publish_dialog_message_hint">Mensagem</string>
<string name="publish_dialog_tags_hint">Tags</string>
<string name="publish_dialog_tags_placeholder">ex. Alerta, skull</string>
<string name="publish_dialog_priority_hint">Prioridade</string>
<string name="publish_dialog_button_publish">Publicar</string>
<string name="publish_dialog_error_sending">Não foi possível publicar a mensagem: %1$s</string>
<string name="publish_dialog_error_server">Não foi possível publicar a mensagem: %1$s (código %2$d)</string>
<string name="publish_dialog_message_published">Mensagem publicada</string>
<string name="publish_dialog_uploading">Subindo: %1$s (%2$s / %3$s)</string>
<string name="publish_dialog_upload_cancelled">Upload cancelado</string>
<string name="publish_dialog_chip_title">Título</string>
<string name="publish_dialog_chip_tags">Etiquetas</string>
<string name="publish_dialog_chip_priority">Prioridade</string>
<string name="publish_dialog_chip_click_url">Click URL</string>
<string name="publish_dialog_chip_email">Email</string>
<string name="publish_dialog_chip_delay">Atraso</string>
<string name="publish_dialog_chip_markdown">Markdown</string>
<string name="publish_dialog_chip_attach_url">Anexar com URL</string>
<string name="publish_dialog_chip_attach_file">Anexar arquivo local</string>
<string name="publish_dialog_chip_phone_call">Chamada telefônica</string>
<string name="publish_dialog_click_url_hint">Click URL</string>
<string name="publish_dialog_click_url_placeholder">ex. https://example.com/alerts/1234</string>
<string name="publish_dialog_email_hint">Email</string>
<string name="publish_dialog_email_placeholder">ex. phil@example.com</string>
<string name="publish_dialog_delay_hint">Atraso na entrega</string>
<string name="publish_dialog_attach_url_hint">URL do anexo</string>
<string name="publish_dialog_attach_url_placeholder">ex. https://example.com/flowers.jpg</string>
<string name="publish_dialog_attach_filename_hint">Nome de arquivo do anexo</string>
<string name="publish_dialog_attach_filename_placeholder">ex. lilies.jpg</string>
<string name="publish_dialog_phone_call_hint">Chamada telefônica</string>
<string name="publish_dialog_phone_call_placeholder">ex. +1234567890</string>
<string name="publish_dialog_docs_text">Para exemplos e uma descrição detalhada de todos os recursos de publicação, por favor consulte <a href="https://docs.ntfy.sh/publish/">documentation</a>.</string>
<string name="message_bar_hint">Barra de mensagens</string>
<string name="message_bar_publish_button_description">Publicar mensagem</string>
<string name="message_bar_expand_button_description">Mais opções</string>
<string name="trusted_certificate_dialog_button_trust">Confie</string>
<string name="client_certificate_dialog_title">Certificado cliente</string>
<string name="client_certificate_dialog_title_add">Adicionar certificado cliente</string>
<string name="client_certificate_dialog_description_page1">Insira a URL de serviço para o qual este certificado deve ser usado e a senha para o arquivo PKCS#12.</string>
<string name="client_certificate_dialog_description_page2">Revisar os detalhes do certificado e salvar para adicionar este certificado de cliente. Este certificado será usado para autenticar com o servidor.</string>
<string name="client_certificate_dialog_password_hint">Senha</string>
<string name="client_certificate_dialog_error_wrong_password">Senha incorreta ou arquivo PKCS#12 inválido</string>
<string name="client_certificate_dialog_error_invalid_p12_password">Senha inválida ou arquivo PKCS#12 corrompido</string>
<string name="client_certificate_dialog_error_invalid_url">URL de serviço inválida</string>
<string name="publish_dialog_delay_placeholder">ex: 30m, 1h, today 9pm (expressões de tempo apenas em inglês)</string>
<string name="detail_fab_publish_description">Publicar notificação</string>
<string name="connection_error_dialog_title">Falha na conexão</string>
<string name="connection_error_dialog_message">Houve um problema ao conectar em %1$s. O app vai continuar tentando reconectar em segundo plano.</string>
<string name="connection_error_dialog_connection_refused">Conexão recusada. O servidor pode estar desligado ou o endereço pode estar incorreto.</string>
<string name="connection_error_dialog_websocket_not_supported">WebSocket não suportado. O servidor pode não suportar conexões WebSocket ou o endereço pode estar incorreto.</string>
<string name="connection_error_dialog_not_authorized">Não autorizado. O servidor retornou uma resposta HTTP 401/403. Favor verificar se seu usuário e senha estão corretos.</string>
<string name="connection_error_dialog_retry_now">Tentar de novo agora</string>
<string name="connection_error_dialog_retry_countdown">Tentando de novo em %1$d…</string>
<string name="connection_error_dialog_retrying">Tentando de novo…</string>
<string name="settings_general_language_title">Idioma</string>
<string name="settings_general_language_summary_system">Usando padrão do sistema</string>
<string name="settings_general_language_system_default">Padrão do sistema</string>
<string name="settings_general_dynamic_colors_title">Cores dinâmicas</string>
<string name="settings_general_dynamic_colors_summary_enabled">Usando as cores dinâmicas do sistema</string>
<string name="settings_general_dynamic_colors_summary_disabled">Usando as cores do tema ntfy</string>
<string name="settings_general_message_bar_title">Mostrar barra de mensagens</string>
<string name="settings_general_message_bar_summary_enabled">Barra de mensagens mostrada abaixo da visualização de tópico</string>
<string name="settings_general_message_bar_summary_disabled">Botão publicar mostrado abaixo da visualização de tópico</string>
<string name="settings_advanced_custom_headers_title">Cabeçalhos customizados</string>
<string name="settings_advanced_custom_headers_summary">Definir cabeçalhos HTTP personalizados que serão enviados junto com cada requisição, por ex. se seu servidor ntfy está atrás de um túnel ou proxy autenticado.</string>
<string name="settings_advanced_custom_headers_prefs_header_add">Adicionar cabeçalho</string>
<string name="settings_advanced_custom_headers_prefs_header_add_title">Adicionar um cabeçalho para um servidor</string>
<string name="settings_advanced_custom_headers_prefs_header_add_summary">Cabeçalhos são incluídos em cada requisição HTTP. Cada servidor ntfy pode ter seu próprio conjunto de cabeçalhos personalizados.</string>
<string name="settings_advanced_certificates_title">Gerenciar certificados</string>
<string name="settings_advanced_certificates_summary">Adicionar certificados para a trust store e gerenciar certificados de cliente para mTLS</string>
<string name="settings_advanced_certificates_trusted_header">Certificados confiáveis</string>
<string name="settings_advanced_certificates_trusted_item_summary">Certificado fixado, emitido por %1$s,expira em %2$s, usado para conexões em %3$s</string>
<string name="settings_advanced_certificates_trusted_item_summary_expired">Certificado fixado, emitido por %1$s, expiradom usado para conexões em %2$s</string>
<string name="settings_advanced_certificates_trusted_add_title">Adicionar um certificado confiável</string>
<string name="settings_advanced_certificates_trusted_add_summary">Importar um certificado para a trust store (PEM). Ao conectar ao servidor ntfy, este certificado será considerado confiável.</string>
<string name="settings_advanced_certificates_client_header">Certificado de cliente (mTLS)</string>
<string name="settings_advanced_certificates_client_item_summary">Certificado de cliente, emitido por %1$s, expira em %2$s, usado para conexões com %3$s</string>
<string name="settings_advanced_certificates_client_item_summary_expired">Certificado de cliente, emitido por %1$s, expirado, usado para conexões com%2$s</string>
<string name="settings_advanced_certificates_client_add_title">Adicionar um certificado de cliente</string>
<string name="settings_advanced_certificates_client_add_summary">Importar certificado para autenticação TLS mútua (PKCS#12). Este certificado será usado ao conectar com o servidor.</string>
<string name="settings_advanced_certificates_error_invalid_cert">Arquivo de certificado inválido</string>
<string name="settings_advanced_certificates_error_invalid_p12">Arquivo PKCS#12 inválido</string>
<string name="user_dialog_base_url_error_authorization_header_exists">Não é possível adicionar usuário se o cabeçalho de autorização personalizada está configurado para este servidor</string>
<string name="default_server_dialog_url_error_invalid">Insira uma URL de serviço válida, por ex. https://ntfy.example.com</string>
<string name="custom_headers_dialog_title_add">Adicionar cabeçalho personalizado</string>
<string name="custom_headers_dialog_title_edit">Editar cabeçalho personalizado</string>
<string name="custom_headers_dialog_name_hint">Nome do cabeçalho (ex. CF-Access-Client-Id)</string>
<string name="custom_headers_dialog_value_hint">Valor do cabeçalho (ex. 9f3c2e4a1b2d4e)</string>
<string name="custom_headers_dialog_description_add">Adicionar um cabeçalho HTTP personalizado que será enviado com cada requisição para o servidor especificado.</string>
<string name="custom_headers_dialog_description_edit">Você pode editar o nome/valor do cabeçalho para o cabeçalho selecionado, ou apaga-lo.</string>
<string name="custom_headers_dialog_error_invalid_name">O nome do cabeçalho contém caracteres inválidos</string>
<string name="custom_headers_dialog_error_reserved_name">Este cabeçalho está reservado e configurado pelo ntfy</string>
<string name="custom_headers_dialog_error_user_exists">Não é possível adicionar um cabeçalho de autorização se um usuário está configurado para este servidor</string>
<string name="custom_headers_dialog_error_duplicate">Um cabeçalho com este nome já existe para este servidor</string>
<string name="trusted_certificate_dialog_title">Detalhes do certificado</string>
<string name="trusted_certificate_dialog_title_unknown">Alerta de segurança</string>
<string name="trusted_certificate_dialog_title_add">Adicionar certificado confiável</string>
<string name="trusted_certificate_dialog_security_title">Sua conexão não é privada</string>
<string name="trusted_certificate_dialog_security_description">O certificado do servidor não é confiável. Atacantes podem estar tentando roubar suas informações. Não prossiga a menos que você saiba porque este certificado não é confiável.</string>
<string name="trusted_certificate_dialog_description_add">Você selecionou um arquivo de certificado. Revise os detalhes abaixo antes de adiciona-lo aos seus certificados confiáveis.</string>
<string name="trusted_certificate_dialog_description_view">Este certificado é usado para conexões à URL de serviço abaixo. Você adicionou esta exceção manualmente.</string>
<string name="trusted_certificate_dialog_description_page1">Insira a URL de serviço para o qual este certificado deveria ser fixado. O certificado será considerado confiável apenas para esta URL.</string>
<string name="trusted_certificate_dialog_expired_warning">Alerta: Este certificado expirou.</string>
<string name="trusted_certificate_dialog_not_yet_valid_warning">Alerta: Este certificado ainda não é válido.</string>
<string name="trusted_certificate_dialog_error_invalid_url">URL inválida</string>
<string name="trusted_certificate_dialog_error_parse">Não foi possível carregar certificado: %1$s</string>
</resources> </resources>

View file

@ -345,4 +345,26 @@
<string name="settings_advanced_exact_alarms_title">Alarmes exactos</string> <string name="settings_advanced_exact_alarms_title">Alarmes exactos</string>
<string name="settings_advanced_exact_alarms_true">O ntfy pode agendar alarmes exatos. Alarmes exatos são necessários para reconectar os WebSockets em segundo plano. Clique para revogar a permissão.</string> <string name="settings_advanced_exact_alarms_true">O ntfy pode agendar alarmes exatos. Alarmes exatos são necessários para reconectar os WebSockets em segundo plano. Clique para revogar a permissão.</string>
<string name="settings_advanced_exact_alarms_false">O ntfy não pode agendar alarmes exatos. Alarmes exatos são necessários para reconectar os WebSockets em segundo plano. Clique para conceder a permissão.</string> <string name="settings_advanced_exact_alarms_false">O ntfy não pode agendar alarmes exatos. Alarmes exatos são necessários para reconectar os WebSockets em segundo plano. Clique para conceder a permissão.</string>
<string name="common_button_next">Próximo</string>
<string name="common_button_add">Adicionar</string>
<string name="common_button_delete">Apagar</string>
<string name="common_button_reset_to_default">Restaurar valores padrão</string>
<string name="common_service_url_placeholder">ex. https://ntfy.example.com</string>
<string name="common_certificate_subject">Assunto</string>
<string name="common_certificate_issuer">Emissor</string>
<string name="common_certificate_fingerprint">Impressão digital SHA-256</string>
<string name="common_certificate_valid_from">Válido desde</string>
<string name="common_certificate_valid_until">Válido até</string>
<string name="common_certificate_added_toast">Certificado adicionado</string>
<string name="common_certificate_deleted_toast">Certificado apagado</string>
<string name="connection_alert_title">Conexão perdida</string>
<string name="publish_dialog_title_placeholder">ex. Alguém está à porta</string>
<string name="publish_dialog_message_hint">Mensagem</string>
<string name="publish_dialog_tags_hint">Etiquetas</string>
<string name="publish_dialog_tags_placeholder">ex. aviso, perigo</string>
<string name="publish_dialog_priority_hint">Prioridade</string>
<string name="publish_dialog_button_publish">Publicar</string>
<string name="publish_dialog_error_sending">Não foi possível publicar a mensagem: %1$s</string>
<string name="publish_dialog_error_server">Não foi possível publicar a mensagem: %1$s (código %2$d)</string>
<string name="publish_dialog_message_published">Mensagem publicada</string>
</resources> </resources>

View file

@ -343,4 +343,7 @@
<string name="settings_advanced_exact_alarms_title">Alarme exacte</string> <string name="settings_advanced_exact_alarms_title">Alarme exacte</string>
<string name="settings_advanced_exact_alarms_true">ntfy poate programa alarme exacte. Alarmele exacte sunt necesare pentru a reconecta WebSocket-urile în fundal. Faceți clic pentru a revoca permisiunea.</string> <string name="settings_advanced_exact_alarms_true">ntfy poate programa alarme exacte. Alarmele exacte sunt necesare pentru a reconecta WebSocket-urile în fundal. Faceți clic pentru a revoca permisiunea.</string>
<string name="settings_advanced_exact_alarms_false">ntfy nu poate programa alarme exacte. Alarmele exacte sunt necesare pentru a reconecta WebSockets în fundal. Faceți clic pentru a acorda permisiunea.</string> <string name="settings_advanced_exact_alarms_false">ntfy nu poate programa alarme exacte. Alarmele exacte sunt necesare pentru a reconecta WebSockets în fundal. Faceți clic pentru a acorda permisiunea.</string>
<string name="common_button_next">Următorul</string>
<string name="common_button_add">Adaugă</string>
<string name="common_button_delete">Şterge</string>
</resources> </resources>

View file

@ -132,9 +132,7 @@
<string name="settings_backup_restore_restore_title">Восстановить из файла</string> <string name="settings_backup_restore_restore_title">Восстановить из файла</string>
<string name="settings_general_users_prefs_user_add_title">Добавить нового пользователя</string> <string name="settings_general_users_prefs_user_add_title">Добавить нового пользователя</string>
<string name="main_menu_notifications_disabled_until">Уведомления отключены до %1$s</string> <string name="main_menu_notifications_disabled_until">Уведомления отключены до %1$s</string>
<string name="refresh_message_error">Не получилось обновить %1$d подписок <string name="refresh_message_error">Не получилось обновить %1$d подписок\n\n%2$s</string>
\n
\n%2$s</string>
<string name="refresh_message_error_one">Не получилось обновить подписку: %1$s</string> <string name="refresh_message_error_one">Не получилось обновить подписку: %1$s</string>
<string name="main_action_bar_title">Темы подписок</string> <string name="main_action_bar_title">Темы подписок</string>
<string name="main_how_to_intro">Нажмите \"+\", чтобы создать или подписаться на тему. Вы будете получать уведомления на вашем устройстве при отправке сообщений через PUT или POST-запросы.</string> <string name="main_how_to_intro">Нажмите \"+\", чтобы создать или подписаться на тему. Вы будете получать уведомления на вашем устройстве при отправке сообщений через PUT или POST-запросы.</string>
@ -466,4 +464,29 @@
<string name="client_certificate_dialog_error_wrong_password">Неверный пароль или недопустимый файл PKCS#12</string> <string name="client_certificate_dialog_error_wrong_password">Неверный пароль или недопустимый файл PKCS#12</string>
<string name="client_certificate_dialog_error_invalid_p12_password">Неверный пароль или поврежденный файл PKCS#12</string> <string name="client_certificate_dialog_error_invalid_p12_password">Неверный пароль или поврежденный файл PKCS#12</string>
<string name="client_certificate_dialog_error_invalid_url">Недопустимый URL сервиса</string> <string name="client_certificate_dialog_error_invalid_url">Недопустимый URL сервиса</string>
<string name="common_button_reset_to_default">Сбросить по умолчанию</string>
<string name="connection_alert_action_never">Не показывать</string>
<string name="connection_alert_title">Соединение разорвано</string>
<string name="connection_alert_action_snooze">Отложить на %1$dч</string>
<string name="connection_alert_text_one">Невозможно подключиться к %1$s более %2$d мин. Проверьте подключение к сети.</string>
<string name="connection_alert_text_multiple">Невозможно подключиться к %1$d серверам более %2$d мин. Проверьте подключение к сети.</string>
<string name="channel_connection_alert_name">Уведомления о подключении</string>
<string name="main_banner_no_network_text">Вы не в сети</string>
<string name="detail_no_search_results">Ничего не найдено</string>
<string name="detail_menu_search_hint">Искать в уведомлениях</string>
<string name="settings_advanced_connection_alert_title">Оповещение при отключении</string>
<string name="settings_advanced_connection_alert_summary_never">Никогда не уведомлять о недоступности сервера</string>
<string name="settings_advanced_connection_alert_summary_five_minutes">Уведомлять, если сервер недоступен более 5 минут</string>
<string name="settings_advanced_connection_alert_summary_fifteen_minutes">Уведомлять, если сервер недоступен более 15 минут</string>
<string name="settings_advanced_connection_alert_summary_one_hour">Уведомлять, если сервер недоступен более 1 часа</string>
<string name="settings_advanced_connection_alert_summary_three_hours">Уведомлять, если сервер недоступен более 3 часов</string>
<string name="settings_advanced_connection_alert_summary_twelve_hours">Уведомлять, если сервер недоступен более 12 часов</string>
<string name="settings_advanced_connection_alert_never">Никогда</string>
<string name="settings_advanced_connection_alert_five_minutes">Через 5 минут</string>
<string name="settings_advanced_connection_alert_fifteen_minutes">Через 15 минут</string>
<string name="settings_advanced_connection_alert_one_hour">Через 1 час</string>
<string name="settings_advanced_connection_alert_three_hours">Через 3 часа</string>
<string name="settings_advanced_connection_alert_twelve_hours">Через 12 часов</string>
<string name="default_server_dialog_url_error_invalid">Укажите верный URL, например https://ntfy.example.com</string>
<string name="detail_menu_search">Поиск уведомлений</string>
</resources> </resources>

View file

@ -51,7 +51,7 @@
<string name="main_banner_battery_button_dismiss">தள்ளுபடி</string> <string name="main_banner_battery_button_dismiss">தள்ளுபடி</string>
<string name="main_banner_battery_button_fix_now">இப்போது சரிசெய்யவும்</string> <string name="main_banner_battery_button_fix_now">இப்போது சரிசெய்யவும்</string>
<string name="add_dialog_base_urls_dropdown_clear">தெளிவான பணி முகவரி</string> <string name="add_dialog_base_urls_dropdown_clear">தெளிவான பணி முகவரி</string>
<string name="detail_how_to_example">எடுத்துக்காட்டு (சுருட்டைப் பயன்படுத்துதல்): <br></br> <tt> $ சுருட்டை -d \"hi\" %1$s </tt></string> <string name="detail_how_to_example">எடுத்துக்காட்டு (சுருட்டைப் பயன்படுத்துதல்): <br/> <tt> $ சுருட்டை -d \"hi\" %1$s </tt></string>
<string name="detail_how_to_link">Ntfy.sh, மற்றும் டாக்சில் விரிவான வழிமுறைகள் கிடைக்கின்றன.</string> <string name="detail_how_to_link">Ntfy.sh, மற்றும் டாக்சில் விரிவான வழிமுறைகள் கிடைக்கின்றன.</string>
<string name="detail_instant_delivery_disabled">உடனடி வழங்கல்</string> <string name="detail_instant_delivery_disabled">உடனடி வழங்கல்</string>
<string name="detail_deep_link_subscribed_toast_message">தலைப்பு %1$s க்கு குழுசேர்ந்தது</string> <string name="detail_deep_link_subscribed_toast_message">தலைப்பு %1$s க்கு குழுசேர்ந்தது</string>

View file

@ -71,7 +71,7 @@
<string name="add_dialog_base_urls_dropdown_choose">URL manzilini tanlang</string> <string name="add_dialog_base_urls_dropdown_choose">URL manzilini tanlang</string>
<string name="add_dialog_base_urls_dropdown_clear">URL manzilini tozalang</string> <string name="add_dialog_base_urls_dropdown_clear">URL manzilini tozalang</string>
<string name="detail_how_to_intro">Ushbu mavzu boyicha bildirishnomalarni yuborish uchun, mavzu URL manziliga PUT yoki POST sorov yuboring.</string> <string name="detail_how_to_intro">Ushbu mavzu boyicha bildirishnomalarni yuborish uchun, mavzu URL manziliga PUT yoki POST sorov yuboring.</string>
<string name="detail_how_to_example">Misol (curl yordamida):<br></br><tt>$ curl -d \"Salom\" %1$s</tt></string> <string name="detail_how_to_example">Misol (curl yordamida):<br/><tt>$ curl -d \"Salom\" %1$s</tt></string>
<string name="detail_how_to_link">Batafsil korsatmalar ntfy.sh saytida va hujjatlarda mavjud.</string> <string name="detail_how_to_link">Batafsil korsatmalar ntfy.sh saytida va hujjatlarda mavjud.</string>
<string name="detail_clear_dialog_message">Ushbu mavzudagi barcha bildirishnomalar ochirilsinmi?</string> <string name="detail_clear_dialog_message">Ushbu mavzudagi barcha bildirishnomalar ochirilsinmi?</string>
<string name="detail_clear_dialog_permanently_delete">Butunlay ochirish</string> <string name="detail_clear_dialog_permanently_delete">Butunlay ochirish</string>

View file

@ -142,7 +142,7 @@
<string name="common_priority_default_name">默认优先级</string> <string name="common_priority_default_name">默认优先级</string>
<string name="common_priority_high_name">高优先级</string> <string name="common_priority_high_name">高优先级</string>
<string name="common_priority_max_name">最高优先级</string> <string name="common_priority_max_name">最高优先级</string>
<string name="channel_subscriber_service_name">订阅服务</string> <string name="channel_subscriber_service_name">后台服务</string>
<string name="channel_subscriber_notification_title">正在监听通知</string> <string name="channel_subscriber_notification_title">正在监听通知</string>
<string name="channel_subscriber_notification_noinstant_text">已订阅主题</string> <string name="channel_subscriber_notification_noinstant_text">已订阅主题</string>
<string name="channel_subscriber_notification_noinstant_text_one">已订阅 1 个主题</string> <string name="channel_subscriber_notification_noinstant_text_one">已订阅 1 个主题</string>
@ -471,4 +471,24 @@
<string name="detail_menu_search_hint">在通知中搜索</string> <string name="detail_menu_search_hint">在通知中搜索</string>
<string name="common_button_reset_to_default">重置为默认</string> <string name="common_button_reset_to_default">重置为默认</string>
<string name="default_server_dialog_url_error_invalid">输入有效的服务 URL如 https://ntfy.example.com</string> <string name="default_server_dialog_url_error_invalid">输入有效的服务 URL如 https://ntfy.example.com</string>
<string name="connection_alert_title">连接丢失</string>
<string name="connection_alert_text_one">已无法连接到 %1$s 超过 %2$d 分钟。检查网络连接。</string>
<string name="connection_alert_text_multiple">已无法连接到 %1$d 台服务器超过 %2$d 分钟。检查网络连接。</string>
<string name="connection_alert_action_snooze">延后 %1$d 小时</string>
<string name="connection_alert_action_never">永不显示</string>
<string name="channel_connection_alert_name">连接警报</string>
<string name="main_banner_no_network_text">未联网</string>
<string name="settings_advanced_connection_alert_title">连接断开时示警</string>
<string name="settings_advanced_connection_alert_summary_never">无法连接到 ntfy 服务器时永远不通知</string>
<string name="settings_advanced_connection_alert_summary_five_minutes">如果无法连接到 ntfy 服务器超过 5 分钟就发出通知</string>
<string name="settings_advanced_connection_alert_summary_fifteen_minutes">如果无法连接到 ntfy 服务器超过 15 分钟就发出通知</string>
<string name="settings_advanced_connection_alert_summary_one_hour">如果无法连接到 ntfy 服务器超过 1 小时就发出通知</string>
<string name="settings_advanced_connection_alert_summary_three_hours">如果无法连接到 ntfy 服务器超过 3 小时就发出通知</string>
<string name="settings_advanced_connection_alert_summary_twelve_hours">如果无法连接到 ntfy 服务器超过 12 小时就发出通知</string>
<string name="settings_advanced_connection_alert_never">永不</string>
<string name="settings_advanced_connection_alert_five_minutes">5 分钟后</string>
<string name="settings_advanced_connection_alert_fifteen_minutes">15 分钟后</string>
<string name="settings_advanced_connection_alert_one_hour">1 小时后</string>
<string name="settings_advanced_connection_alert_three_hours">3 小时后</string>
<string name="settings_advanced_connection_alert_twelve_hours">12 小时后</string>
</resources> </resources>

View file

@ -471,4 +471,9 @@
<string name="detail_menu_search_hint">通知中的搜尋</string> <string name="detail_menu_search_hint">通知中的搜尋</string>
<string name="common_button_reset_to_default">重設為預設值</string> <string name="common_button_reset_to_default">重設為預設值</string>
<string name="default_server_dialog_url_error_invalid">請輸入有效的服務 URL例如https://ntfy.example.com</string> <string name="default_server_dialog_url_error_invalid">請輸入有效的服務 URL例如https://ntfy.example.com</string>
<string name="connection_alert_title">連線已中斷</string>
<string name="connection_alert_text_one">超過 %2$d 分鐘無法連線至 %1$s</string>
<string name="connection_alert_text_multiple">超過 %2$d 分鐘無法連線至 %1$d 個伺服器</string>
<string name="connection_alert_action_snooze">延後 %1$d 小時</string>
<string name="connection_alert_action_never">永不顯示</string>
</resources> </resources>

View file

@ -28,7 +28,8 @@
<!-- Notification channels --> <!-- Notification channels -->
<string name="channel_notifications_group_default_name">Default</string> <string name="channel_notifications_group_default_name">Default</string>
<string name="channel_subscriber_service_name">Subscription Service</string> <string name="channel_subscriber_service_name">Background service</string>
<string name="channel_connection_alert_name">Connection alerts</string>
<string name="channel_subscriber_notification_title">Listening for incoming notifications</string> <string name="channel_subscriber_notification_title">Listening for incoming notifications</string>
<string name="channel_subscriber_notification_instant_text">Subscribed to instant delivery topics</string> <string name="channel_subscriber_notification_instant_text">Subscribed to instant delivery topics</string>
<string name="channel_subscriber_notification_instant_text_one">Subscribed to one instant delivery topic</string> <string name="channel_subscriber_notification_instant_text_one">Subscribed to one instant delivery topic</string>
@ -49,10 +50,10 @@
<!-- Connection alert notification --> <!-- Connection alert notification -->
<string name="connection_alert_title">Connection lost</string> <string name="connection_alert_title">Connection lost</string>
<string name="connection_alert_text_one">Unable to connect to %1$s for more than %2$d minutes</string> <string name="connection_alert_text_one">Unable to connect to %1$s for more than %2$d minutes. Check your network connection.</string>
<string name="connection_alert_text_multiple">Unable to connect to %1$d servers for more than %2$d minutes</string> <string name="connection_alert_text_multiple">Unable to connect to %1$d servers for more than %2$d minutes. Check your network connection.</string>
<string name="connection_alert_action_dismiss">Dismiss</string> <!-- %1$d is the snooze duration in hours, currently set to 8 -->
<string name="connection_alert_action_snooze">Snooze 1h</string> <string name="connection_alert_action_snooze">Snooze %1$dh</string>
<string name="connection_alert_action_never">Never show</string> <string name="connection_alert_action_never">Never show</string>
<!-- Common refresh toasts --> <!-- Common refresh toasts -->
@ -115,6 +116,7 @@
<string name="main_banner_websocket_reconnect_button_remind_later">Ask later</string> <string name="main_banner_websocket_reconnect_button_remind_later">Ask later</string>
<string name="main_banner_websocket_reconnect_button_dismiss">Dismiss</string> <string name="main_banner_websocket_reconnect_button_dismiss">Dismiss</string>
<string name="main_banner_websocket_reconnect_button_enable_now">Grant now</string> <string name="main_banner_websocket_reconnect_button_enable_now">Grant now</string>
<string name="main_banner_no_network_text">You\'re offline</string>
<!-- Add dialog --> <!-- Add dialog -->
<string name="add_dialog_title">Subscribe to topic</string> <string name="add_dialog_title">Subscribe to topic</string>
@ -415,6 +417,19 @@
<string name="settings_backup_restore_restore_successful">Restore successful</string> <string name="settings_backup_restore_restore_successful">Restore successful</string>
<string name="settings_backup_restore_restore_failed">Restore failed: %1$s</string> <string name="settings_backup_restore_restore_failed">Restore failed: %1$s</string>
<string name="settings_advanced_header">Advanced</string> <string name="settings_advanced_header">Advanced</string>
<string name="settings_advanced_connection_alert_title">Alert when disconnected</string>
<string name="settings_advanced_connection_alert_summary_never">Never notify when the ntfy server cannot be reached</string>
<string name="settings_advanced_connection_alert_summary_five_minutes">Notify if the ntfy server cannot be reached for more than 5 minutes</string>
<string name="settings_advanced_connection_alert_summary_fifteen_minutes">Notify if the ntfy server cannot be reached for more than 15 minutes</string>
<string name="settings_advanced_connection_alert_summary_one_hour">Notify if the ntfy server cannot be reached for more than 1 hour</string>
<string name="settings_advanced_connection_alert_summary_three_hours">Notify if the ntfy server cannot be reached for more than 3 hours</string>
<string name="settings_advanced_connection_alert_summary_twelve_hours">Notify if the ntfy server cannot be reached for more than 12 hours</string>
<string name="settings_advanced_connection_alert_never">Never</string>
<string name="settings_advanced_connection_alert_five_minutes">After 5 minutes</string>
<string name="settings_advanced_connection_alert_fifteen_minutes">After 15 minutes</string>
<string name="settings_advanced_connection_alert_one_hour">After 1 hour</string>
<string name="settings_advanced_connection_alert_three_hours">After 3 hours</string>
<string name="settings_advanced_connection_alert_twelve_hours">After 12 hours</string>
<string name="settings_advanced_broadcast_title">Broadcast messages</string> <string name="settings_advanced_broadcast_title">Broadcast messages</string>
<string name="settings_advanced_broadcast_summary_enabled">Apps can receive incoming notifications as broadcasts</string> <string name="settings_advanced_broadcast_summary_enabled">Apps can receive incoming notifications as broadcasts</string>
<string name="settings_advanced_broadcast_summary_disabled">Apps cannot receive notifications as broadcasts</string> <string name="settings_advanced_broadcast_summary_disabled">Apps cannot receive notifications as broadcasts</string>

View file

@ -47,6 +47,7 @@
<string name="detail_settings_notifications_min_priority_key" translatable="false">SubscriptionMinPriority</string> <string name="detail_settings_notifications_min_priority_key" translatable="false">SubscriptionMinPriority</string>
<string name="detail_settings_notifications_auto_delete_key" translatable="false">SubscriptionAutoDelete</string> <string name="detail_settings_notifications_auto_delete_key" translatable="false">SubscriptionAutoDelete</string>
<string name="detail_settings_notifications_insistent_max_priority_key" translatable="false">SubscriptionInsistentMaxPriority</string> <string name="detail_settings_notifications_insistent_max_priority_key" translatable="false">SubscriptionInsistentMaxPriority</string>
<string name="settings_advanced_connection_alert_key" translatable="false">ConnectionAlert</string>
<string name="detail_settings_appearance_header_key" translatable="false">SubscriptionAppearance</string> <string name="detail_settings_appearance_header_key" translatable="false">SubscriptionAppearance</string>
<string name="detail_settings_appearance_icon_set_key" translatable="false">SubscriptionIconSet</string> <string name="detail_settings_appearance_icon_set_key" translatable="false">SubscriptionIconSet</string>
<string name="detail_settings_appearance_icon_remove_key" translatable="false">SubscriptionIconRemove</string> <string name="detail_settings_appearance_icon_remove_key" translatable="false">SubscriptionIconRemove</string>
@ -167,6 +168,22 @@
<item>1</item> <item>1</item>
<item>0</item> <item>0</item>
</string-array> </string-array>
<string-array name="settings_advanced_connection_alert_entries">
<item>@string/settings_advanced_connection_alert_never</item>
<item>@string/settings_advanced_connection_alert_five_minutes</item>
<item>@string/settings_advanced_connection_alert_fifteen_minutes</item>
<item>@string/settings_advanced_connection_alert_one_hour</item>
<item>@string/settings_advanced_connection_alert_three_hours</item>
<item>@string/settings_advanced_connection_alert_twelve_hours</item>
</string-array>
<string-array name="settings_advanced_connection_alert_values">
<item>0</item>
<item>300</item>
<item>900</item>
<item>3600</item>
<item>10800</item>
<item>43200</item>
</string-array>
<string-array name="settings_advanced_connection_protocol_entries"> <string-array name="settings_advanced_connection_protocol_entries">
<item>@string/settings_advanced_connection_protocol_entry_jsonhttp</item> <item>@string/settings_advanced_connection_protocol_entry_jsonhttp</item>
<item>@string/settings_advanced_connection_protocol_entry_ws</item> <item>@string/settings_advanced_connection_protocol_entry_ws</item>

View file

@ -2,4 +2,5 @@
<paths> <paths>
<external-path name="external_files" path="."/> <external-path name="external_files" path="."/>
<cache-path name="cache_files" path="."/> <cache-path name="cache_files" path="."/>
<files-path name="files" path="."/>
</paths> </paths>

View file

@ -77,6 +77,12 @@
app:summary="@string/settings_backup_restore_restore_summary"/> app:summary="@string/settings_backup_restore_restore_summary"/>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory app:title="@string/settings_advanced_header"> <PreferenceCategory app:title="@string/settings_advanced_header">
<ListPreference
app:key="@string/settings_advanced_connection_alert_key"
app:title="@string/settings_advanced_connection_alert_title"
app:entries="@array/settings_advanced_connection_alert_entries"
app:entryValues="@array/settings_advanced_connection_alert_values"
app:defaultValue="0"/>
<ListPreference <ListPreference
app:key="@string/settings_advanced_connection_protocol_key" app:key="@string/settings_advanced_connection_protocol_key"
app:title="@string/settings_advanced_connection_protocol_title" app:title="@string/settings_advanced_connection_protocol_title"

View file

@ -0,0 +1,7 @@
Features:
* Add configurable "Alert when connection is lost" setting (#1665, #1662, #1652, #1655, thanks to @tintamarre, @sjozs, @TheRealOne78, and @DAE51D for reporting)
* Suppress connection alerts and stop foreground service when there is no network (ntfy-android#165, thanks to @tintamarre for the contribution)
Bug fixes + maintenance:
* Undo automatic phone number linking for numbers in message body (ntfy-android#170, thanks to @acortelyou for the contribution)
* Fix subscription icons disappearing after a few days due to Android clearing cache (#1322, thanks to @mcanning for reporting)

View file

@ -1 +1 @@
ntfy - PUT/POST ao seu disp. ntfy - PUT/POST ao dispositivo