aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Heckel <pheckel@datto.com>2022-12-06 11:39:36 -0500
committerPhilipp Heckel <pheckel@datto.com>2022-12-06 11:39:36 -0500
commitfea0709a557a2926806234954c89f79f12e72ec9 (patch)
treed861151da00eb85f2628aa8223172d437ed63bff
parent96d867c1a63a2e662de2385d6af0b7a0bbecc98b (diff)
parent5409c84c667590b482222ee6b9c36cb7a547a76c (diff)
downloadponypush-fea0709a557a2926806234954c89f79f12e72ec9.tar.gz
ponypush-fea0709a557a2926806234954c89f79f12e72ec9.tar.bz2
ponypush-fea0709a557a2926806234954c89f79f12e72ec9.zip
Merge branch 'main' into constant-ring
-rw-r--r--app/build.gradle23
-rw-r--r--app/src/main/AndroidManifest.xml10
-rw-r--r--app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt3
-rw-r--r--app/src/main/java/io/heckel/ntfy/ui/DetailAdapter.kt8
-rw-r--r--app/src/main/java/io/heckel/ntfy/util/Util.kt18
-rw-r--r--app/src/main/res/values-bg/strings.xml1
-rw-r--r--app/src/main/res/values-cs/strings.xml1
-rw-r--r--app/src/main/res/values-de/strings.xml1
-rw-r--r--app/src/main/res/values-es/strings.xml1
-rw-r--r--app/src/main/res/values-in/strings.xml5
-rw-r--r--app/src/main/res/values-iw/strings.xml20
-rw-r--r--app/src/main/res/values-ja/strings.xml1
-rw-r--r--app/src/main/res/values-pt/strings.xml72
-rw-r--r--app/src/main/res/values-sv/strings.xml24
-rw-r--r--app/src/main/res/values-tr/strings.xml1
-rw-r--r--app/src/main/res/values-zh-rCN/strings.xml1
-rw-r--r--app/src/main/res/values/strings.xml1
-rw-r--r--app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt5
-rw-r--r--fastlane/metadata/android/en-US/changelog/31.txt (renamed from fastlane/metadata/android/en-US/changelog/29.txt)2
19 files changed, 186 insertions, 12 deletions
diff --git a/app/build.gradle b/app/build.gradle
index d34de50..2f42134 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -14,8 +14,8 @@ android {
minSdkVersion 21
targetSdkVersion 33
- versionCode 29
- versionName "1.15.0"
+ versionCode 31
+ versionName "1.15.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -44,10 +44,12 @@ android {
play {
buildConfigField 'boolean', 'FIREBASE_AVAILABLE', 'true'
buildConfigField 'boolean', 'RATE_APP_AVAILABLE', 'true'
+ buildConfigField 'boolean', 'INSTALL_PACKAGES_AVAILABLE', 'false'
}
fdroid {
buildConfigField 'boolean', 'FIREBASE_AVAILABLE', 'false'
buildConfigField 'boolean', 'RATE_APP_AVAILABLE', 'false'
+ buildConfigField 'boolean', 'INSTALL_PACKAGES_AVAILABLE', 'true'
}
}
@@ -64,12 +66,29 @@ android {
}
}
+// Disables GoogleServices tasks for F-Droid variant
android.applicationVariants.all { variant ->
def shouldProcessGoogleServices = variant.flavorName == "play"
def googleTask = tasks.findByName("process${variant.name.capitalize()}GoogleServices")
googleTask.enabled = shouldProcessGoogleServices
}
+// Strips out REQUEST_INSTALL_PACKAGES permission for Google Play variant
+android.applicationVariants.all { variant ->
+ def shouldStripInstallPermission = variant.flavorName == "play"
+ if (shouldStripInstallPermission) {
+ variant.outputs.each { output ->
+ def processManifest = output.getProcessManifestProvider().get()
+ processManifest.doLast { task ->
+ def outputDir = task.getMultiApkManifestOutputDirectory().get().asFile
+ def manifestOutFile = file("$outputDir/AndroidManifest.xml")
+ def newFileContents = manifestOutFile.collect { s -> s.contains("android.permission.REQUEST_INSTALL_PACKAGES") ? "" : s }.join("\n")
+ manifestOutFile.write(newFileContents, 'UTF-8')
+ }
+ }
+ }
+}
+
dependencies {
// AndroidX, The Basics
implementation "androidx.appcompat:appcompat:1.5.1"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8120b56..0950b40 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.heckel.ntfy">
+
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <!-- For instant delivery foregrounds service -->
@@ -8,10 +9,17 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <!-- To restart service on reboot -->
<uses-permission android:name="android.permission.VIBRATE"/> <!-- Incoming notifications should be able to vibrate the phone -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/> <!-- Only required on SDK <= 28 -->
- <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> <!-- To install packages downloaded through ntfy; craazyy! -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/> <!-- To reschedule the websocket retry -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <!-- As of Android 13, we need to ask for permission to post notifications -->
+ <!--
+ Permission REQUEST_INSTALL_PACKAGES (F-Droid only!):
+ - Permission is used to install .apk files that were received as attachments
+ - Google rejected the permission for ntfy, so this permission is STRIPPED OUT by the build process
+ for the Google Play variant of the app.
+ -->
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
+
<application
android:name=".app.Application"
android:allowBackup="true"
diff --git a/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt b/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt
index 53e7fe9..2a1ee12 100644
--- a/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt
+++ b/app/src/main/java/io/heckel/ntfy/msg/NotificationService.kt
@@ -183,6 +183,9 @@ class NotificationService(val context: Context) {
}
private fun maybeAddOpenAction(builder: NotificationCompat.Builder, notification: Notification) {
+ if (!canOpenAttachment(notification.attachment)) {
+ return
+ }
if (notification.attachment?.contentUri != null) {
val contentUri = Uri.parse(notification.attachment.contentUri)
val intent = Intent(Intent.ACTION_VIEW, contentUri).apply {
diff --git a/app/src/main/java/io/heckel/ntfy/ui/DetailAdapter.kt b/app/src/main/java/io/heckel/ntfy/ui/DetailAdapter.kt
index 19f6f3c..9aca884 100644
--- a/app/src/main/java/io/heckel/ntfy/ui/DetailAdapter.kt
+++ b/app/src/main/java/io/heckel/ntfy/ui/DetailAdapter.kt
@@ -25,6 +25,7 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton
import com.stfalcon.imageviewer.StfalconImageViewer
+import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
import io.heckel.ntfy.db.*
import io.heckel.ntfy.msg.DownloadManager
@@ -35,7 +36,6 @@ import io.heckel.ntfy.msg.NotificationService.Companion.ACTION_VIEW
import io.heckel.ntfy.util.*
import kotlinx.coroutines.*
-
class DetailAdapter(private val activity: Activity, private val lifecycleScope: CoroutineScope, private val repository: Repository, private val onClick: (Notification) -> Unit, private val onLongClick: (Notification) -> Unit) :
ListAdapter<Notification, DetailAdapter.DetailViewHolder>(TopicDiffCallback) {
val selected = mutableSetOf<String>() // Notification IDs
@@ -371,6 +371,12 @@ class DetailAdapter(private val activity: Activity, private val lifecycleScope:
}
private fun openFile(context: Context, attachment: Attachment): Boolean {
+ if (!canOpenAttachment(attachment)) {
+ Toast
+ .makeText(context, context.getString(R.string.detail_item_cannot_open_apk), Toast.LENGTH_LONG)
+ .show()
+ return true
+ }
Log.d(TAG, "Opening file ${attachment.contentUri}")
try {
val contentUri = Uri.parse(attachment.contentUri)
diff --git a/app/src/main/java/io/heckel/ntfy/util/Util.kt b/app/src/main/java/io/heckel/ntfy/util/Util.kt
index d987e41..078bf27 100644
--- a/app/src/main/java/io/heckel/ntfy/util/Util.kt
+++ b/app/src/main/java/io/heckel/ntfy/util/Util.kt
@@ -25,6 +25,7 @@ import android.view.Window
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatDelegate
+import io.heckel.ntfy.BuildConfig
import io.heckel.ntfy.R
import io.heckel.ntfy.db.*
import io.heckel.ntfy.msg.MESSAGE_ENCODING_BASE64
@@ -321,6 +322,8 @@ fun formatBytes(bytes: Long, decimals: Int = 1): String {
return java.lang.String.format("%.${decimals}f %cB", value / 1024.0, ci.current())
}
+const val androidAppMimeType = "application/vnd.android.package-archive"
+
fun mimeTypeToIconResource(mimeType: String?): Int {
return if (mimeType?.startsWith("image/") == true) {
R.drawable.ic_file_image_red_24dp
@@ -328,7 +331,7 @@ fun mimeTypeToIconResource(mimeType: String?): Int {
R.drawable.ic_file_video_orange_24dp
} else if (mimeType?.startsWith("audio/") == true) {
R.drawable.ic_file_audio_purple_24dp
- } else if (mimeType == "application/vnd.android.package-archive") {
+ } else if (mimeType == androidAppMimeType) {
R.drawable.ic_file_app_gray_24dp
} else {
R.drawable.ic_file_document_blue_24dp
@@ -339,6 +342,15 @@ fun supportedImage(mimeType: String?): Boolean {
return listOf("image/jpeg", "image/png").contains(mimeType)
}
+// Google Play doesn't allow us to install received .apk files anymore.
+// See https://github.com/binwiederhier/ntfy/issues/531
+fun canOpenAttachment(attachment: Attachment?): Boolean {
+ if (attachment?.type == androidAppMimeType && !BuildConfig.INSTALL_PACKAGES_AVAILABLE) {
+ return false
+ }
+ return true
+}
+
// Check if battery optimization is enabled, see https://stackoverflow.com/a/49098293/1440785
fun isIgnoringBatteryOptimizations(context: Context): Boolean {
val powerManager = context.applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager
@@ -443,6 +455,10 @@ fun String.readBitmapFromUriOrNull(context: Context): Bitmap? {
}
}
+fun Long.nullIfZero(): Long? {
+ return if (this == 0L) return null else this
+}
+
// TextWatcher that only implements the afterTextChanged method
class AfterChangedTextWatcher(val afterTextChangedFn: (s: Editable?) -> Unit) : TextWatcher {
override fun afterTextChanged(s: Editable?) {
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index 197ea54..fc6777e 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -327,4 +327,5 @@
<string name="detail_settings_about_header">Относно</string>
<string name="detail_settings_about_topic_url_title">Адрес на темата</string>
<string name="detail_settings_about_topic_url_copied_to_clipboard_message">Копирано в междинната памет</string>
+ <string name="main_menu_donate_title">Даряване 💸</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 646024f..d44c7f0 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -327,4 +327,5 @@
<string name="main_banner_websocket_button_enable_now">Povolit nyní</string>
<string name="main_banner_websocket_text">WebSockets jsou doporučenou metodou připojení k vašemu serveru, která může zlepšit zvýšit výdrž baterie, ale může vyžadovat <a href="https://ntfy.sh/docs/config/#nginxapache2caddy">další konfiguraci v proxy serveru</a>. Metodu připojení lze přepnout v Nastavení.</string>
<string name="add_dialog_base_urls_dropdown_choose">Zvolit URL služby</string>
+ <string name="main_menu_donate_title">Přispět 💸</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 029f961..2d4f08c 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -327,4 +327,5 @@
<string name="detail_settings_about_topic_url_title">Themen-URL</string>
<string name="detail_settings_about_header">Über</string>
<string name="detail_settings_about_topic_url_copied_to_clipboard_message">In Zwischenablage kopiert</string>
+ <string name="main_menu_donate_title">Spenden 💸</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 436b898..56ad002 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -327,4 +327,5 @@
<string name="add_dialog_base_urls_dropdown_clear">Borrar la URL del servicio</string>
<string name="main_banner_websocket_text">Cambiar a WebSockets es la forma recomendada para conectarse a su servidor, y podría mejorar la vida de la batería, pero puede requerir <a href="https://ntfy.sh/docs/config/#nginxapache2caddy">configuración adicional en su proxy</a>. Esto se puede cambiar en la Configuración.</string>
<string name="main_banner_websocket_button_enable_now">Habilitar ahora</string>
+ <string name="main_menu_donate_title">Donar 💸</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index 4e2b23c..fda4855 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -6,7 +6,7 @@
<string name="channel_notifications_min_name">Notifikasi (prioritas min)</string>
<string name="channel_notifications_low_name">Notifikasi (prioritas rendah)</string>
<string name="channel_subscriber_service_name">Layanan Langganan</string>
- <string name="channel_subscriber_notification_title">Mendengarkan untuk notifikasi masuk</string>
+ <string name="channel_subscriber_notification_title">Mendengarkan notifikasi masuk</string>
<string name="channel_subscriber_notification_instant_text">Berlangganan ke topik pengiriman instan</string>
<string name="channel_subscriber_notification_instant_text_one">Berlangganan ke satu topik pengiriman instan</string>
<string name="channel_subscriber_notification_instant_text_two">Berlangganan ke dua topik pengiriman instan</string>
@@ -23,7 +23,7 @@
<string name="main_menu_notifications_disabled_forever">Notifikasi dibisukan</string>
<string name="main_menu_notifications_disabled_until">Notifikasi dibisukan sampai %1$s</string>
<string name="main_menu_settings_title">Pengaturan</string>
- <string name="main_menu_report_bug_title">Laporkan sebuah bug</string>
+ <string name="main_menu_report_bug_title">Laporkan kutu</string>
<string name="main_menu_docs_title">Baca dokumentasi</string>
<string name="main_menu_rate_title">Beri nilai aplikasi ⭐</string>
<string name="main_action_mode_menu_unsubscribe">Batalkan langganan</string>
@@ -327,4 +327,5 @@
<string name="detail_settings_about_topic_url_copied_to_clipboard_message">Disalin ke papan klip</string>
<string name="detail_settings_about_header">Tentang</string>
<string name="detail_settings_about_topic_url_title">URL Topik</string>
+ <string name="main_menu_donate_title">Donasi 💸</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index 8fb0788..dabff91 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -15,7 +15,7 @@
<string name="main_action_mode_delete_dialog_permanently_delete">מחק/י לצמיתות</string>
<string name="main_action_mode_delete_dialog_cancel">ביטול</string>
<string name="main_item_status_text_one">התראת %1$d</string>
- <string name="main_item_status_text_not_one">התראות %1$d</string>
+ <string name="main_item_status_text_not_one">%1$d התראות</string>
<string name="main_item_date_yesterday">אתמול</string>
<string name="main_add_button_description">הוספת רישום</string>
<string name="main_no_subscriptions_text">נראה שלא נרשמת לאף נושא עדיין.</string>
@@ -39,4 +39,20 @@
<string name="main_item_status_reconnecting">מתחבר מחדש…</string>
<string name="main_how_to_intro">לחצ\\י על + על מנת ליצור או להירשם אל מול נושא מסוים. לאחר מכן תקבל\\י התראות במכשירך כשתשלח\\י התראות דרך PUT או POST.</string>
<string name="main_how_to_link">הוראות מפורטות זמינות ב-ntfy.sh, ובדוקומנטציה.</string>
-</resources>
+ <string name="main_menu_donate_title">תרום 💸</string>
+ <string name="channel_subscriber_notification_instant_text_two">רשום לשני נושאים במשלוח מהיר</string>
+ <string name="channel_subscriber_notification_instant_text_three">רשום לשלושה נושאים במשלוח מהיר</string>
+ <string name="channel_subscriber_notification_instant_text">רשום לנושאים במשלוח מהיר</string>
+ <string name="channel_subscriber_notification_instant_text_one">רשום לנושא אחד במשלוח מהיר</string>
+ <string name="channel_subscriber_notification_instant_text_four">רשום לארבעה נושאים במשלוח מהיר</string>
+ <string name="channel_subscriber_notification_instant_text_five">רשום לחמישה נושאים במשלוח מהיר</string>
+ <string name="channel_subscriber_notification_instant_text_six">רשום לשישה נושאים במשלוח מהיר</string>
+ <string name="channel_subscriber_notification_instant_text_more">רשום ל%1$d נושאים במשלוח מהיר</string>
+ <string name="channel_subscriber_notification_noinstant_text">רשום לנושאים</string>
+ <string name="channel_subscriber_notification_noinstant_text_one">רשום לנושא אחד</string>
+ <string name="channel_subscriber_notification_noinstant_text_two">רשום לשני נושאים</string>
+ <string name="channel_subscriber_notification_noinstant_text_three">רשום לשלושה נושאים</string>
+ <string name="channel_subscriber_notification_noinstant_text_four">רשום לארבעה נושאים</string>
+ <string name="channel_subscriber_notification_noinstant_text_five">רשום לחמישה נושאים</string>
+ <string name="channel_subscriber_notification_noinstant_text_six">רשום לשישה נושאים</string>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 0731eb9..6d05912 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -327,4 +327,5 @@
<string name="detail_settings_about_header">About</string>
<string name="detail_settings_about_topic_url_title">トピックのURL</string>
<string name="detail_settings_about_topic_url_copied_to_clipboard_message">クリップボードにコピーしました</string>
+ <string name="main_menu_donate_title">寄付する💸</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index bc95a63..10983c4 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -77,7 +77,7 @@
<string name="detail_item_tags">Tags: %1$s</string>
<string name="detail_item_snack_deleted">Notificação deletada</string>
<string name="detail_item_snack_undo">Desfazer</string>
- <string name="detail_item_menu_download">Fazer download do arquivo</string>
+ <string name="detail_item_menu_download">Baixar arquivo</string>
<string name="detail_item_menu_cancel">Cancelar o download</string>
<string name="detail_item_cannot_open">Não foi possível abrir o anexo: %1$s</string>
<string name="detail_item_cannot_open_not_found">Não foi possível abrir o anexo: O arquivo pode ter sido deletado, ou não existe app instalado que consiga abrir o arquivo.</string>
@@ -258,4 +258,74 @@
<string name="settings_general_dark_mode_summary_light">Modo claro</string>
<string name="settings_general_dark_mode_title">Modo escuro</string>
<string name="settings_general_dark_mode_summary_system">Usar o padrão do sistema</string>
+ <string name="main_menu_donate_title">Doar 💸</string>
+ <string name="settings_backup_restore_restore_failed">Recuperação falhou %1$s</string>
+ <string name="settings_advanced_header">Avançado</string>
+ <string name="settings_advanced_broadcast_title">Messagens de broadcast</string>
+ <string name="settings_advanced_broadcast_summary_disabled">Os aplicativos não podem receber notificações por broadcast</string>
+ <string name="settings_advanced_broadcast_summary_enabled">Os aplicativos já podem receber notificações por broadcast</string>
+ <string name="settings_advanced_export_logs_entry_copy_original">Copiar para área de transferência</string>
+ <string name="settings_advanced_export_logs_entry_copy_scrubbed">Copiar para área de transferência (censurado)</string>
+ <string name="settings_advanced_export_logs_entry_upload_original">Carregar e copiar link</string>
+ <string name="settings_advanced_export_logs_uploading">Carregando logs …</string>
+ <string name="settings_advanced_export_logs_copied_url">Logs enviados e URL copiada</string>
+ <string name="settings_advanced_connection_protocol_summary_jsonhttp">Use um stream de JSON através de HTTP para se conectar ao servidor. Este método foi testado na pratica, mas pode consumir mais bateria.</string>
+ <string name="settings_about_version_format">ntfy %1$s (%2$s)</string>
+ <string name="detail_settings_notifications_instant_summary_off">As notificações são entregues utilizando o Firebase. A entrega pode atrasar, mas consome menos bateria.</string>
+ <string name="detail_settings_appearance_header">Aparência</string>
+ <string name="detail_settings_appearance_icon_set_title">Ícone de assinatura</string>
+ <string name="detail_settings_appearance_icon_set_summary">Defina um ícone para ser exibido nas notificações</string>
+ <string name="detail_settings_appearance_icon_remove_title">Ícone de assinatura (toque para remover)</string>
+ <string name="detail_settings_appearance_display_name_message">Defina um nome de exibição personalizado para esta assinatura. Deixe em branco para o padrão (%1$s).</string>
+ <string name="detail_settings_appearance_display_name_default_summary">%1$s (padrão)</string>
+ <string name="detail_settings_global_setting_title">Usar configuração global</string>
+ <string name="detail_settings_global_setting_suffix">usar configuração global</string>
+ <string name="detail_settings_about_header">Sobre</string>
+ <string name="detail_settings_about_topic_url_title">URL do tópico</string>
+ <string name="detail_settings_about_topic_url_copied_to_clipboard_message">Copiado para a área de transferência</string>
+ <string name="user_dialog_title_add">Adicionar usuário</string>
+ <string name="settings_backup_restore_restore_title">Recuperar do arquivo</string>
+ <string name="settings_backup_restore_restore_summary">Importar configurações, notificações e usuários</string>
+ <string name="settings_backup_restore_restore_successful">Restauração concluída</string>
+ <string name="settings_advanced_record_logs_summary_enabled">Registros (ate 1000 registros) para o dispositivo …</string>
+ <string name="settings_advanced_record_logs_summary_disabled">Ative o log para que seja possível compartilhar mais tarde os registros para diagnostico.</string>
+ <string name="settings_advanced_record_logs_title">Logs de registros</string>
+ <string name="settings_advanced_export_logs_title">Copiar/carregar logs</string>
+ <string name="settings_advanced_export_logs_summary">Copie os logs para a área de transferência ou faça o upload para nopaste.net (propriedade do autor do ntfy). Hostnames e tópicos podem ser censurados, as notificações não.</string>
+ <string name="settings_advanced_export_logs_entry_upload_scrubbed">Carregar e copiar link (censurado)</string>
+ <string name="settings_advanced_export_logs_copied_logs">Logs copiado para área de transferência</string>
+ <string name="settings_advanced_export_logs_scrub_dialog_text">Esses tópicos/hostnames foram substituídos por nomes de frutas, então você pode compartilhar o log sem se preocupar:
+\n
+\n%1$s
+\n
+\nAs senhas são retiradas e não são listadas aqui.</string>
+ <string name="settings_advanced_export_logs_error_uploading">Não foi possível carregar os logs: %1$s</string>
+ <string name="settings_advanced_export_logs_scrub_dialog_button_ok">OK</string>
+ <string name="settings_advanced_export_logs_scrub_dialog_empty">Nenhum tópico/hostname foi editado. Talvez você não tenha nenhuma assinatura\?</string>
+ <string name="settings_advanced_clear_logs_title">Limpar logs</string>
+ <string name="settings_advanced_clear_logs_deleted_toast">Logs excluídos</string>
+ <string name="settings_advanced_connection_protocol_title">Protocolo de conexão</string>
+ <string name="settings_advanced_clear_logs_summary">Exclua os logs gravados anteriormente e comece de novo</string>
+ <string name="settings_advanced_connection_protocol_summary_ws">Use WebSockets para se conectar ao servidor. Este é o método recomendado, mas pode exigir configuração adicional em seu proxy.</string>
+ <string name="settings_advanced_connection_protocol_entry_jsonhttp">Stream JSON através de HTTP</string>
+ <string name="settings_about_header">Sobre</string>
+ <string name="settings_about_version_title">Versão</string>
+ <string name="settings_advanced_connection_protocol_entry_ws">Web Sockets</string>
+ <string name="detail_settings_notifications_instant_title">Entrega instantânea</string>
+ <string name="detail_settings_notifications_instant_summary_on">As notificações são entregues instantaneamente. Requer um serviço que roda em primeiro plano e consome mais bateria.</string>
+ <string name="settings_about_version_copied_to_clipboard_message">Copiado para área de transferência</string>
+ <string name="detail_settings_appearance_icon_error_saving">Não foi possível salvar o ícone: %1$s</string>
+ <string name="detail_settings_appearance_icon_remove_summary">Ícone exibido nas notificações deste tópico</string>
+ <string name="detail_settings_appearance_display_name_title">Nome de exibição</string>
+ <string name="user_dialog_description_edit">Você pode editar o nome de usuário/senha do usuário selecionado ou excluí-lo.</string>
+ <string name="user_dialog_base_url_hint">URL do serviço</string>
+ <string name="user_dialog_title_edit">Editar usuário</string>
+ <string name="user_dialog_description_add">Você pode adicionar um usuário aqui. Todos os tópicos para o servidor fornecido usarão esse usuário.</string>
+ <string name="user_dialog_username_hint">Nome do usuário</string>
+ <string name="user_dialog_password_hint_add">Senha</string>
+ <string name="user_dialog_password_hint_edit">Senha (se deixada em branco não será alterada)</string>
+ <string name="user_dialog_button_cancel">Cancelar</string>
+ <string name="user_dialog_button_add">Adicionar usuário</string>
+ <string name="user_dialog_button_delete">Deletar usuário</string>
+ <string name="user_dialog_button_save">Salvar</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index c9e4078..24ede8f 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -99,4 +99,28 @@
<string name="add_dialog_login_username_hint">Användarnamn</string>
<string name="add_dialog_login_password_hint">Lösenord</string>
<string name="detail_test_message_error_unauthorized_anon">Kan inte skicka meddelande: Anonym publicering är inte tillåten.</string>
+ <string name="detail_item_snack_deleted">Notifikation borttagen</string>
+ <string name="detail_item_menu_copy_url_copied">URL kopierad till urklipp</string>
+ <string name="detail_item_menu_copy_contents">Kopiera notifikation</string>
+ <string name="detail_item_menu_copy_contents_copied">Notifikation kopierad till urklipp</string>
+ <string name="detail_item_cannot_open_url">Kan inte öppna URL: %1$s</string>
+ <string name="detail_menu_clear">Rensa alla notifikationer</string>
+ <string name="detail_menu_test">Skicka testnotifikation</string>
+ <string name="detail_action_mode_menu_copy">Kopiera</string>
+ <string name="detail_action_mode_menu_delete">Ta bort</string>
+ <string name="detail_action_mode_delete_dialog_permanently_delete">Ta bort permanent</string>
+ <string name="detail_action_mode_delete_dialog_cancel">Avbryt</string>
+ <string name="detail_settings_title">Prenumerationsinställningar</string>
+ <string name="share_title">Dela</string>
+ <string name="share_menu_send">Dela</string>
+ <string name="main_menu_donate_title">Donera 💸</string>
+ <string name="detail_item_snack_undo">Ångra</string>
+ <string name="detail_item_menu_open">Öppna fil</string>
+ <string name="detail_item_menu_delete">Ta bort fil</string>
+ <string name="detail_item_menu_download">Ladda ner fil</string>
+ <string name="detail_item_menu_cancel">Avbryt nedladdning</string>
+ <string name="detail_item_menu_save_file">Spara fil</string>
+ <string name="detail_item_menu_copy_url">Kopiera URL</string>
+ <string name="detail_item_download_info_download_failed">nedladdning misslyckad</string>
+ <string name="detail_menu_settings">Prenumerationsinställningar</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index dbf2453..393ec47 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -327,4 +327,5 @@
<string name="detail_settings_about_header">Hakkında</string>
<string name="detail_settings_about_topic_url_title">Konu URL\'si</string>
<string name="detail_settings_about_topic_url_copied_to_clipboard_message">Panoya kopyalandı</string>
+ <string name="main_menu_donate_title">Bağış yap 💸</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index f6efd9c..c0012bc 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -327,4 +327,5 @@
<string name="detail_settings_about_header">关于</string>
<string name="detail_settings_about_topic_url_title">话题 URL</string>
<string name="detail_settings_about_topic_url_copied_to_clipboard_message">已复制到剪贴板</string>
+ <string name="main_menu_donate_title">捐赠 💸</string>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 77089bc..82f1d79 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -153,6 +153,7 @@
<string name="detail_item_cannot_open">Cannot open attachment: %1$s</string>
<string name="detail_item_cannot_open_not_found">Cannot open attachment: The file may have been deleted, or no installed app can open the file.</string>
<string name="detail_item_cannot_open_url">Cannot open URL: %1$s</string>
+ <string name="detail_item_cannot_open_apk">Apps cannot be installed anymore. Download via browser instead. See issue #531 for details.</string>
<string name="detail_item_cannot_save">Cannot save attachment: %1$s</string>
<string name="detail_item_cannot_delete">Cannot delete attachment: %1$s</string>
<string name="detail_item_download_failed">Could not download attachment: %1$s</string>
diff --git a/app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt b/app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt
index 1a367ab..830b92d 100644
--- a/app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt
+++ b/app/src/play/java/io/heckel/ntfy/firebase/FirebaseService.kt
@@ -14,6 +14,7 @@ import io.heckel.ntfy.msg.ApiService
import io.heckel.ntfy.msg.NotificationDispatcher
import io.heckel.ntfy.msg.NotificationParser
import io.heckel.ntfy.service.SubscriberService
+import io.heckel.ntfy.util.nullIfZero
import io.heckel.ntfy.util.toPriority
import io.heckel.ntfy.util.topicShortUrl
import io.heckel.ntfy.work.PollWorker
@@ -94,8 +95,8 @@ class FirebaseService : FirebaseMessagingService() {
val encoding = data["encoding"]
val attachmentName = data["attachment_name"] ?: "attachment.bin"
val attachmentType = data["attachment_type"]
- val attachmentSize = data["attachment_size"]?.toLongOrNull()
- val attachmentExpires = data["attachment_expires"]?.toLongOrNull()
+ val attachmentSize = data["attachment_size"]?.toLongOrNull()?.nullIfZero()
+ val attachmentExpires = data["attachment_expires"]?.toLongOrNull()?.nullIfZero()
val attachmentUrl = data["attachment_url"]
val truncated = (data["truncated"] ?: "") == "1"
if (id == null || topic == null || message == null || timestamp == null) {
diff --git a/fastlane/metadata/android/en-US/changelog/29.txt b/fastlane/metadata/android/en-US/changelog/31.txt
index 805d904..c807459 100644
--- a/fastlane/metadata/android/en-US/changelog/29.txt
+++ b/fastlane/metadata/android/en-US/changelog/31.txt
@@ -12,6 +12,8 @@ Bug fixes + maintenance:
* Fix topics do not re-subscribe to Firebase after restoring from backup (#511)
* Fix crashes from large images (#474, thanks to @daedric7 for reporting)
* Fix notification click opens wrong subscription (#261, thanks to @SMAW for reporting)
+* Fix Firebase-only "link expired" issue (#529)
+* Remove "Install .apk" feature in Google Play variant due to policy change (#531)
* Add donate button (no ticket)
Additional translations: