diff --git a/build.gradle b/build.gradle
index c5c78b6ab8d5ab8cc925621b3c83a45a0654e776..5dc6e2fd5d013deb734ed141b7af772b8c53cee6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -49,7 +49,7 @@ dependencies {
implementation 'androidx.viewpager:viewpager:1.0.0'
- playstoreImplementation('com.google.firebase:firebase-messaging:23.3.0') {
+ playstoreImplementation('com.google.firebase:firebase-messaging:23.3.1') {
exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
@@ -93,7 +93,7 @@ dependencies {
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
- implementation "com.squareup.okhttp3:okhttp:4.10.0"
+ implementation "com.squareup.okhttp3:okhttp:4.11.0"
implementation 'com.google.guava:guava:32.1.3-android'
implementation 'io.michaelrocks:libphonenumber-android:8.13.17'
@@ -106,7 +106,7 @@ dependencies {
implementation 'com.github.singpolyma:TokenAutoComplete:bfa93780e0'
implementation 'com.github.singpolyma:Better-Link-Movement-Method:4df081e1e4'
implementation 'com.github.singpolyma:android-identicons:3361281bd4'
- implementation 'im.conversations.webrtc:webrtc-android:117.1.0'
+ implementation 'im.conversations.webrtc:webrtc-android:119.0.0'
implementation 'com.github.woltapp:blurhash:master'
implementation 'com.caverock:androidsvg-aar:1.4'
implementation 'org.tomlj:tomlj:1.1.0'
@@ -123,7 +123,7 @@ ext {
android {
namespace 'eu.siacs.conversations'
- compileSdkVersion 34
+ compileSdk 34
defaultConfig {
minSdkVersion 21
@@ -141,6 +141,9 @@ android {
abi {
universalApk true
enable true
+ reset()
+ //noinspection ChromeOsAbiSupport
+ include project.ext.abiCodes.keySet() as String[]
}
}
@@ -158,7 +161,8 @@ android {
targetCompatibility JavaVersion.VERSION_17
}
- flavorDimensions("mode", "distribution")
+ flavorDimensions += "mode"
+ flavorDimensions += "distribution"
productFlavors {
@@ -295,7 +299,7 @@ android {
buildConfig true
}
- android.applicationVariants.all { variant ->
+ android.applicationVariants.configureEach { variant ->
variant.outputs.each { output ->
def baseAbiVersionCode = project.ext.abiCodes.get(output.getFilter(com.android.build.OutputFile.ABI))
if (baseAbiVersionCode != null) {
diff --git a/fastlane/metadata/android/gl-ES/changelogs/353.txt b/fastlane/metadata/android/gl-ES/changelogs/353.txt
index 63b829a805b5815fd806f397d60951d58e0a8ee8..388f9ae915cf278e72a7e63e3b7a5d20da81575f 100644
--- a/fastlane/metadata/android/gl-ES/changelogs/353.txt
+++ b/fastlane/metadata/android/gl-ES/changelogs/353.txt
@@ -1,4 +1,4 @@
-* let users set their own nick name
-* resume download of OMEMO encrypted files
-* Channels now use '#' as symbol in avatar
-* Quicksy uses 'always' as OMEMO encryption default (hides lock icon)
+* permitir que as usuarias elixan o seu propio alcume
+* retomar a descarga de ficheiros cifrados con OMEMO
+* agora as Canles usan '#' como símbolo no avatar
+* Quicksy establece 'sempre' para a cifraxe OMEMO por defecto (agocha a icona do cadeado)
diff --git a/fastlane/metadata/android/gl-ES/changelogs/360.txt b/fastlane/metadata/android/gl-ES/changelogs/360.txt
index 87b92f0339c3bf13debb2a379f5bb23d9514e21c..f31c6d51c36d5cbcc536185542d204014f24580d 100644
--- a/fastlane/metadata/android/gl-ES/changelogs/360.txt
+++ b/fastlane/metadata/android/gl-ES/changelogs/360.txt
@@ -1 +1 @@
-* Support for ?register and ?register;preauth XMPP uri parameters
+* Soporte para os parámetros ?register e ?register;preauth da uri XMPP
diff --git a/fastlane/metadata/android/gl-ES/changelogs/362.txt b/fastlane/metadata/android/gl-ES/changelogs/362.txt
index f4bbc2ab12c20689eddff86bc7a015f859a827b9..e357ba94c7bd21cc4fe2fffbbf092c754b4d71de 100644
--- a/fastlane/metadata/android/gl-ES/changelogs/362.txt
+++ b/fastlane/metadata/android/gl-ES/changelogs/362.txt
@@ -1 +1 @@
-* Support automatic theme switching on Android 10
+* Soporte para o cambio automático de decorado en Android 10
diff --git a/fastlane/metadata/android/gl-ES/changelogs/364.txt b/fastlane/metadata/android/gl-ES/changelogs/364.txt
index ed2c80678b53cd8f43be1a848ce82ddae251bc17..338355cbf9ac85aefe4cb8163194eea9be3447bd 100644
--- a/fastlane/metadata/android/gl-ES/changelogs/364.txt
+++ b/fastlane/metadata/android/gl-ES/changelogs/364.txt
@@ -1,2 +1,2 @@
-* Provide PDF preview on Android 5+
-* Use 12 byte IVs for OMEMO
+* Proporciona vista previa dos PDF en Android 5+
+* Usa 12 byte IVs con OMEMO
diff --git a/fastlane/metadata/android/gl-ES/changelogs/367.txt b/fastlane/metadata/android/gl-ES/changelogs/367.txt
index 2f42bd95f0437ba06548c9ca1f96b2ecef37fbe8..9b96da762ad5fba23067c7218add157a41bc38e1 100644
--- a/fastlane/metadata/android/gl-ES/changelogs/367.txt
+++ b/fastlane/metadata/android/gl-ES/changelogs/367.txt
@@ -1,2 +1,2 @@
-* Fix avatar selection on some Android 10 devices
-* Fix file transfer for larger files
+* Arranxo da selección do avatar en dispositivos Android 10
+* Arranxo da transferencia de ficheiros moi grandes
diff --git a/fastlane/metadata/android/gl-ES/changelogs/379.txt b/fastlane/metadata/android/gl-ES/changelogs/379.txt
index a99adee16ff86f6e22e2783b76a2e6c020c6ca31..495c4da0c4240b997ee507766da0bef4d964046e 100644
--- a/fastlane/metadata/android/gl-ES/changelogs/379.txt
+++ b/fastlane/metadata/android/gl-ES/changelogs/379.txt
@@ -1 +1 @@
-* Audio/Video calls (Requires server support in form of STUN and TURN servers discoverable via XEP-0215)
+* Chamadas de Audio/Video (Require soporte no servidor para que os servidores STUN e TURN sexan accesibles vía XEP-0215)
diff --git a/fastlane/metadata/android/gl-ES/changelogs/42038.txt b/fastlane/metadata/android/gl-ES/changelogs/42038.txt
index da3c42237f1a1b58be3ee50209e60a08c3c684b9..71373032f01dfdf97444bc780c6e36fb6208763a 100644
--- a/fastlane/metadata/android/gl-ES/changelogs/42038.txt
+++ b/fastlane/metadata/android/gl-ES/changelogs/42038.txt
@@ -1,2 +1,2 @@
-* Minor bug fixes
-* Restore ability to call out via JMP and other services (Playstore version)
+* Arranxos menores
+* Restablecida a posibilidade de chamar vía JMP e outros servizos (versión Playstore)
diff --git a/fastlane/metadata/android/gl-ES/changelogs/42041.txt b/fastlane/metadata/android/gl-ES/changelogs/42041.txt
index 302e9719b1b0c4db9306a087930f7e0911b7948c..37079b4f7f5a8e1e7a12ed3c47166668f10d6c69 100644
--- a/fastlane/metadata/android/gl-ES/changelogs/42041.txt
+++ b/fastlane/metadata/android/gl-ES/changelogs/42041.txt
@@ -1,5 +1,5 @@
-* Implement Extensible SASL Profile, Bind 2.0 and Fast for faster reconnects
-* Implement Channel Binding
-* Add ability to switch from audio call to video call
-* Add ability to delete own avatar
-* Add notification for missed calls
+* Implementamos Extensible SASL Profile, Bind 2.0 e Fast para reconectar máis rápidamente
+* Implementamos Channel Binding
+* Engadimos a posibilidade de pasar de chamada de audio a chamada de vídeo
+* Podes eliminar o teu propio avatar
+* Engadida notificación de chamada perdida
diff --git a/fastlane/metadata/android/gl-ES/changelogs/4207704.txt b/fastlane/metadata/android/gl-ES/changelogs/4207704.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f0955243822f5c7d77ce08f1c99ac4a1b23b5ccb
--- /dev/null
+++ b/fastlane/metadata/android/gl-ES/changelogs/4207704.txt
@@ -0,0 +1,3 @@
+* Soporte para Private DNS (DNS sobre TLS)
+* Soporte para decorar a icona no iniciador
+* Arranxo dun problema pouco común de permisos ao compartir ficheiros en Android 11+
diff --git a/fastlane/metadata/android/zh-TW/changelogs/349.txt b/fastlane/metadata/android/zh-TW/changelogs/349.txt
new file mode 100644
index 0000000000000000000000000000000000000000..af513671463bfe2175f29f93a30ba903fc8abcf3
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/349.txt
@@ -0,0 +1,4 @@
+* 引入專家設置,以在本地伺服器上執行通道發現,而非 search.jabber.network
+* 默認啟用傳送檢查標記,並刪除相應設置
+* 默認啟用「發送按鈕顯示狀態」,並刪除相應設置
+* 將備份和前景服務設置移至主畫面
diff --git a/fastlane/metadata/android/zh-TW/changelogs/351.txt b/fastlane/metadata/android/zh-TW/changelogs/351.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bf292b34ab8e70761134c4f1d5f4fc4f55593d6f
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/351.txt
@@ -0,0 +1,3 @@
+* 修復 Jingle IBB 檔案傳輸問題
+* 修復重複更正填充數據庫的問題
+* 切換至 Last Message Correction 版本 1.1
diff --git a/fastlane/metadata/android/zh-TW/changelogs/353.txt b/fastlane/metadata/android/zh-TW/changelogs/353.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5aeb2bb4226820b97abe66b04a08c8c4c76ff386
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/353.txt
@@ -0,0 +1,4 @@
+* 允許用戶設置自己的暱稱
+* 恢復下載 OMEMO 加密文件
+* 頻道頭像中現在使用 '#' 符號
+* Quicksy 將「總是」用作 OMEMO 加密的默認值(隱藏鎖定圖標)
diff --git a/fastlane/metadata/android/zh-TW/changelogs/360.txt b/fastlane/metadata/android/zh-TW/changelogs/360.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6fff27feacc815a4e21f3fc69d084a1731d91d16
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/360.txt
@@ -0,0 +1 @@
+* 支援 ?register 和 ?register;preauth XMPP URI 參數
diff --git a/fastlane/metadata/android/zh-TW/changelogs/362.txt b/fastlane/metadata/android/zh-TW/changelogs/362.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7919d5550684c461b533b0a008c864010b37b26b
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/362.txt
@@ -0,0 +1 @@
+在 Android 10 上支援自動主題切換
diff --git a/fastlane/metadata/android/zh-TW/changelogs/364.txt b/fastlane/metadata/android/zh-TW/changelogs/364.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b46a5c3e22805022f54f5ff8bedc08cad2df2ca4
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/364.txt
@@ -0,0 +1,2 @@
+* 在 Android 5+ 上提供 PDF 預覽
+* 在 OMEMO 中使用 12 字節的 IV
diff --git a/fastlane/metadata/android/zh-TW/changelogs/367.txt b/fastlane/metadata/android/zh-TW/changelogs/367.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6ace96e8b51e732f0d6620a7fc14651df40684e1
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/367.txt
@@ -0,0 +1,2 @@
+* 修復在某些 Android 10 設備上的頭像選擇問題
+* 修復傳輸較大文件的問題
diff --git a/fastlane/metadata/android/zh-TW/changelogs/379.txt b/fastlane/metadata/android/zh-TW/changelogs/379.txt
new file mode 100644
index 0000000000000000000000000000000000000000..aae54ffb2469366c6b7b9b70d87f76301b195d7d
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/379.txt
@@ -0,0 +1 @@
+* 音訊/視訊通話(需要伺服器支援,以 STUN 和 TURN 伺服器的形式通過 XEP-0215 可發現)
diff --git a/fastlane/metadata/android/zh-TW/changelogs/381.txt b/fastlane/metadata/android/zh-TW/changelogs/381.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c714a43f2d15e1efc120b426be04b46437832c10
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/381.txt
@@ -0,0 +1,2 @@
+* 語音通話的聲音回饋(撥號、通話開始、通話結束)
+* 修復重試失敗的視訊通話問題
diff --git a/fastlane/metadata/android/zh-TW/changelogs/382.txt b/fastlane/metadata/android/zh-TW/changelogs/382.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b026a4707100d47cce436eaa89a93f16e14af9e9
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/382.txt
@@ -0,0 +1,2 @@
+* 新增在視訊通話中切換攝像頭的按鈕
+* 修復平板電腦上的語音通話問題
diff --git a/fastlane/metadata/android/zh-TW/changelogs/383.txt b/fastlane/metadata/android/zh-TW/changelogs/383.txt
new file mode 100644
index 0000000000000000000000000000000000000000..027684eedeff64cf3f5ccd035e283d6e9b41b8c0
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/383.txt
@@ -0,0 +1,3 @@
+* 將通話圖標移至左側,以保持其他工具欄圖標的一致位置
+* 在語音通話期間顯示通話持續時間
+* 視訊/音訊通話的分開處理(同時互打電話的兩人的處理)
diff --git a/fastlane/metadata/android/zh-TW/changelogs/387.txt b/fastlane/metadata/android/zh-TW/changelogs/387.txt
new file mode 100644
index 0000000000000000000000000000000000000000..38f22df14f2caeff87781e02d682189d777e66d5
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/387.txt
@@ -0,0 +1,2 @@
+* 重做憑證登錄的使用者介面
+* 新增置頂聊天的功能(加入最愛)
diff --git a/fastlane/metadata/android/zh-TW/changelogs/388.txt b/fastlane/metadata/android/zh-TW/changelogs/388.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3ea17c53f76c7b331a13bb61c2b68629fb9e0a00
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/388.txt
@@ -0,0 +1,3 @@
+* 在某些設備上減少通話中的回音
+* 修復當密碼包含特殊字符時的登錄問題
+* 在視訊通話期間在揚聲器上播放撥號和忙音
diff --git a/fastlane/metadata/android/zh-TW/changelogs/390.txt b/fastlane/metadata/android/zh-TW/changelogs/390.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8452f3854e58b707b5e31852e347e502dfb8dc8f
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/390.txt
@@ -0,0 +1 @@
+* 在被呼叫方忙線時提供錄製語音訊息的選項
diff --git a/fastlane/metadata/android/zh-TW/changelogs/393.txt b/fastlane/metadata/android/zh-TW/changelogs/393.txt
new file mode 100644
index 0000000000000000000000000000000000000000..855909e6e1dde11ffcace4bac51df3e85ac44ec1
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/393.txt
@@ -0,0 +1,3 @@
+* 如果音訊/視訊通話失敗,顯示求助按鈕
+* 修復一些令人困擾的崩潰問題
+* 修復使用裸 JIDs 的 Jingle 連接(檔案傳輸 + 通話)
diff --git a/fastlane/metadata/android/zh-TW/changelogs/394.txt b/fastlane/metadata/android/zh-TW/changelogs/394.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4e27642492d5a8a2aca23b4e9105fd269e56295c
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/394.txt
@@ -0,0 +1,2 @@
+* 修復在某些情況下通知未顯示的問題
+* 修復與音訊/視訊通話相關的相容性問題和崩潰
diff --git a/fastlane/metadata/android/zh-TW/changelogs/395.txt b/fastlane/metadata/android/zh-TW/changelogs/395.txt
new file mode 100644
index 0000000000000000000000000000000000000000..09ebd7faafbf985168f7eeb2498a71d415ecc42f
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/395.txt
@@ -0,0 +1,3 @@
+* 在音訊通話畫面中新增「返回聊天」選項
+* 改進鍵盤快捷鍵
+* 修復錯誤
diff --git a/fastlane/metadata/android/zh-TW/changelogs/398.txt b/fastlane/metadata/android/zh-TW/changelogs/398.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a728131a91a3e5e2feb52a191c72ed8d9187c0f3
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/398.txt
@@ -0,0 +1,4 @@
+* 搜尋個別對話
+* 如果消息未能傳遞,通知使用者
+* 在重新啟動後保留來自 Quicksy 使用者的顯示名稱(暱稱)
+* 如果需要,新增從通知啟動 Orbot(洋蔥路由器)的按鈕
diff --git a/fastlane/metadata/android/zh-TW/changelogs/401.txt b/fastlane/metadata/android/zh-TW/changelogs/401.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b24fec2cb3c7b59ae7165f6318471f0b0fed757e
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/401.txt
@@ -0,0 +1,2 @@
+* 修復在 Android <= 5 上的搜尋問題
+* 優化內存消耗
diff --git a/fastlane/metadata/android/zh-TW/changelogs/402.txt b/fastlane/metadata/android/zh-TW/changelogs/402.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1897128a293499a56f32021766ebd2cf33ec658e
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/402.txt
@@ -0,0 +1,3 @@
+* 在支援的伺服器上提供簡易邀請生成
+* 顯示從 Movim 發送的 GIF
+* 將頭像存儲在快取中
diff --git a/fastlane/metadata/android/zh-TW/changelogs/403.txt b/fastlane/metadata/android/zh-TW/changelogs/403.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e3e5e57f1e4fd2b431f8800a246630fde4b62571
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/403.txt
@@ -0,0 +1,3 @@
+* 修復使用不同 SCRAM 機制的不同帳戶時的連接問題
+* 新增對 SCRAM-SHA-512 的支援
+* 允許自己與自己進行 P2P(Jingle)文件傳輸
diff --git a/fastlane/metadata/android/zh-TW/changelogs/404.txt b/fastlane/metadata/android/zh-TW/changelogs/404.txt
new file mode 100644
index 0000000000000000000000000000000000000000..60d555681f49fa14781cfa1c4856ce44dda24c0e
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/404.txt
@@ -0,0 +1 @@
+* 對音訊/視訊通話進行了小幅度的穩定性改進
diff --git a/fastlane/metadata/android/zh-TW/changelogs/405.txt b/fastlane/metadata/android/zh-TW/changelogs/405.txt
new file mode 100644
index 0000000000000000000000000000000000000000..189b5a041bf8ef2dc675b581528ad1d9be8b7100
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/405.txt
@@ -0,0 +1 @@
+* Quicksy:自動接收驗證簡訊
diff --git a/fastlane/metadata/android/zh-TW/changelogs/407.txt b/fastlane/metadata/android/zh-TW/changelogs/407.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bcc3aca98d51d4fd181251030a3d69b224814055
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/407.txt
@@ -0,0 +1,3 @@
+* 如果離線的聯絡人之前宣告支援,則顯示通話按鈕
+* 當通話已連接時,返回按鈕不再結束通話
+* 錯誤修復
diff --git a/fastlane/metadata/android/zh-TW/changelogs/42000.txt b/fastlane/metadata/android/zh-TW/changelogs/42000.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3e492327121b589b7e411066f8501a854172b50a
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/42000.txt
@@ -0,0 +1,4 @@
+* 可選擇來電鈴聲
+* 修復 開放金鑰匙圈 5.6+ 的 開放PGP 金鑰 ID 查找問題
+* 正確驗證 punycode TLS 證書
+* 改進 RTP 會話建立的穩定性(呼叫)
diff --git a/fastlane/metadata/android/zh-TW/changelogs/42006.txt b/fastlane/metadata/android/zh-TW/changelogs/42006.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d435a9bfbef51576e40939c44a0c2cb6b3c809df
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/42006.txt
@@ -0,0 +1,2 @@
+* 使用預先存在的 OMEMO 會話驗證音訊/視訊通話
+* 改進與非 libwebrtc WebRTC 實現的相容性
diff --git a/fastlane/metadata/android/zh-TW/changelogs/42010.txt b/fastlane/metadata/android/zh-TW/changelogs/42010.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2ff72721935097c002ca62feb012fd81136f3ad0
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/42010.txt
@@ -0,0 +1,2 @@
+* 針對 Tor 支援進行了各種錯誤修復
+* 改進與 Dino 的通話相容性
diff --git a/fastlane/metadata/android/zh-TW/changelogs/42012.txt b/fastlane/metadata/android/zh-TW/changelogs/42012.txt
new file mode 100644
index 0000000000000000000000000000000000000000..5e5b341fbbd5d834f3a4e562ea1494d685313d0c
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/42012.txt
@@ -0,0 +1 @@
+* 修復對不信任系統 CA 的用戶的 HTTP 上傳/下載問題
diff --git a/fastlane/metadata/android/zh-TW/changelogs/42013.txt b/fastlane/metadata/android/zh-TW/changelogs/42013.txt
new file mode 100644
index 0000000000000000000000000000000000000000..51d6c51b689c2c77a5f007ac27324e44a509847d
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/42013.txt
@@ -0,0 +1 @@
+* 修復在 Android 7.1 上的「無連線」問題
diff --git a/fastlane/metadata/android/zh-TW/changelogs/42014.txt b/fastlane/metadata/android/zh-TW/changelogs/42014.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f2106a3bdd51717fc7c93acfe31c87765b244338
--- /dev/null
+++ b/fastlane/metadata/android/zh-TW/changelogs/42014.txt
@@ -0,0 +1,2 @@
+* 總是驗證域名。不允許使用者覆寫
+* 支援花名冊預先驗證
diff --git a/src/conversations/fastlane/metadata/android/zh-CN/full_description.txt b/src/conversations/fastlane/metadata/android/zh-CN/full_description.txt
index f19e2cd87ad63a69cb93ac385c655b960d6c59a7..085d211918bbb84e521fbe495bae713c8c1cef40 100644
--- a/src/conversations/fastlane/metadata/android/zh-CN/full_description.txt
+++ b/src/conversations/fastlane/metadata/android/zh-CN/full_description.txt
@@ -1,23 +1,23 @@
-易于使用、性能可靠、电池友好。内置支持图片、群组聊天和 e2e 加密功能。
+易于使用、性能可靠、电池友好。内置支持图片、群聊和 e2e 加密功能。
设计原则:
* 在不牺牲安全性和隐私性的前提下,尽可能美观易用
* 依赖现有的、完善的协议
-* 不需要 Google 账号或特定的 Google 云通讯服务 (GCM)
+* 不需要 Google 账号或特定的 Google 云通讯服务(GCM)
* 要求尽可能少的权限
特点:
* 使用 OMEMO 或 OpenPGP 进行端对端加密
* 发送和接收图片
-* 加密音视频通话 (DTLS-SRTP)
+* 加密音视频通话(DTLS-SRTP)
* 直观的用户界面,遵循 Android 设计准则
* 为您的联系人添加图片/头像
* 与桌面客户端同步
-* 群聊(支持书签功能)
+* 群聊(支持书签功能)
* 通讯录集成
-* 多账号/统一收件箱
+* 多账号/统一消息栏
* 对电池寿命的影响非常小
Conversations 使在免费的 conversations.im 服务器上创建账号变得非常简单。不过,Conversations 也适用于任何其他 XMPP 服务器。许多 XMPP 服务器都是由志愿者免费运行的。
@@ -28,9 +28,9 @@ Conversations 适用于所有 XMPP 服务器。然而,XMPP 是一种可扩展
到目前为止,这些 XEP 是:
-* XEP-0065:SOCKS5 字节流 (or mod_proxy65)。如果双方都在防火墙 (NAT) 后面,将用于传输文件。
-* XEP-0163:个人事件协议对于头像
-* XEP-0191:屏蔽指令可让您将垃圾邮件发送者列入黑名单或屏蔽的联系人中,而不会将其从花名册中删除。
+* XEP-0065:SOCKS5 字节流(or mod_proxy65)。如果双方都在防火墙(NAT)后面,将用于传输文件。
+* XEP-0163:个人事件协议(头像)
+* XEP-0191:屏蔽命令可让您将垃圾消息发送者列入黑名单或屏蔽的联系人中,而不会将其从花名册中删除。
* XEP-0198:流管理允许 XMPP 在小规模网络中断和底层 TCP 连接发生变化时继续运行。
* XEP-0280:消息抄送,可自动将您发送的消息同步到桌面客户端,因此您可以在一次对话中从手机客户端无缝切换到桌面客户端,然后再返回。
* XEP-0237:花名册版本控制主要是为了在移动连接不佳的情况下节省带宽
diff --git a/src/conversations/fastlane/metadata/android/zh-CN/short_description.txt b/src/conversations/fastlane/metadata/android/zh-CN/short_description.txt
index 28659cd560ea87036b941574e960744c0bda7ab3..913d97480e7dd7382ea44f5bfa8f5dd80632ab36 100644
--- a/src/conversations/fastlane/metadata/android/zh-CN/short_description.txt
+++ b/src/conversations/fastlane/metadata/android/zh-CN/short_description.txt
@@ -1 +1 @@
-加密、易于使用的 XMPP 即时信使,适用于您的移动设备
+加密、易于使用的 XMPP 即时通讯软件,适用于您的移动设备
diff --git a/src/conversations/res/values-zh-rCN/strings.xml b/src/conversations/res/values-zh-rCN/strings.xml
index 058adbf1e3ec5979085013ceb2daaa81f6fc4480..34cf3573435def88de5e2dd17a9286321702f7f5 100644
--- a/src/conversations/res/values-zh-rCN/strings.xml
+++ b/src/conversations/res/values-zh-rCN/strings.xml
@@ -3,7 +3,7 @@
选择您的 XMPP 提供者
使用 conversations.im
创建新账号
- 您已经有 XMPP 账号了吗?如果您之前使用过 Conversations 或其他 XMPP 客户端,那么您已经有这种账号了。如果没有,您可以立即创建一个。
+ 您已经有 XMPP 账号了吗?如果您之前使用过 Conversations 或其他 XMPP 客户端,那么您已经有账号了。如果没有,您可以立即创建一个。
\n提示:一些电子邮件服务也提供 XMPP 账号。
XMPP 是独立于提供者的即时通讯网络。您选择的任何 XMPP 服务器都可以使用此客户端。
\n不过,您可以轻松地在 conversations.im 上创建账号;特别适合与 Conversations 使用的提供者。
@@ -13,7 +13,7 @@
\n向其他 XMPP 用户提供您的完整地址,就能和对方交流。
您的服务器邀请
配置代码格式不正确
- 点击分享按钮,向您的联系人发送加入 %1$s 的邀请。
+ 轻击分享按钮,向您的联系人发送加入 %1$s 的邀请。
如果您的联系人在附近,对方也可以扫描下方二维码接受邀请。
加入 %1$s 和我聊天:%2$s
分享邀请至…
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 1e0e71f41b72586283c006682e81811724f9783e..4a0581f4cbea83603fd5d876e6a2302a60b9362d 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -116,9 +116,6 @@ public final class Config {
public static final boolean OMEMO_PADDING = false;
public static final boolean PUT_AUTH_TAG_INTO_KEY = true;
public static final boolean AUTOMATICALLY_COMPLETE_SESSIONS = true;
-
- public static final boolean USE_BOOKMARKS2 = false;
-
public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb
public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true;
public static final boolean DISABLE_HTTP_UPLOAD = false;
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index 306589c1d44f4818761f2c61f38f73bea81e7f17..10cf19de1a5eb3219cd948c788c7bf0202fcca07 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -360,12 +360,18 @@ public class IqGenerator extends AbstractGenerator {
return iq;
}
- public IqPacket generateSetBlockRequest(final Jid jid, boolean reportSpam) {
+ public IqPacket generateSetBlockRequest(final Jid jid, final boolean reportSpam, final String serverMsgId) {
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
final Element block = iq.addChild("block", Namespace.BLOCKING);
final Element item = block.addChild("item").setAttribute("jid", jid);
if (reportSpam) {
- item.addChild("report", "urn:xmpp:reporting:0").addChild("spam");
+ final Element report = item.addChild("report", Namespace.REPORTING);
+ report.setAttribute("reason", Namespace.REPORTING_REASON_SPAM);
+ if (serverMsgId != null) {
+ final Element stanzaId = report.addChild("stanza-id", Namespace.STANZA_IDS);
+ stanzaId.setAttribute("by", jid);
+ stanzaId.setAttribute("id", serverMsgId);
+ }
}
Log.d(Config.LOGTAG, iq.toString());
return iq;
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 15015e01778a9cef793ba37ad5306ade61bac535..998e741cd1a3aaa077f0d550db7edb1d2440ae86 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -2262,6 +2262,7 @@ public class XmppConnectionService extends Service {
if (connection == null) {
Log.d(Config.LOGTAG, account.getJid().asBareJid()+": no connection. ignoring bookmark creation");
} else if (connection.getFeatures().bookmarks2()) {
+ Log.d(Config.LOGTAG,account.getJid().asBareJid() + ": pushing bookmark via Bookmarks 2");
final Element item = mIqGenerator.publishBookmarkItem(bookmark);
pushNodeAndEnforcePublishOptions(account, Namespace.BOOKMARKS2, item, bookmark.getJid().asBareJid().toEscapedString(), PublishOptions.persistentWhitelistAccessMaxItems());
} else if (connection.getFeatures().bookmarksConversion()) {
@@ -2280,7 +2281,8 @@ public class XmppConnectionService extends Service {
if (connection == null) return;
if (connection.getFeatures().bookmarks2()) {
- IqPacket request = mIqGenerator.deleteItem(Namespace.BOOKMARKS2, bookmark.getJid().asBareJid().toEscapedString());
+ final IqPacket request = mIqGenerator.deleteItem(Namespace.BOOKMARKS2, bookmark.getJid().asBareJid().toEscapedString());
+ Log.d(Config.LOGTAG,account.getJid().asBareJid() + ": removing bookmark via Bookmarks 2");
sendIqPacket(account, request, (a, response) -> {
if (response.getType() == IqPacket.TYPE.ERROR) {
Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": unable to delete bookmark " + response.getErrorCondition());
@@ -5208,10 +5210,10 @@ public class XmppConnectionService extends Service {
mDatabaseWriterExecutor.execute(runnable);
}
- public boolean sendBlockRequest(final Blockable blockable, boolean reportSpam) {
+ public boolean sendBlockRequest(final Blockable blockable, final boolean reportSpam, final String serverMsgId) {
if (blockable != null && blockable.getBlockedJid() != null) {
final Jid jid = blockable.getBlockedJid();
- this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam), (a, response) -> {
+ this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid, reportSpam, serverMsgId), (a, response) -> {
if (response.getType() == IqPacket.TYPE.RESULT) {
a.getBlocklist().add(jid);
updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
diff --git a/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java b/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
index 6f4d77c51ffc00da35861dd727525da7f72b76cf..986aeb5639c2751873aaa820441da766905fbafe 100644
--- a/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
+++ b/src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java
@@ -14,13 +14,27 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.ui.util.JidDialog;
public final class BlockContactDialog {
+
public static void show(final XmppActivity xmppActivity, final Blockable blockable) {
+ show(xmppActivity, blockable, null);
+ }
+ public static void show(final XmppActivity xmppActivity, final Blockable blockable, final String serverMsgId) {
final AlertDialog.Builder builder = new AlertDialog.Builder(xmppActivity);
final boolean isBlocked = blockable.isBlocked();
builder.setNegativeButton(R.string.cancel, null);
DialogBlockContactBinding binding = DataBindingUtil.inflate(xmppActivity.getLayoutInflater(), R.layout.dialog_block_contact, null, false);
final boolean reporting = blockable.getAccount().getXmppConnection().getFeatures().spamReporting();
- binding.reportSpam.setVisibility(!isBlocked && reporting ? View.VISIBLE : View.GONE);
+ if (reporting && !isBlocked) {
+ binding.reportSpam.setVisibility(View.VISIBLE);
+ if (serverMsgId != null) {
+ binding.reportSpam.setChecked(true);
+ binding.reportSpam.setEnabled(false);
+ } else {
+ binding.reportSpam.setEnabled(true);
+ }
+ } else {
+ binding.reportSpam.setVisibility(View.GONE);
+ }
builder.setView(binding.getRoot());
final String value;
@@ -34,8 +48,18 @@ public final class BlockContactDialog {
value =blockable.getJid().getDomain().toEscapedString();
res = isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text;
} else {
- int resBlockAction = blockable instanceof Conversation && ((Conversation) blockable).isWithStranger() ? R.string.block_stranger : R.string.action_block_contact;
- builder.setTitle(isBlocked ? R.string.action_unblock_contact : resBlockAction);
+ if (isBlocked) {
+ builder.setTitle(R.string.action_unblock_contact);
+ } else if (serverMsgId != null) {
+ builder.setTitle(R.string.report_spam_and_block);
+ } else {
+ final int resBlockAction =
+ blockable instanceof Conversation
+ && ((Conversation) blockable).isWithStranger()
+ ? R.string.block_stranger
+ : R.string.action_block_contact;
+ builder.setTitle(resBlockAction);
+ }
value = blockable.getJid().asBareJid().toEscapedString();
res = isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text;
}
@@ -45,7 +69,7 @@ public final class BlockContactDialog {
xmppActivity.xmppConnectionService.sendUnblockRequest(blockable);
} else {
boolean toastShown = false;
- if (xmppActivity.xmppConnectionService.sendBlockRequest(blockable, binding.reportSpam.isChecked())) {
+ if (xmppActivity.xmppConnectionService.sendBlockRequest(blockable, binding.reportSpam.isChecked(), serverMsgId)) {
Toast.makeText(xmppActivity, R.string.corresponding_conversations_closed, Toast.LENGTH_SHORT).show();
toastShown = true;
}
diff --git a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
index 52e12d96010cdba4e1353049aec4543691ca60fe..21a90ffd80fa9a360ae025d7ec89bb5a9fe7a534 100644
--- a/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java
@@ -89,7 +89,7 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem
dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid, x, y) -> {
Blockable blockable = new RawBlockable(account, contactJid);
- if (xmppConnectionService.sendBlockRequest(blockable, false)) {
+ if (xmppConnectionService.sendBlockRequest(blockable, false, null)) {
Toast.makeText(BlocklistActivity.this, R.string.corresponding_conversations_closed, Toast.LENGTH_SHORT).show();
}
return true;
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 096240b300086cc7ff8e1fc2ac4764e3f907d0cb..c3af9daf862dbd79315623bd18863c900212a484 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -1653,6 +1653,7 @@ public class ConversationFragment extends XmppFragment
&& (t instanceof JingleFileTransferConnection
|| t instanceof HttpDownloadConnection);
activity.getMenuInflater().inflate(R.menu.message_context, menu);
+ final MenuItem reportAndBlock = menu.findItem(R.id.action_report_and_block);
MenuItem openWith = menu.findItem(R.id.open_with);
MenuItem copyMessage = menu.findItem(R.id.copy_message);
MenuItem quoteMessage = menu.findItem(R.id.quote_message);
@@ -1676,6 +1677,17 @@ public class ConversationFragment extends XmppFragment
m.getStatus() == Message.STATUS_SEND_FAILED
&& m.getErrorMessage() != null
&& !Message.ERROR_MESSAGE_CANCELLED.equals(m.getErrorMessage());
+ final Conversational conversational = m.getConversation();
+ if (m.getStatus() == Message.STATUS_RECEIVED && conversational instanceof Conversation c) {
+ final XmppConnection connection = c.getAccount().getXmppConnection();
+ if (c.isWithStranger()
+ && m.getServerMsgId() != null
+ && !c.isBlocked()
+ && connection != null
+ && connection.getFeatures().spamReporting()) {
+ reportAndBlock.setVisible(true);
+ }
+ }
if (!encrypted && !m.getBody().equals("")) {
copyMessage.setVisible(true);
}
@@ -1878,6 +1890,8 @@ public class ConversationFragment extends XmppFragment
backPressedLeaveSingleThread.setEnabled(true);
setThread(selectedMessage.getThread());
refresh();
+ case R.id.action_report_and_block:
+ reportMessage(selectedMessage);
return true;
default:
return onOptionsItemSelected(item);
@@ -2641,6 +2655,10 @@ public class ConversationFragment extends XmppFragment
}
}
+ private void reportMessage(final Message message) {
+ BlockContactDialog.show(activity, conversation.getContact(), message.getServerMsgId());
+ }
+
private void showErrorMessage(final Message message) {
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
builder.setTitle(R.string.error_message);
diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java
index 5a92525151454483daea182a133c314b3b0cc84e..27bc38dce3e3be6fe2e7b33d3f360f3993e455d6 100644
--- a/src/main/java/eu/siacs/conversations/xml/Namespace.java
+++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java
@@ -68,4 +68,7 @@ public final class Namespace {
public static final String JINGLE_TRANSPORT_ICE_OPTION = "http://gultsch.de/xmpp/drafts/jingle/transports/ice-udp/option";
public static final String UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push";
public static final String VCARD4 = "urn:ietf:params:xml:ns:vcard-4.0";
+ public static final String REPORTING = "urn:xmpp:reporting:1";
+ public static final String REPORTING_REASON_SPAM = "urn:xmpp:reporting:spam";
+ public static final String SDP_OFFER_ANSWER = "urn:ietf:rfc:3264";
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index 609e2627cf7a0db699edd52c7e80e98e29eef33c..93b4ba32775feebb80b67e8e1382c41fe02a63bf 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -2743,7 +2743,7 @@ public class XmppConnection implements Runnable {
}
public boolean spamReporting() {
- return hasDiscoFeature(account.getDomain(), "urn:xmpp:reporting:reason:spam:0");
+ return hasDiscoFeature(account.getDomain(), Namespace.REPORTING);
}
public boolean flexibleOfflineMessageRetrieval() {
@@ -2884,7 +2884,7 @@ public class XmppConnection implements Runnable {
}
public boolean bookmarks2() {
- return Config.USE_BOOKMARKS2 || hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT);
+ return pepPublishOptions() && hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT);
}
public boolean externalServiceDiscovery() {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
index 4d9dbe18aa675d006352f8144698f05ce88f94af..cb59b385caf2cb35def9036229ac68ce73e54533 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
@@ -19,34 +19,12 @@ import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.SettableFuture;
-
-import org.webrtc.DtmfSender;
-import org.webrtc.EglBase;
-import org.webrtc.IceCandidate;
-import org.webrtc.PeerConnection;
-import org.webrtc.VideoTrack;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
import eu.siacs.conversations.BuildConfig;
import eu.siacs.conversations.Config;
@@ -66,7 +44,6 @@ import eu.siacs.conversations.utils.IP;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
-import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
@@ -78,6 +55,26 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
+import org.webrtc.DtmfSender;
+import org.webrtc.EglBase;
+import org.webrtc.IceCandidate;
+import org.webrtc.PeerConnection;
+import org.webrtc.VideoTrack;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
public class JingleRtpConnection extends AbstractJingleConnection
implements WebRTCWrapper.EventCallback {
@@ -185,7 +182,6 @@ public class JingleRtpConnection extends AbstractJingleConnection
private final Queue stateHistory = new LinkedList<>();
private ScheduledFuture> ringingTimeoutFuture;
private final long created = System.currentTimeMillis() / 1000L;
- private final SettableFuture> iceGatheringComplete = SettableFuture.create();
JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) {
super(jingleConnectionManager, id, initiator);
@@ -202,64 +198,37 @@ public class JingleRtpConnection extends AbstractJingleConnection
}
private static State reasonToState(Reason reason) {
- switch (reason) {
- case SUCCESS:
- return State.TERMINATED_SUCCESS;
- case DECLINE:
- case BUSY:
- return State.TERMINATED_DECLINED_OR_BUSY;
- case CANCEL:
- case TIMEOUT:
- return State.TERMINATED_CANCEL_OR_TIMEOUT;
- case SECURITY_ERROR:
- return State.TERMINATED_SECURITY_ERROR;
- case FAILED_APPLICATION:
- case UNSUPPORTED_TRANSPORTS:
- case UNSUPPORTED_APPLICATIONS:
- return State.TERMINATED_APPLICATION_FAILURE;
- default:
- return State.TERMINATED_CONNECTIVITY_ERROR;
- }
+ return switch (reason) {
+ case SUCCESS -> State.TERMINATED_SUCCESS;
+ case DECLINE, BUSY -> State.TERMINATED_DECLINED_OR_BUSY;
+ case CANCEL, TIMEOUT -> State.TERMINATED_CANCEL_OR_TIMEOUT;
+ case SECURITY_ERROR -> State.TERMINATED_SECURITY_ERROR;
+ case FAILED_APPLICATION, UNSUPPORTED_TRANSPORTS, UNSUPPORTED_APPLICATIONS -> State
+ .TERMINATED_APPLICATION_FAILURE;
+ default -> State.TERMINATED_CONNECTIVITY_ERROR;
+ };
}
@Override
synchronized void deliverPacket(final JinglePacket jinglePacket) {
switch (jinglePacket.getAction()) {
- case SESSION_INITIATE:
- receiveSessionInitiate(jinglePacket);
- break;
- case TRANSPORT_INFO:
- receiveTransportInfo(jinglePacket);
- break;
- case SESSION_ACCEPT:
- receiveSessionAccept(jinglePacket);
- break;
- case SESSION_TERMINATE:
- receiveSessionTerminate(jinglePacket);
- break;
- case CONTENT_ADD:
- receiveContentAdd(jinglePacket);
- break;
- case CONTENT_ACCEPT:
- receiveContentAccept(jinglePacket);
- break;
- case CONTENT_REJECT:
- receiveContentReject(jinglePacket);
- break;
- case CONTENT_REMOVE:
- receiveContentRemove(jinglePacket);
- break;
- case CONTENT_MODIFY:
- receiveContentModify(jinglePacket);
- break;
- default:
+ case SESSION_INITIATE -> receiveSessionInitiate(jinglePacket);
+ case TRANSPORT_INFO -> receiveTransportInfo(jinglePacket);
+ case SESSION_ACCEPT -> receiveSessionAccept(jinglePacket);
+ case SESSION_TERMINATE -> receiveSessionTerminate(jinglePacket);
+ case CONTENT_ADD -> receiveContentAdd(jinglePacket);
+ case CONTENT_ACCEPT -> receiveContentAccept(jinglePacket);
+ case CONTENT_REJECT -> receiveContentReject(jinglePacket);
+ case CONTENT_REMOVE -> receiveContentRemove(jinglePacket);
+ case CONTENT_MODIFY -> receiveContentModify(jinglePacket);
+ default -> {
respondOk(jinglePacket);
Log.d(
Config.LOGTAG,
String.format(
"%s: received unhandled jingle action %s",
id.account.getJid().asBareJid(), jinglePacket.getAction()));
- break;
+ }
}
}
@@ -365,15 +334,22 @@ public class JingleRtpConnection extends AbstractJingleConnection
final Set> candidates =
contentMap.contents.entrySet();
final RtpContentMap remote = getRemoteContentMap();
- final Set remoteContentIds = remote == null ? Collections.emptySet() : remote.contents.keySet();
+ final Set remoteContentIds =
+ remote == null ? Collections.emptySet() : remote.contents.keySet();
if (Collections.disjoint(remoteContentIds, contentMap.contents.keySet())) {
- Log.d(Config.LOGTAG,"received transport-info for unknown contents "+contentMap.contents.keySet()+" (known: "+remoteContentIds+")");
+ Log.d(
+ Config.LOGTAG,
+ "received transport-info for unknown contents "
+ + contentMap.contents.keySet()
+ + " (known: "
+ + remoteContentIds
+ + ")");
respondOk(jinglePacket);
pendingIceCandidates.addAll(candidates);
return;
}
if (this.state != State.SESSION_ACCEPTED) {
- Log.d(Config.LOGTAG,"received transport-info prematurely. adding to backlog");
+ Log.d(Config.LOGTAG, "received transport-info prematurely. adding to backlog");
respondOk(jinglePacket);
pendingIceCandidates.addAll(candidates);
return;
@@ -412,26 +388,31 @@ public class JingleRtpConnection extends AbstractJingleConnection
final boolean hasFullTransportInfo = modification.hasFullTransportInfo();
final ListenableFuture future =
receiveRtpContentMap(
- modification, this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
- Futures.addCallback(future, new FutureCallback() {
- @Override
- public void onSuccess(final RtpContentMap rtpContentMap) {
- receiveContentAdd(jinglePacket, rtpContentMap);
- }
+ modification,
+ this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
+ Futures.addCallback(
+ future,
+ new FutureCallback<>() {
+ @Override
+ public void onSuccess(final RtpContentMap rtpContentMap) {
+ receiveContentAdd(jinglePacket, rtpContentMap);
+ }
- @Override
- public void onFailure(@NonNull Throwable throwable) {
- respondOk(jinglePacket);
- final Throwable rootCause = Throwables.getRootCause(throwable);
- Log.d(
- Config.LOGTAG,
- id.account.getJid().asBareJid()
- + ": improperly formatted contents in content-add",
- throwable);
- webRTCWrapper.close();
- sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
- }
- }, MoreExecutors.directExecutor());
+ @Override
+ public void onFailure(@NonNull Throwable throwable) {
+ respondOk(jinglePacket);
+ final Throwable rootCause = Throwables.getRootCause(throwable);
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": improperly formatted contents in content-add",
+ throwable);
+ webRTCWrapper.close();
+ sendSessionTerminate(
+ Reason.ofThrowable(rootCause), rootCause.getMessage());
+ }
+ },
+ MoreExecutors.directExecutor());
} else {
terminateWithOutOfOrder(jinglePacket);
}
@@ -519,19 +500,24 @@ public class JingleRtpConnection extends AbstractJingleConnection
final boolean hasFullTransportInfo = receivedContentAccept.hasFullTransportInfo();
final ListenableFuture future =
receiveRtpContentMap(
- receivedContentAccept, this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
- Futures.addCallback(future, new FutureCallback() {
- @Override
- public void onSuccess(final RtpContentMap result) {
- receiveContentAccept(result);
- }
+ receivedContentAccept,
+ this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
+ Futures.addCallback(
+ future,
+ new FutureCallback<>() {
+ @Override
+ public void onSuccess(final RtpContentMap result) {
+ receiveContentAccept(result);
+ }
- @Override
- public void onFailure(@NonNull final Throwable throwable) {
- webRTCWrapper.close();
- sendSessionTerminate(Reason.ofThrowable(throwable), throwable.getMessage());
- }
- }, MoreExecutors.directExecutor());
+ @Override
+ public void onFailure(@NonNull final Throwable throwable) {
+ webRTCWrapper.close();
+ sendSessionTerminate(
+ Reason.ofThrowable(throwable), throwable.getMessage());
+ }
+ },
+ MoreExecutors.directExecutor());
} else {
Log.d(Config.LOGTAG, "received content-accept did not match our outgoing content-add");
terminateWithOutOfOrder(jinglePacket);
@@ -584,25 +570,34 @@ public class JingleRtpConnection extends AbstractJingleConnection
final boolean isInitiator = isInitiator();
final RtpContentMap currentOutgoing = this.outgoingContentAdd;
final RtpContentMap remoteContentMap = this.getRemoteContentMap();
- final Set currentOutgoingMediaIds = currentOutgoing == null ? Collections.emptySet() : currentOutgoing.contents.keySet();
+ final Set currentOutgoingMediaIds =
+ currentOutgoing == null
+ ? Collections.emptySet()
+ : currentOutgoing.contents.keySet();
Log.d(Config.LOGTAG, "receiveContentModification(" + modification + ")");
if (currentOutgoing != null && currentOutgoingMediaIds.containsAll(modification.keySet())) {
respondOk(jinglePacket);
final RtpContentMap modifiedContentMap;
try {
- modifiedContentMap = currentOutgoing.modifiedSendersChecked(isInitiator, modification);
+ modifiedContentMap =
+ currentOutgoing.modifiedSendersChecked(isInitiator, modification);
} catch (final IllegalArgumentException e) {
webRTCWrapper.close();
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
return;
}
this.outgoingContentAdd = modifiedContentMap;
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid()+": processed content-modification for pending content-add");
- } else if (remoteContentMap != null && remoteContentMap.contents.keySet().containsAll(modification.keySet())) {
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": processed content-modification for pending content-add");
+ } else if (remoteContentMap != null
+ && remoteContentMap.contents.keySet().containsAll(modification.keySet())) {
respondOk(jinglePacket);
final RtpContentMap modifiedRemoteContentMap;
try {
- modifiedRemoteContentMap = remoteContentMap.modifiedSendersChecked(isInitiator, modification);
+ modifiedRemoteContentMap =
+ remoteContentMap.modifiedSendersChecked(isInitiator, modification);
} catch (final IllegalArgumentException e) {
webRTCWrapper.close();
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
@@ -612,20 +607,27 @@ public class JingleRtpConnection extends AbstractJingleConnection
try {
offer = SessionDescription.of(modifiedRemoteContentMap, !isInitiator());
} catch (final IllegalArgumentException | NullPointerException e) {
- Log.d(Config.LOGTAG, id.getAccount().getJid().asBareJid() + ": unable convert offer from content-modify to SDP", e);
+ Log.d(
+ Config.LOGTAG,
+ id.getAccount().getJid().asBareJid()
+ + ": unable convert offer from content-modify to SDP",
+ e);
webRTCWrapper.close();
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
return;
}
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid()+": auto accepting content-modification");
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid() + ": auto accepting content-modification");
this.autoAcceptContentModify(modifiedRemoteContentMap, offer);
} else {
- Log.d(Config.LOGTAG,"received unsupported content modification "+modification);
+ Log.d(Config.LOGTAG, "received unsupported content modification " + modification);
respondWithItemNotFound(jinglePacket);
}
}
- private void autoAcceptContentModify(final RtpContentMap modifiedRemoteContentMap, final SessionDescription offer) {
+ private void autoAcceptContentModify(
+ final RtpContentMap modifiedRemoteContentMap, final SessionDescription offer) {
this.setRemoteContentMap(modifiedRemoteContentMap);
final org.webrtc.SessionDescription sdp =
new org.webrtc.SessionDescription(
@@ -636,8 +638,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
final SessionDescription answer = setLocalSessionDescription();
final RtpContentMap rtpContentMap = RtpContentMap.of(answer, isInitiator());
modifyLocalContentMap(rtpContentMap);
- // we do not need to send an answer but do we have to resend the candidates currently in SDP?
- //resendCandidatesFromSdp(answer);
+ // we do not need to send an answer but do we have to resend the candidates currently in
+ // SDP?
+ // resendCandidatesFromSdp(answer);
webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
} catch (final Exception e) {
Log.d(Config.LOGTAG, "unable to accept content add", Throwables.getRootCause(e));
@@ -646,22 +649,24 @@ public class JingleRtpConnection extends AbstractJingleConnection
}
}
- private void resendCandidatesFromSdp(final SessionDescription answer) {
- final ImmutableMultimap.Builder candidateBuilder = new ImmutableMultimap.Builder<>();
- for(final SessionDescription.Media media : answer.media) {
+ private static ImmutableMultimap parseCandidates(
+ final SessionDescription answer) {
+ final ImmutableMultimap.Builder candidateBuilder =
+ new ImmutableMultimap.Builder<>();
+ for (final SessionDescription.Media media : answer.media) {
final String mid = Iterables.getFirst(media.attributes.get("mid"), null);
if (Strings.isNullOrEmpty(mid)) {
continue;
}
- for(final String sdpCandidate : media.attributes.get("candidate")) {
- final IceUdpTransportInfo.Candidate candidate = IceUdpTransportInfo.Candidate.fromSdpAttributeValue(sdpCandidate, null);
+ for (final String sdpCandidate : media.attributes.get("candidate")) {
+ final IceUdpTransportInfo.Candidate candidate =
+ IceUdpTransportInfo.Candidate.fromSdpAttributeValue(sdpCandidate, null);
if (candidate != null) {
- candidateBuilder.put(mid,candidate);
+ candidateBuilder.put(mid, candidate);
}
}
}
- final ImmutableMultimap candidates = candidateBuilder.build();
- sendTransportInfo(candidates);
+ return candidateBuilder.build();
}
private void receiveContentReject(final JinglePacket jinglePacket) {
@@ -689,7 +694,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
if (ourSummary.equals(ContentAddition.summary(receivedContentReject))) {
this.outgoingContentAdd = null;
respondOk(jinglePacket);
- Log.d(Config.LOGTAG,jinglePacket.toString());
+ Log.d(Config.LOGTAG, jinglePacket.toString());
receiveContentReject(ourSummary);
} else {
Log.d(Config.LOGTAG, "received content-reject did not match our outgoing content-add");
@@ -825,7 +830,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
"Unexpected rollback condition. Senders were not uniformly none");
}
- public synchronized void acceptContentAdd(@NonNull final Set contentAddition) {
+ public synchronized void acceptContentAdd(
+ @NonNull final Set contentAddition) {
final RtpContentMap incomingContentAdd = this.incomingContentAdd;
if (incomingContentAdd == null) {
throw new IllegalStateException("No incoming content add");
@@ -834,37 +840,63 @@ public class JingleRtpConnection extends AbstractJingleConnection
if (contentAddition.equals(ContentAddition.summary(incomingContentAdd))) {
this.incomingContentAdd = null;
final Set senders = incomingContentAdd.getSenders();
- Log.d(Config.LOGTAG,"senders of incoming content-add: "+senders);
+ Log.d(Config.LOGTAG, "senders of incoming content-add: " + senders);
if (senders.equals(Content.Senders.receiveOnly(isInitiator()))) {
- Log.d(Config.LOGTAG,"content addition is receive only. we want to upgrade to 'both'");
- final RtpContentMap modifiedSenders = incomingContentAdd.modifiedSenders(Content.Senders.BOTH);
- final JinglePacket proposedContentModification = modifiedSenders.toStub().toJinglePacket(JinglePacket.Action.CONTENT_MODIFY, id.sessionId);
+ Log.d(
+ Config.LOGTAG,
+ "content addition is receive only. we want to upgrade to 'both'");
+ final RtpContentMap modifiedSenders =
+ incomingContentAdd.modifiedSenders(Content.Senders.BOTH);
+ final JinglePacket proposedContentModification =
+ modifiedSenders
+ .toStub()
+ .toJinglePacket(JinglePacket.Action.CONTENT_MODIFY, id.sessionId);
proposedContentModification.setTo(id.with);
- xmppConnectionService.sendIqPacket(id.account, proposedContentModification, (account, response) -> {
- if (response.getType() == IqPacket.TYPE.RESULT) {
- Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": remote has accepted our upgrade to senders=both");
- acceptContentAdd(ContentAddition.summary(modifiedSenders), modifiedSenders);
- } else {
- Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": remote has rejected our upgrade to senders=both");
- acceptContentAdd(contentAddition, incomingContentAdd);
- }
- });
+ xmppConnectionService.sendIqPacket(
+ id.account,
+ proposedContentModification,
+ (account, response) -> {
+ if (response.getType() == IqPacket.TYPE.RESULT) {
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": remote has accepted our upgrade to senders=both");
+ acceptContentAdd(
+ ContentAddition.summary(modifiedSenders), modifiedSenders);
+ } else {
+ Log.d(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": remote has rejected our upgrade to senders=both");
+ acceptContentAdd(contentAddition, incomingContentAdd);
+ }
+ });
+ } else {
+ acceptContentAdd(contentAddition, incomingContentAdd);
}
} else {
- throw new IllegalStateException("Accepted content add does not match pending content-add");
+ throw new IllegalStateException(
+ "Accepted content add does not match pending content-add");
}
}
- private void acceptContentAdd(@NonNull final Set contentAddition, final RtpContentMap incomingContentAdd) {
+ private void acceptContentAdd(
+ @NonNull final Set contentAddition,
+ final RtpContentMap incomingContentAdd) {
final IceUdpTransportInfo.Setup setup = getPeerDtlsSetup();
- final RtpContentMap modifiedContentMap = getRemoteContentMap().addContent(incomingContentAdd, setup);
+ final RtpContentMap modifiedContentMap =
+ getRemoteContentMap().addContent(incomingContentAdd, setup);
this.setRemoteContentMap(modifiedContentMap);
final SessionDescription offer;
try {
offer = SessionDescription.of(modifiedContentMap, !isInitiator());
} catch (final IllegalArgumentException | NullPointerException e) {
- Log.d(Config.LOGTAG, id.getAccount().getJid().asBareJid() + ": unable convert offer from content-add to SDP", e);
+ Log.d(
+ Config.LOGTAG,
+ id.getAccount().getJid().asBareJid()
+ + ": unable convert offer from content-add to SDP",
+ e);
webRTCWrapper.close();
sendSessionTerminate(Reason.FAILED_APPLICATION, e.getMessage());
return;
@@ -905,10 +937,11 @@ public class JingleRtpConnection extends AbstractJingleConnection
addIceCandidatesFromBlackLog();
modifyLocalContentMap(rtpContentMap);
- final ListenableFuture future = prepareOutgoingContentMap(contentAcceptMap);
+ final ListenableFuture future =
+ prepareOutgoingContentMap(contentAcceptMap);
Futures.addCallback(
future,
- new FutureCallback() {
+ new FutureCallback<>() {
@Override
public void onSuccess(final RtpContentMap rtpContentMap) {
sendContentAccept(rtpContentMap);
@@ -929,7 +962,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
}
private void sendContentAccept(final RtpContentMap contentAcceptMap) {
- final JinglePacket jinglePacket = contentAcceptMap.toJinglePacket(JinglePacket.Action.CONTENT_ACCEPT, id.sessionId);
+ final JinglePacket jinglePacket =
+ contentAcceptMap.toJinglePacket(JinglePacket.Action.CONTENT_ACCEPT, id.sessionId);
send(jinglePacket);
}
@@ -975,7 +1009,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
// ICE-restart
// and if that's the case we are seeing an answer.
// This might be more spec compliant but also more error prone potentially
- final boolean isSignalStateStable = this.webRTCWrapper.getSignalingState() == PeerConnection.SignalingState.STABLE;
+ final boolean isSignalStateStable =
+ this.webRTCWrapper.getSignalingState() == PeerConnection.SignalingState.STABLE;
// TODO a stable signal state can be another indicator that we have an offer to restart ICE
final boolean isOffer = rtpContentMap.emptyCandidates();
final RtpContentMap restartContentMap;
@@ -1039,7 +1074,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
final RtpContentMap restartContentMap,
final boolean isOffer)
throws ExecutionException, InterruptedException {
- final SessionDescription sessionDescription = SessionDescription.of(restartContentMap, !isInitiator());
+ final SessionDescription sessionDescription =
+ SessionDescription.of(restartContentMap, !isInitiator());
final org.webrtc.SessionDescription.Type type =
isOffer
? org.webrtc.SessionDescription.Type.OFFER
@@ -1138,8 +1174,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
} catch (final Exception e) {
return Futures.immediateFailedFuture(e);
}
- }
- private ListenableFuture receiveRtpContentMap(final RtpContentMap receivedContentMap, final boolean expectVerification) {
+ }
+
+ private ListenableFuture receiveRtpContentMap(
+ final RtpContentMap receivedContentMap, final boolean expectVerification) {
Log.d(
Config.LOGTAG,
"receiveRtpContentMap("
@@ -1192,13 +1230,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
}
return;
}
- final Presence presence = id.getContact().getPresences().get(id.getWith().getResource());
- final ServiceDiscoveryResult disco = presence == null ? null : presence.getServiceDiscoveryResult();
- if (disco != null && disco.getFeatures().contains("urn:ietf:rfc:3264")) webRTCWrapper.setRFC3264(true);
final ListenableFuture future = receiveRtpContentMap(jinglePacket, false);
Futures.addCallback(
future,
- new FutureCallback() {
+ new FutureCallback<>() {
@Override
public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
receiveSessionInitiate(jinglePacket, rtpContentMap);
@@ -1287,7 +1322,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
receiveRtpContentMap(jinglePacket, this.omemoVerification.hasFingerprint());
Futures.addCallback(
future,
- new FutureCallback() {
+ new FutureCallback<>() {
@Override
public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
receiveSessionAccept(jinglePacket, rtpContentMap);
@@ -1420,8 +1455,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
+ ": ICE servers got discovered when session was already terminated. nothing to do.");
return;
}
+ final boolean includeCandidates = remoteHasSdpOfferAnswer();
try {
- setupWebRTC(media, iceServers);
+ setupWebRTC(media, iceServers, !includeCandidates);
} catch (final WebRTCWrapper.InitializationException e) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to initialize WebRTC");
webRTCWrapper.close();
@@ -1435,8 +1471,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
this.webRTCWrapper.setRemoteDescription(sdp).get();
addIceCandidatesFromBlackLog();
org.webrtc.SessionDescription webRTCSessionDescription =
- this.webRTCWrapper.setLocalDescription().get();
- prepareSessionAccept(webRTCSessionDescription);
+ this.webRTCWrapper.setLocalDescription(includeCandidates).get();
+ prepareSessionAccept(webRTCSessionDescription, includeCandidates);
} catch (final Exception e) {
failureToAcceptSession(e);
}
@@ -1452,7 +1488,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
}
- private void failureToPerformAction(final JinglePacket.Action action, final Throwable throwable) {
+ private void failureToPerformAction(
+ final JinglePacket.Action action, final Throwable throwable) {
if (isTerminated()) {
return;
}
@@ -1473,46 +1510,46 @@ public class JingleRtpConnection extends AbstractJingleConnection
}
private void prepareSessionAccept(
- final org.webrtc.SessionDescription initialWebRTCSessionDescription) {
+ final org.webrtc.SessionDescription webRTCSessionDescription,
+ final boolean includeCandidates) {
+ final SessionDescription sessionDescription =
+ SessionDescription.parse(webRTCSessionDescription.description);
+ final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription, false);
+ final ImmutableMultimap candidates;
+ if (includeCandidates) {
+ candidates = parseCandidates(sessionDescription);
+ } else {
+ candidates = ImmutableMultimap.of();
+ }
+ this.responderRtpContentMap = respondingRtpContentMap;
+ storePeerDtlsSetup(respondingRtpContentMap.getDtlsSetup().flip());
+ final ListenableFuture outgoingContentMapFuture =
+ prepareOutgoingContentMap(respondingRtpContentMap);
Futures.addCallback(
- webRTCWrapper.getRFC3264() ? Futures.withTimeout(iceGatheringComplete, 2, TimeUnit.SECONDS, JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE) : Futures.immediateFuture(null),
- new FutureCallback>() {
- @Override
- public void onSuccess(final Collection iceCandidates) {
- org.webrtc.SessionDescription webRTCSessionDescription =
- JingleRtpConnection.this.webRTCWrapper.getLocalDescription();
- final SessionDescription sessionDescription =
- SessionDescription.parse(webRTCSessionDescription.description);
- final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription, false);
- JingleRtpConnection.this.responderRtpContentMap = respondingRtpContentMap;
- storePeerDtlsSetup(respondingRtpContentMap.getDtlsSetup().flip());
- final ListenableFuture outgoingContentMapFuture =
- prepareOutgoingContentMap(respondingRtpContentMap);
- Futures.addCallback(
- outgoingContentMapFuture,
- new FutureCallback() {
- @Override
- public void onSuccess(final RtpContentMap outgoingContentMap) {
- sendSessionAccept(outgoingContentMap);
- webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
- }
-
- @Override
- public void onFailure(@NonNull Throwable throwable) {
- failureToAcceptSession(throwable);
- }
- },
- MoreExecutors.directExecutor());
- }
+ outgoingContentMapFuture,
+ new FutureCallback<>() {
+ @Override
+ public void onSuccess(final RtpContentMap outgoingContentMap) {
+ if (includeCandidates) {
+ Log.d(
+ Config.LOGTAG,
+ "including "
+ + candidates.size()
+ + " candidates in session accept");
+ sendSessionAccept(outgoingContentMap.withCandidates(candidates));
+ webRTCWrapper.resetPendingCandidates();
+ } else {
+ sendSessionAccept(outgoingContentMap);
+ webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
+ }
+ }
- @Override
- public void onFailure(@NonNull final Throwable throwable) {
- Log.e(Config.LOGTAG, "ICE gathering didn't finish clean: " + throwable);
- onSuccess(null);
- }
- },
- MoreExecutors.directExecutor()
- );
+ @Override
+ public void onFailure(@NonNull Throwable throwable) {
+ failureToAcceptSession(throwable);
+ }
+ },
+ MoreExecutors.directExecutor());
}
private void sendSessionAccept(final RtpContentMap rtpContentMap) {
@@ -1563,30 +1600,23 @@ public class JingleRtpConnection extends AbstractJingleConnection
+ ": delivered message to JingleRtpConnection "
+ message);
switch (message.getName()) {
- case "propose":
- receivePropose(from, Propose.upgrade(message), serverMessageId, timestamp);
- break;
- case "proceed":
- receiveProceed(from, Proceed.upgrade(message), serverMessageId, timestamp);
- break;
- case "retract":
- receiveRetract(from, serverMessageId, timestamp);
- break;
- case "reject":
- receiveReject(from, serverMessageId, timestamp);
- break;
- case "accept":
- receiveAccept(from, serverMessageId, timestamp);
- break;
- default:
- break;
+ case "propose" -> receivePropose(
+ from, Propose.upgrade(message), serverMessageId, timestamp);
+ case "proceed" -> receiveProceed(
+ from, Proceed.upgrade(message), serverMessageId, timestamp);
+ case "retract" -> receiveRetract(from, serverMessageId, timestamp);
+ case "reject" -> receiveReject(from, serverMessageId, timestamp);
+ case "accept" -> receiveAccept(from, serverMessageId, timestamp);
}
}
void deliverFailedProceed(final String message) {
Log.d(
Config.LOGTAG,
- id.account.getJid().asBareJid() + ": receive message error for proceed message ("+Strings.nullToEmpty(message)+")");
+ id.account.getJid().asBareJid()
+ + ": receive message error for proceed message ("
+ + Strings.nullToEmpty(message)
+ + ")");
if (transition(State.TERMINATED_CONNECTIVITY_ERROR)) {
webRTCWrapper.close();
Log.d(
@@ -1624,9 +1654,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
this.message.setTime(timestamp);
this.message.setCarbon(true); // indicate that call was accepted on other device
this.writeLogMessageSuccess(0);
- this.xmppConnectionService
- .getNotificationService()
- .cancelIncomingCallNotification();
+ this.xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
this.finish();
}
@@ -1764,14 +1792,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
private synchronized void ringingTimeout() {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": timeout reached for ringing");
switch (this.state) {
- case PROPOSED:
+ case PROPOSED -> {
message.markUnread();
rejectCallFromProposed();
- break;
- case SESSION_INITIALIZED:
+ }
+ case SESSION_INITIALIZED -> {
message.markUnread();
rejectCallFromSessionInitiate();
- break;
+ }
}
xmppConnectionService.getNotificationService().pushMissedCallNow(message);
}
@@ -1902,8 +1930,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
+ ": ICE servers got discovered when session was already terminated. nothing to do.");
return;
}
+ final boolean includeCandidates = remoteHasSdpOfferAnswer();
try {
- setupWebRTC(media, iceServers);
+ setupWebRTC(media, iceServers, !includeCandidates);
} catch (final WebRTCWrapper.InitializationException e) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to initialize WebRTC");
webRTCWrapper.close();
@@ -1912,8 +1941,8 @@ public class JingleRtpConnection extends AbstractJingleConnection
}
try {
org.webrtc.SessionDescription webRTCSessionDescription =
- this.webRTCWrapper.setLocalDescription().get();
- prepareSessionInitiate(webRTCSessionDescription, targetState);
+ this.webRTCWrapper.setLocalDescription(includeCandidates).get();
+ prepareSessionInitiate(webRTCSessionDescription, includeCandidates, targetState);
} catch (final Exception e) {
// TODO sending the error text is worthwhile as well. Especially for FailureToSet
// exceptions
@@ -1946,46 +1975,47 @@ public class JingleRtpConnection extends AbstractJingleConnection
}
private void prepareSessionInitiate(
- final org.webrtc.SessionDescription initialWebRTCSessionDescription, final State targetState) {
+ final org.webrtc.SessionDescription webRTCSessionDescription,
+ final boolean includeCandidates,
+ final State targetState) {
+ final SessionDescription sessionDescription =
+ SessionDescription.parse(webRTCSessionDescription.description);
+ final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription, true);
+ final ImmutableMultimap candidates;
+ if (includeCandidates) {
+ candidates = parseCandidates(sessionDescription);
+ } else {
+ candidates = ImmutableMultimap.of();
+ }
+ this.initiatorRtpContentMap = rtpContentMap;
+ final ListenableFuture outgoingContentMapFuture =
+ encryptSessionInitiate(rtpContentMap);
Futures.addCallback(
- webRTCWrapper.getRFC3264() ? Futures.withTimeout(iceGatheringComplete, 2, TimeUnit.SECONDS, JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE) : Futures.immediateFuture(null),
- new FutureCallback>() {
- @Override
- public void onSuccess(final Collection iceCandidates) {
- org.webrtc.SessionDescription webRTCSessionDescription =
- JingleRtpConnection.this.webRTCWrapper.getLocalDescription();
- final SessionDescription sessionDescription =
- SessionDescription.parse(webRTCSessionDescription.description);
- final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription, true);
- JingleRtpConnection.this.initiatorRtpContentMap = rtpContentMap;
- final ListenableFuture outgoingContentMapFuture =
- encryptSessionInitiate(rtpContentMap);
- Futures.addCallback(
- outgoingContentMapFuture,
- new FutureCallback() {
- @Override
- public void onSuccess(final RtpContentMap outgoingContentMap) {
- sendSessionInitiate(outgoingContentMap, targetState);
- webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
- }
-
- @Override
- public void onFailure(@NonNull final Throwable throwable) {
- failureToInitiateSession(throwable, targetState);
- }
- },
- MoreExecutors.directExecutor()
- );
- }
+ outgoingContentMapFuture,
+ new FutureCallback<>() {
+ @Override
+ public void onSuccess(final RtpContentMap outgoingContentMap) {
+ if (includeCandidates) {
+ Log.d(
+ Config.LOGTAG,
+ "including "
+ + candidates.size()
+ + " candidates in session initiate");
+ sendSessionInitiate(
+ outgoingContentMap.withCandidates(candidates), targetState);
+ webRTCWrapper.resetPendingCandidates();
+ } else {
+ sendSessionInitiate(outgoingContentMap, targetState);
+ webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
+ }
+ }
- @Override
- public void onFailure(@NonNull final Throwable throwable) {
- Log.e(Config.LOGTAG, "ICE gathering didn't finish clean: " + throwable);
- onSuccess(null);
- }
- },
- MoreExecutors.directExecutor()
- );
+ @Override
+ public void onFailure(@NonNull final Throwable throwable) {
+ failureToInitiateSession(throwable, targetState);
+ }
+ },
+ MoreExecutors.directExecutor());
}
private void sendSessionInitiate(final RtpContentMap rtpContentMap, final State targetState) {
@@ -2080,11 +2110,6 @@ public class JingleRtpConnection extends AbstractJingleConnection
send(jinglePacket);
}
- private void sendTransportInfo(final Multimap candidates) {
- // TODO send all candidates in one transport-info
- }
-
-
private void send(final JinglePacket jinglePacket) {
jinglePacket.setTo(id.with);
xmppConnectionService.sendIqPacket(id.account, jinglePacket, this::handleIqResponse);
@@ -2191,59 +2216,62 @@ public class JingleRtpConnection extends AbstractJingleConnection
public RtpEndUserState getEndUserState() {
switch (this.state) {
- case NULL:
- case PROPOSED:
- case SESSION_INITIALIZED:
+ case NULL, PROPOSED, SESSION_INITIALIZED -> {
if (isInitiator()) {
return RtpEndUserState.RINGING;
} else {
return RtpEndUserState.INCOMING_CALL;
}
- case PROCEED:
+ }
+ case PROCEED -> {
if (isInitiator()) {
return RtpEndUserState.RINGING;
} else {
return RtpEndUserState.ACCEPTING_CALL;
}
- case SESSION_INITIALIZED_PRE_APPROVED:
+ }
+ case SESSION_INITIALIZED_PRE_APPROVED -> {
if (isInitiator()) {
return RtpEndUserState.RINGING;
} else {
return RtpEndUserState.CONNECTING;
}
- case SESSION_ACCEPTED:
+ }
+ case SESSION_ACCEPTED -> {
final ContentAddition ca = getPendingContentAddition();
if (ca != null && ca.direction == ContentAddition.Direction.INCOMING) {
return RtpEndUserState.INCOMING_CONTENT_ADD;
}
return getPeerConnectionStateAsEndUserState();
- case REJECTED:
- case REJECTED_RACED:
- case TERMINATED_DECLINED_OR_BUSY:
+ }
+ case REJECTED, REJECTED_RACED, TERMINATED_DECLINED_OR_BUSY -> {
if (isInitiator()) {
return RtpEndUserState.DECLINED_OR_BUSY;
} else {
return RtpEndUserState.ENDED;
}
- case TERMINATED_SUCCESS:
- case ACCEPTED:
- case RETRACTED:
- case TERMINATED_CANCEL_OR_TIMEOUT:
+ }
+ case TERMINATED_SUCCESS, ACCEPTED, RETRACTED, TERMINATED_CANCEL_OR_TIMEOUT -> {
return RtpEndUserState.ENDED;
- case RETRACTED_RACED:
+ }
+ case RETRACTED_RACED -> {
if (isInitiator()) {
return RtpEndUserState.ENDED;
} else {
return RtpEndUserState.RETRACTED;
}
- case TERMINATED_CONNECTIVITY_ERROR:
+ }
+ case TERMINATED_CONNECTIVITY_ERROR -> {
return zeroDuration()
? RtpEndUserState.CONNECTIVITY_ERROR
: RtpEndUserState.CONNECTIVITY_LOST_ERROR;
- case TERMINATED_APPLICATION_FAILURE:
+ }
+ case TERMINATED_APPLICATION_FAILURE -> {
return RtpEndUserState.APPLICATION_ERROR;
- case TERMINATED_SECURITY_ERROR:
+ }
+ case TERMINATED_SECURITY_ERROR -> {
return RtpEndUserState.SECURITY_ERROR;
+ }
}
throw new IllegalStateException(
String.format("%s has no equivalent EndUserState", this.state));
@@ -2258,19 +2286,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
// be in SESSION_ACCEPTED even though the peerConnection has been torn down
return RtpEndUserState.ENDING_CALL;
}
- switch (state) {
- case CONNECTED:
- return RtpEndUserState.CONNECTED;
- case NEW:
- case CONNECTING:
- return RtpEndUserState.CONNECTING;
- case CLOSED:
- return RtpEndUserState.ENDING_CALL;
- default:
- return zeroDuration()
- ? RtpEndUserState.CONNECTIVITY_ERROR
- : RtpEndUserState.RECONNECTING;
- }
+ return switch (state) {
+ case CONNECTED -> RtpEndUserState.CONNECTED;
+ case NEW, CONNECTING -> RtpEndUserState.CONNECTING;
+ case CLOSED -> RtpEndUserState.ENDING_CALL;
+ default -> zeroDuration()
+ ? RtpEndUserState.CONNECTIVITY_ERROR
+ : RtpEndUserState.RECONNECTING;
+ };
}
public ContentAddition getPendingContentAddition() {
@@ -2305,9 +2328,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
} else if (initiatorContentMap != null) {
return initiatorContentMap.getMedia();
} else if (isTerminated()) {
- return Collections.emptySet(); //we might fail before we ever got a chance to set media
+ return Collections.emptySet(); // we might fail before we ever got a chance to set media
} else {
- return Preconditions.checkNotNull(this.proposedMedia, "RTP connection has not been initialized properly");
+ return Preconditions.checkNotNull(
+ this.proposedMedia, "RTP connection has not been initialized properly");
}
}
@@ -2327,35 +2351,29 @@ public class JingleRtpConnection extends AbstractJingleConnection
throw new IllegalStateException(String.format("%s has already been proposed", media));
}
// TODO add state protection - can only add while ACCEPTED or so
- Log.d(Config.LOGTAG,"adding media: "+media);
+ Log.d(Config.LOGTAG, "adding media: " + media);
return webRTCWrapper.addTrack(media);
}
public synchronized void acceptCall() {
switch (this.state) {
- case PROPOSED:
+ case PROPOSED -> {
cancelRingingTimeout();
acceptCallFromProposed();
- break;
- case SESSION_INITIALIZED:
+ }
+ case SESSION_INITIALIZED -> {
cancelRingingTimeout();
acceptCallFromSessionInitialized();
- break;
- case ACCEPTED:
- Log.w(
- Config.LOGTAG,
- id.account.getJid().asBareJid()
- + ": the call has already been accepted with another client. UI was just lagging behind");
- break;
- case PROCEED:
- case SESSION_ACCEPTED:
- Log.w(
- Config.LOGTAG,
- id.account.getJid().asBareJid()
- + ": the call has already been accepted. user probably double tapped the UI");
- break;
- default:
- throw new IllegalStateException("Can not accept call from " + this.state);
+ }
+ case ACCEPTED -> Log.w(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": the call has already been accepted with another client. UI was just lagging behind");
+ case PROCEED, SESSION_ACCEPTED -> Log.w(
+ Config.LOGTAG,
+ id.account.getJid().asBareJid()
+ + ": the call has already been accepted. user probably double tapped the UI");
+ default -> throw new IllegalStateException("Can not accept call from " + this.state);
}
}
@@ -2377,14 +2395,9 @@ public class JingleRtpConnection extends AbstractJingleConnection
return;
}
switch (this.state) {
- case PROPOSED:
- rejectCallFromProposed();
- break;
- case SESSION_INITIALIZED:
- rejectCallFromSessionInitiate();
- break;
- default:
- throw new IllegalStateException("Can not reject call from " + this.state);
+ case PROPOSED -> rejectCallFromProposed();
+ case SESSION_INITIALIZED -> rejectCallFromSessionInitiate();
+ default -> throw new IllegalStateException("Can not reject call from " + this.state);
}
}
@@ -2449,13 +2462,15 @@ public class JingleRtpConnection extends AbstractJingleConnection
finish();
}
- private void setupWebRTC(final Set media, final List iceServers) throws WebRTCWrapper.InitializationException {
+ private void setupWebRTC(
+ final Set media,
+ final List iceServers,
+ final boolean trickle)
+ throws WebRTCWrapper.InitializationException {
this.jingleConnectionManager.ensureConnectionIsRegistered(this);
- final Presence presence = id.getContact().getPresences().get(id.getWith().getResource());
- final ServiceDiscoveryResult disco = presence == null ? null : presence.getServiceDiscoveryResult();
- if (disco != null && disco.getFeatures().contains("urn:ietf:rfc:3264")) webRTCWrapper.setRFC3264(true);
- this.webRTCWrapper.setup(this.xmppConnectionService, AppRTCAudioManager.SpeakerPhonePreference.of(media));
- this.webRTCWrapper.initializePeerConnection(media, iceServers);
+ this.webRTCWrapper.setup(
+ this.xmppConnectionService, AppRTCAudioManager.SpeakerPhonePreference.of(media));
+ this.webRTCWrapper.initializePeerConnection(media, iceServers, trickle);
}
private void acceptCallFromProposed() {
@@ -2576,11 +2591,6 @@ public class JingleRtpConnection extends AbstractJingleConnection
sendTransportInfo(iceCandidate.sdpMid, candidate);
}
- @Override
- public void onIceGatheringComplete(Collection iceCandidates) {
- iceGatheringComplete.set(iceCandidates);
- }
-
@Override
public void onConnectionChange(final PeerConnection.PeerConnectionState newState) {
Log.d(
@@ -2635,7 +2645,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
final Throwable cause = Throwables.getRootCause(e);
webRTCWrapper.close();
if (isTerminated()) {
- Log.d(Config.LOGTAG, "failed to renegotiate. session was already terminated", cause);
+ Log.d(
+ Config.LOGTAG,
+ "failed to renegotiate. session was already terminated",
+ cause);
return;
}
Log.d(Config.LOGTAG, "failed to renegotiate. sending session-terminate", cause);
@@ -2720,7 +2733,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
prepareOutgoingContentMap(contentAdd);
Futures.addCallback(
outgoingContentMapFuture,
- new FutureCallback() {
+ new FutureCallback<>() {
@Override
public void onSuccess(final RtpContentMap outgoingContentMap) {
sendContentAdd(outgoingContentMap);
@@ -2763,7 +2776,6 @@ public class JingleRtpConnection extends AbstractJingleConnection
handleIqTimeoutResponse(response);
}
});
- this.webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
}
private void setLocalContentMap(final RtpContentMap rtpContentMap) {
@@ -2794,7 +2806,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
private SessionDescription setLocalSessionDescription()
throws ExecutionException, InterruptedException {
final org.webrtc.SessionDescription sessionDescription =
- this.webRTCWrapper.setLocalDescription().get();
+ this.webRTCWrapper.setLocalDescription(false).get();
return SessionDescription.parse(sessionDescription.description);
}
@@ -2919,9 +2931,6 @@ public class JingleRtpConnection extends AbstractJingleConnection
continue;
}
-
-
-
if (Arrays.asList("stun", "stuns", "turn", "turns")
.contains(type)
&& Arrays.asList("udp", "tcp").contains(transport)) {
@@ -2936,15 +2945,19 @@ public class JingleRtpConnection extends AbstractJingleConnection
// STUN URLs do not support a query section since M110
final String uri;
- if (Arrays.asList("stun","stuns").contains(type)) {
- uri = String.format("%s:%s:%s", type, IP.wrapIPv6(host),port);
+ if (Arrays.asList("stun", "stuns").contains(type)) {
+ uri =
+ String.format(
+ "%s:%s:%s",
+ type, IP.wrapIPv6(host), port);
} else {
- uri = String.format(
- "%s:%s:%s?transport=%s",
- type,
- IP.wrapIPv6(host),
- port,
- transport);
+ uri =
+ String.format(
+ "%s:%s:%s?transport=%s",
+ type,
+ IP.wrapIPv6(host),
+ port,
+ transport);
}
final PeerConnection.IceServer.Builder iceServerBuilder =
@@ -3087,6 +3100,14 @@ public class JingleRtpConnection extends AbstractJingleConnection
}
private boolean remoteHasVideoFeature() {
+ return remoteHasFeature(Namespace.JINGLE_FEATURE_VIDEO);
+ }
+
+ private boolean remoteHasSdpOfferAnswer() {
+ return remoteHasFeature(Namespace.SDP_OFFER_ANSWER);
+ }
+
+ private boolean remoteHasFeature(final String feature) {
final Contact contact = id.getContact();
final Presence presence =
contact.getPresences().get(Strings.nullToEmpty(id.with.getResource()));
@@ -3094,7 +3115,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
presence == null ? null : presence.getServiceDiscoveryResult();
final List features =
serviceDiscoveryResult == null ? null : serviceDiscoveryResult.getFeatures();
- return features != null && features.contains(Namespace.JINGLE_FEATURE_VIDEO);
+ return features != null && features.contains(feature);
}
private interface OnIceServersDiscovered {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java
index 0dc99a944e905d09bc0d3281f589ae014842218d..2e548d60ada2d52cb996e228e5be3c175cf8e331 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java
@@ -8,6 +8,7 @@ import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
@@ -197,6 +198,24 @@ public class RtpContentMap {
dt.senders, null, dt.transport.cloneWrapper())));
}
+ RtpContentMap withCandidates(
+ ImmutableMultimap candidates) {
+ final ImmutableMap.Builder contentBuilder =
+ new ImmutableMap.Builder<>();
+ for (final Map.Entry entry : this.contents.entrySet()) {
+ final String name = entry.getKey();
+ final DescriptionTransport descriptionTransport = entry.getValue();
+ final var transport = descriptionTransport.transport;
+ contentBuilder.put(
+ name,
+ new DescriptionTransport(
+ descriptionTransport.senders,
+ descriptionTransport.description,
+ transport.withCandidates(candidates.get(name))));
+ }
+ return new RtpContentMap(group, contentBuilder.build());
+ }
+
public IceUdpTransportInfo.Credentials getDistinctCredentials() {
final Set allCredentials = getCredentials();
final IceUdpTransportInfo.Credentials credentials =
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java
index dbfef237b216b7eb84e461140a26191b17af8537..7356477eed1883551cccf2c6dd4ff2308be4b582 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java
@@ -49,6 +49,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
@@ -104,7 +105,6 @@ public class WebRTCWrapper {
private final EventCallback eventCallback;
private final AtomicBoolean readyToReceivedIceCandidates = new AtomicBoolean(false);
- private final AtomicBoolean rfc3264 = new AtomicBoolean(false);
private final Queue iceCandidates = new LinkedList<>();
private final AppRTCAudioManager.AudioManagerEvents audioManagerEvents =
new AppRTCAudioManager.AudioManagerEvents() {
@@ -119,6 +119,8 @@ public class WebRTCWrapper {
private TrackWrapper localAudioTrack = null;
private TrackWrapper localVideoTrack = null;
private VideoTrack remoteVideoTrack = null;
+
+ private final SettableFuture iceGatheringComplete = SettableFuture.create();
private final PeerConnection.Observer peerConnectionObserver =
new PeerConnection.Observer() {
@Override
@@ -153,16 +155,16 @@ public class WebRTCWrapper {
@Override
public void onIceGatheringChange(
- PeerConnection.IceGatheringState iceGatheringState) {
+ final PeerConnection.IceGatheringState iceGatheringState) {
Log.d(EXTENDED_LOGGING_TAG, "onIceGatheringChange(" + iceGatheringState + ")");
if (iceGatheringState == PeerConnection.IceGatheringState.COMPLETE) {
- execute(() -> eventCallback.onIceGatheringComplete(iceCandidates));
+ iceGatheringComplete.set(null);
}
}
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
- if (readyToReceivedIceCandidates.get() && !rfc3264.get()) {
+ if (readyToReceivedIceCandidates.get()) {
eventCallback.onIceCandidate(iceCandidate);
} else {
iceCandidates.add(iceCandidate);
@@ -284,7 +286,9 @@ public class WebRTCWrapper {
}
synchronized void initializePeerConnection(
- final Set media, final List iceServers)
+ final Set media,
+ final List iceServers,
+ final boolean trickle)
throws InitializationException {
Preconditions.checkState(this.eglBase != null);
Preconditions.checkNotNull(media);
@@ -311,7 +315,7 @@ public class WebRTCWrapper {
.createAudioDeviceModule())
.createPeerConnectionFactory();
- final PeerConnection.RTCConfiguration rtcConfig = buildConfiguration(iceServers, rfc3264.get());
+ final PeerConnection.RTCConfiguration rtcConfig = buildConfiguration(iceServers, trickle);
final PeerConnection peerConnection =
requirePeerConnectionFactory()
.createPeerConnection(rtcConfig, peerConnectionObserver);
@@ -426,17 +430,17 @@ public class WebRTCWrapper {
}
private static PeerConnection.RTCConfiguration buildConfiguration(
- final List iceServers, boolean rfc3264) {
+ final List iceServers, final boolean trickle) {
final PeerConnection.RTCConfiguration rtcConfig =
new PeerConnection.RTCConfiguration(iceServers);
rtcConfig.tcpCandidatePolicy =
PeerConnection.TcpCandidatePolicy.DISABLED; // XEP-0176 doesn't support tcp
- if (rfc3264) {
+ if (trickle) {
rtcConfig.continualGatheringPolicy =
- PeerConnection.ContinualGatheringPolicy.GATHER_ONCE;
+ PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
} else {
rtcConfig.continualGatheringPolicy =
- PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
+ PeerConnection.ContinualGatheringPolicy.GATHER_ONCE;
}
rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.NEGOTIATE;
@@ -444,8 +448,9 @@ public class WebRTCWrapper {
return rtcConfig;
}
- void reconfigurePeerConnection(final List iceServers) {
- requirePeerConnection().setConfiguration(buildConfiguration(iceServers, rfc3264.get()));
+ void reconfigurePeerConnection(
+ final List iceServers, final boolean trickle) {
+ requirePeerConnection().setConfiguration(buildConfiguration(iceServers, trickle));
}
void restartIceAsync() {
@@ -466,7 +471,6 @@ public class WebRTCWrapper {
public void setIsReadyToReceiveIceCandidates(final boolean ready) {
readyToReceivedIceCandidates.set(ready);
- if (this.rfc3264.get()) return;
final int was = iceCandidates.size();
while (ready && iceCandidates.peek() != null) {
eventCallback.onIceCandidate(iceCandidates.poll());
@@ -477,13 +481,9 @@ public class WebRTCWrapper {
"setIsReadyToReceiveCandidates(" + ready + ") was=" + was + " is=" + is);
}
- public void setRFC3264(final boolean rfc3264) {
- // When this feature is enabled, do not trickle candidates
- this.rfc3264.set(rfc3264);
- }
-
- public boolean getRFC3264() {
- return this.rfc3264.get();
+ public void resetPendingCandidates() {
+ this.readyToReceivedIceCandidates.set(true);
+ this.iceCandidates.clear();
}
synchronized void close() {
@@ -616,11 +616,7 @@ public class WebRTCWrapper {
throw new IllegalStateException("Local video track does not exist");
}
- synchronized SessionDescription getLocalDescription() {
- return peerConnection.getLocalDescription();
- }
-
- synchronized ListenableFuture setLocalDescription() {
+ synchronized ListenableFuture setLocalDescription(final boolean waitForCandidates) {
this.setIsReadyToReceiveIceCandidates(false);
return Futures.transformAsync(
getPeerConnectionFuture(),
@@ -634,7 +630,16 @@ public class WebRTCWrapper {
new SetSdpObserver() {
@Override
public void onSetSuccess() {
- future.setFuture(getLocalDescriptionFuture());
+ final var delay =
+ waitForCandidates
+ ? Futures.catching(Futures.withTimeout(iceGatheringComplete, 2, TimeUnit.SECONDS, JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE), Exception.class, (Exception e) -> { return null; }, MoreExecutors.directExecutor())
+ : Futures.immediateVoidFuture();
+ final var delayedSessionDescription =
+ Futures.transformAsync(
+ delay,
+ v -> getLocalDescriptionFuture(),
+ MoreExecutors.directExecutor());
+ future.setFuture(delayedSessionDescription);
}
@Override
@@ -794,8 +799,6 @@ public class WebRTCWrapper {
Set availableAudioDevices);
void onRenegotiationNeeded();
-
- void onIceGatheringComplete(Collection iceCandidates);
}
private abstract static class SetSdpObserver implements SdpObserver {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java
index 925e63977b48eea645187c7938eb316ca8c1b025..8c8a9683d09f08bd1c0b666c0d59335a8b1250f2 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java
@@ -12,6 +12,7 @@ import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -155,6 +156,16 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
return transportInfo;
}
+ public IceUdpTransportInfo withCandidates(ImmutableCollection candidates) {
+ final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo();
+ transportInfo.setAttributes(new Hashtable<>(getAttributes()));
+ transportInfo.setChildren(this.getChildren());
+ for(final Candidate candidate : candidates) {
+ transportInfo.addChild(candidate);
+ }
+ return transportInfo;
+ }
+
public static class Credentials {
public final String ufrag;
public final String password;
diff --git a/src/main/res/menu/message_context.xml b/src/main/res/menu/message_context.xml
index 049c8a7e279d12c9f6e61874b9f87f30100af881..1ddcccd53b74dbabfc5910450a5c3df11fda7340 100644
--- a/src/main/res/menu/message_context.xml
+++ b/src/main/res/menu/message_context.xml
@@ -1,6 +1,11 @@
(Oder klicke lange, um den Standard wiederherzustellen)
Dein Server unterstützt die Veröffentlichung von Profilbildern nicht
- geflüstert
+ private Nachricht:
an %s
Private Nachricht an %s senden
Verbinden
@@ -1011,4 +1011,11 @@
Du versuchst, ein veraltetes Sicherungsdateiformat zu importieren
Hörbuch
Verbindung auf anderem Host wiederherstellen
+ Du hast dich von diesem Konto abgemeldet
+ Anmelden
+ Benachrichtigung ausblenden
+ Dein Kontakt verwendet nicht verifizierte Geräte. Scanne deren 2D-Barcode, um eine Verifizierung durchzuführen und aktive MITM-Angriffe zu verhindern.
+ Abmelden
+ Abgemeldet
+ Du verwendest nicht verifizierte Geräte. Scanne die 2D-Barcodes deiner anderen Geräte, um eine Verifizierung durchzuführen und aktive MITM-Angriffe zu verhindern.
\ No newline at end of file
diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml
index 573f31e728e3fc6aeee7b3f3108db136abe966ea..7393ed624683ea767d33a1a7601ba15565a559a7 100644
--- a/src/main/res/values-es/strings.xml
+++ b/src/main/res/values-es/strings.xml
@@ -537,7 +537,7 @@
Este campo es requerido
Corregir mensaje
Enviar mensaje corregido
- Ya has validado la huella digital de esta persona de forma segura confirmando su confianza. Seleccionando \'Hecho\', estás confirmando que %s es parte de esta conversación en grupo.
+ Ya has confiado la huella digital de esta persona. Al seleccionar “Listo” solo estás confirmando que %s es parte de este chat grupal.
Has deshabilitado esta cuenta
Error de seguridad: ¡Acceso a archivo inválido!
No se ha encontrado ninguna aplicación para compartir la URI
@@ -1025,4 +1025,11 @@
Estás intentando importar un formato de copia de seguridad obsoleto
Audiolibro
Reconectarse a otros hosts
+ Has salido de esta cuenta
+ Iniciar sesión
+ Ocultar la notificación
+ Su contacto utiliza dispositivos no verificados. Escanea su código de barras en 2D para realizar la verificación e impedir ataques MITM activos.
+ Desconectarse
+ Desconectado
+ Está utilizando dispositivos no verificados. Escanea el código de barras 2D en tus otros dispositivos para realizar la verificación e impedir los ataques MITM activos.
\ No newline at end of file
diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml
index c8c7441e2c9e9dfbbab0a1516a19226c96f1621f..bfa004720fb4b3716cab324b5643bdd2a7c3fb0f 100644
--- a/src/main/res/values-gl/strings.xml
+++ b/src/main/res/values-gl/strings.xml
@@ -536,7 +536,7 @@
Este campo é requerido
Correxir mensaxe
Enviar mensaxe correxida
- Xa validaches as impresións dixitais destas persoas de xeito seguro para confiar nelas. Ao escoller \"Feito\" estás simplemente confirmando que %s é parte desta conversa en grupo.
+ Xa confiaches na impresión dixital destas persoas. Ao escoller \"Feito\" estás simplemente confirmando que %s é parte desta conversa en grupo.
Desactivou esta conta
Fallo de seguridade: Acceso non válido ao ficheiro!
Non se atopou unha app para compartir URI
@@ -1014,4 +1014,13 @@
Estás intentando importar un ficheiro de apoio co formato antigo
Audiolibro
Volver conectar noutro servidor
+ Pechaches a sesión desta conta
+ Acceder
+ Agochar notificación
+ O teu contacto usa dispositivos non verificados. Escanea o seu código de barras 2D para verficalo e impedir ataques MITM.
+ Saír da sesión
+ Sesión pechada
+ Estás a usar dispositivos non verificados. Escanea os códigos de barras 2D nos teus outros dispositivos para verificalos e impedir ataques MITM.
+ Informar de spam e bloquear conta
+ Informar de spam
diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml
index 8ee669b42cb1f899739c1c37c06ba132e427abc2..c35b8f171b2e3a6efe861e751c7047c580cd0194 100644
--- a/src/main/res/values-it/strings.xml
+++ b/src/main/res/values-it/strings.xml
@@ -1025,4 +1025,11 @@
Stai tentando di importare un formato di file di backup obsoleto
Audiolibro
Riconnetti su altro host
+ Ti sei disconnesso da questo profilo
+ Accedi
+ Nascondi notifica
+ Il tuo contatto usa dispositivi non verificati. Scansiona il suo codice a barre 2D per effettuare la verifica e impedire attacchi MITM attivi.
+ Disconnetti
+ Disconnesso
+ Stai usando dispositivi non verificati. Scansiona il codice a barre 2D nei tuoi altri dispositivi per effettuare la verifica e impedire attacchi MITM attivi.
\ No newline at end of file
diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml
index 8ef68c6459e31fb3a09c0f920307bf5c83fb9cef..b23c50b96efbf777a9ec9f62ab1f7aeac9cf437f 100644
--- a/src/main/res/values-ja/strings.xml
+++ b/src/main/res/values-ja/strings.xml
@@ -967,4 +967,18 @@
アバターを削除
Tor使用中のため通話できません
ビデオ通話切替
+ このアカウントをログアウトしました
+ ルート
+ ビデオ通話を却下する
+ 着信通話 (%s) · %s
+ XMPPアカウント
+ グループ
+ 選択したファイルは、旧式のファイル形式ので復元できません
+ グループを検索
+ 発信通話 (%s) · %s
+ ログアウトしました
+ 発信通話 · %s
+ オーディオブック
+ 談話室の発見は<a href=https://search.jabber.network>search.jabber.network</a>というサービスを利用します.<br><br>利用するとIPアドレスと検索語はそのサービスに送信されます。詳細についてはそのサービスの<a href=https://search.jabber.network/privacy>個人情報保護方針</a>を参照してください。
+ 自分で保存したバックアップしか復元しないでください!
diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml
index 393fda62190f3c977b84f9a1ec0d6399d397ebc8..f558800f4fe0feed32762b28e17427ff601112d4 100644
--- a/src/main/res/values-nl/strings.xml
+++ b/src/main/res/values-nl/strings.xml
@@ -31,10 +31,7 @@
%d min. geleden
- %d ongelezen gesprek
-
-
- %d ongelezen gesprekken
-
versturen…
Bericht aan het ontsleutelen. Even geduld…
@@ -168,7 +165,8 @@
Avatar publiceren
OpenPGP-publieke sleutel publiceren
OpenPGP-publieke sleutel verwijderen
- Weet je zeker dat je je OpenPGP-publieke sleutel uit je aanwezigheidsaankondiging wil verwijderen?\nJe contacten zullen je geen OpenPGP-versleutelde berichten meer kunnen sturen.
+ Weet je zeker dat je je publieke OpenPGP-sleutel uit je aankondiging van aanwezigheid wilt verwijderen\?
+\nJe contacten zullen je geen versleutelde OpenPGP-berichten meer kunnen sturen.
OpenPGP-publieke sleutel gepubliceerd.
Account inschakelen
Stem opnemen
@@ -816,4 +814,4 @@
Kan video niet schakelen.
Onversleuteld document
Accountregistraties zijn niet ondersteund
-
+
\ No newline at end of file
diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml
index 1df5bce622a8ef755b1d010228478464e1de4bfd..97121e8b3cc051b28f02b39715d134295d48564b 100644
--- a/src/main/res/values-ro-rRO/strings.xml
+++ b/src/main/res/values-ro-rRO/strings.xml
@@ -545,7 +545,7 @@
Acest câmp este obligatoriu
Corecție mesaj
Trimite text corectat
- Ați validat deja amprenta acestei persoane într-un mod securizat pentru a confirma încrederea. Selectând \'Gata\' doar confirmați că %s ia parte la această discuție de grup.
+ Ați avut deja încredere în amprenta acestei persoane. Selectând \'Gata\' doar confirmați că %s ia parte la această discuție de grup.
Ați dezactivat acest cont
Eroare de securitate.: Acces fișier invalid!
Nu s-a găsit nici o aplicație care să partajeze adresa
@@ -1033,4 +1033,13 @@
Încercați să importați un fișier copie de rezervă format vechi
Carte audio
Reconectat pe altă gazdă
+ V-ați deconectat de la acest cont
+ Conectați-vă
+ Ascunde notificare
+ Persoana de contact utilizează dispozitive neverificate. Scanați codul de bare 2D al acestora pentru a efectua verificarea și a împiedica atacurile MITM active.
+ Deconectare
+ Deconectat
+ Folosiți dispozitive neverificate. Scanați codul de bare 2D pe celelalte dispozitive pentru a efectua verificarea și a împiedica atacurile MITM active.
+ Raportează spam și blochează spamerul
+ Raportează spam
\ No newline at end of file
diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml
index a62f5c792a3a2ed847ffe2f3527d408bbcfac506..fe454a0791594f24e466b0c15c8bf11eba7a831a 100644
--- a/src/main/res/values-ru/strings.xml
+++ b/src/main/res/values-ru/strings.xml
@@ -1035,4 +1035,11 @@
- %1$d пропущенных вызовов от %2$d контактов
Аудиокнига
+ Вы вышли из этой учётной записи
+ Войти
+ Скрыть уведомление
+ Ваш контакт использует неподтверждённые устройства. Отсканируйте его штрих-код для проверки и предотвращения атаки посредника.
+ Выйти
+ Деавторизован
+ Вы используете неподтверждённые устройства. Отсканируйте штрих-код на подтверждённом устройстве для проверки и предотвращения атаки посредника.
\ No newline at end of file
diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml
index 24c6e6e5f5c4aa54d85b52e1621b13e860f3f542..676e24ffd5d45248ad972c4cf4d4f8c984878e29 100644
--- a/src/main/res/values-sv/strings.xml
+++ b/src/main/res/values-sv/strings.xml
@@ -2,14 +2,14 @@
Inställningar
Ny konversation
- Kontoinställningar
+ Hantera konton
Hantera konto
Stäng konversation
Kontaktdetaljer
Gruppchattdetaljer
- Kanaldetaljer
+ Gruppdetaljer
Lägg till konto
- Ändra namn
+ Redigera namn
Lägg till i kontakter
Ta bort kontakt
Blockera kontakt
@@ -36,23 +36,23 @@
skickar…
Avkrypterar meddelande. Vänta…
OpenPGP-krypterat meddelande
- Nick används redan
+ Smeknamn används redan
Ogiltigt smeknamn
Admin
Ägare
Moderator
Deltagare
Besökare
- Vill du ta bort %s från din kontaktlista? Konversationer med denna kontakt kommer inte tas bort.
+ Vill du ta bort %s från din kontaktlista\? Konversationer med denna kontakt kommer inte att tas bort.
Vill du blockera %s från att skicka dig meddelanden?
- Vill du avblockera %s och tillåta denne att skicka dig meddelanden?
+ Vill du avblockera %s och tillåta användaren att skicka dig meddelanden\?
Blockera alla kontakter från %s?
Avblockera alla kontakter från %s?
Kontakt blockerad
Blockerad
- Vill du ta bort %s som ett bokmärke? Konversationer med detta bokmärke kommer inte tas bort.
- Registrera nytt konto på servern
- Byt lösenord på server
+ Vill du ta bort %s som ett bokmärke\? Konversationer med detta bokmärke kommer inte att tas bort.
+ Registrera ett nytt konto på servern
+ Byt lösenord på servern
Dela med…
Börja konversation
Bjud in kontakt
@@ -60,7 +60,7 @@
Kontakter
Kontakt
Avbryt
- Sätt
+ Ange
Lägg till
Ändra
Ta bort
@@ -69,27 +69,29 @@
Spara
Ok
%1$s har kraschat
- Att använda ditt XMPP-konto för att skicka in \'stack traces\' hjälper den pågående utvecklingen av %1$s.
+ Att använda ditt XMPP-konto för att skicka in \'stack traces\', hjälper den pågående utvecklingen av %1$s.
Skicka nu
Fråga aldrig igen
- Kunde inte ansluta till konto
+ Kunde inte ansluta till kontot
Kunde inte ansluta till flera konton
Tryck för att hantera dina konton
Bifoga fil
- Vill du lägga till den här saknade kontakten i din kontaktlista?
+ Vill du lägga till den här kontakten i din kontaktlista\?
Lägg till kontakt
sändning misslyckades
Förbereder att skicka bild
Förbereder att skicka bilder
- Delar filer. Vänta…
+ Delar filer. Var god vänta…
Rensa historik
Rensa konversationshistorik
- Vill du radera alla meddelanden i den här konversationen?\n\nVarning: Det här påverkar inte meddelanden som finns lagrade på andra enheter eller servrar.
+ Vill du ta bort alla meddelanden i den här konversationen\?
+\n
+\nVarning: Det här påverkar inte meddelanden som finns lagrade på andra enheter eller servrar.
Ta bort fil
Är du säker på att du vill ta bort den här filen\?
\n
\nVarning: Den här åtgärden kommer inte att ta bort kopior av den här filen som finns lagrad på andra enheter eller servrar.
- Stäng denna konversation efteråt
+ Stäng den här konversation efteråt
Välj enhet
Skicka okrypterat meddelande
Skicka meddelande
@@ -97,79 +99,83 @@
Skicka OMEMO-krypterat meddelande
Skicka v\\OMEMO-krypterat meddelande
Skicka OpenPGP-krypterat meddelande
- Nytt smeknamn används
+ Nytt smeknamn är nu i bruk
Skicka okrypterat
Dekrypteringen misslyckades. Du har kanske inte rätt privat nyckel.
OpenKeychain
- OpenKeychain för att kryptera och avkryptera dina publika nycklar.
Programmet är licensierat under GPLv3+ och finns tillgänglig via F-Droid and Google Play.
(Var god och starta om %1$s efter installationen.)]]>
+ %1$s använder <b>OpenKeychain</b> för att kryptera och avkryptera dina publika nycklar.<br><br>Programmet är licensierat under GPLv3+, och finns tillgängligt via F-Droid och Google Play.<br><br><small>(Var god starta om %1$s efter installationen.)</small>
Starta om
Installera
- Installera OpenKeychain
+ Var god installera OpenKeychain
erbjuder…
väntar…
- Ingen OpenPGP-nyckel funnen
- Det gick inte att kryptera ditt meddelande eftersom att din kontakt inte har annonserat sin publika nyckel.\n\nVänligen be din kontakt att sätta upp OpenPGP.
- Inga OpenPGP-nycklar funna
- Det gick inte att kryptera ditt meddelande eftersom att din kontakt inte har annonserat sina publika nycklar.\n\nVänligen be din kontakt att sätta upp OpenPGP.
- Generellt
+ Ingen OpenPGP-nyckel hittades
+ Det gick inte att kryptera ditt meddelande, eftersom att din kontakt inte offentligjort sin publika nyckel.
+\n
+\nVänligen be din kontakt att sätta upp OpenPGP.
+ Inga OpenPGP-nycklar hittades
+ Det gick inte att kryptera ditt meddelande, eftersom att din kontakt inte har offentliggjort sina publika nycklar.
+\n
+\nVänligen be din kontakt att sätta upp OpenPGP.
+ Allmänt
Acceptera filer
Acceptera automatiskt filer som är mindre än…
- Bifogningar
- Notifiering
+ Bilagor
+ Avisering
Vibrera
- Vibrera när meddelande tagits emot
- LED notifieringar
- Blinka med notifieringsljuset när ett meddelande tagits emot
- Meddelandesignal
+ Vibrera när ett nytt meddelande tas emot
+ LED-avisering
+ Blinka med aviseringsljuset när ett nytt meddelande tas emot
+ Ringsignal
Aviseringsljud
- Aviseringsljud för nya meddelande
+ Aviseringsljud för nya meddelanden
Ringsignal för inkommande samtal
- Notifieringsfrist
- Tidsgräns för hur länge notiser ska tystas efter att aktivitet har upptäckts på en av dina andra enheter.
+ Tidsfrist
+ Tidsfrist för hur länge aviseringar ska vara tysta efter att aktivitet har upptäckts på en av dina andra enheter.
Avancerat
- Skicka aldrig krasch-rapporter
- Genom att skicka in kraschrapporter hjälper du den pågående utvecklingen
+ Skicka aldrig kraschrapporter
+ Genom att skicka in kraschrapporter hjälper du till med utvecklingen av appen
Bekräfta meddelanden
Låt dina kontakter veta när du har mottagit och läst deras meddelanden
Förhindra skärmdumpar
- Dölj innehållet från applikationen i applikationsväxlaren och blockera skärmdumpar
+ Dölj innehållet från applikationen i appväxlaren, och blockera skärmdumpar
Gränssnitt
- OpenKeychain genererade ett fel.
- Dålig krypterings-nyckel.
+ OpenKeychain orsakade ett fel.
+ Ogiltig nyckel för kryptering.
Acceptera
Ett fel har inträffat
Fel
Ditt konto
- Skicka tillgänglighetsuppdatering
- Ta emot tillgänglighetsuppdateringar
- Be om tillgänglighetsuppdateringar
+ Skicka närvarouppdateringar
+ Ta emot närvarouppdateringar
+ Be om närvarouppdateringar
Välj bild
Ta ny bild
- Tillåt abonnemangsbegäran i förväg
- Filen du valt är inte en bild
+ Bevilja prenumerationsförfrågan i förebyggande syfte
+ Filen som du har valt, är inte en bild
Det gick inte att konvertera bildfilen
- Filen hittas ej
- Generellt I/O-fel. Du kanske fick slut på plats?
- Applikationen som du använde för att välja den här bilden tillhandahöll inte tillräckligt med rättigheter för att läsa filen.
+ Filen kunde ej hittas
+ Generellt I/O-fel. Du kanske fick slut på lagringsutrymme\?
+ Applikationen som du använde för att välja den här bilden, tillhandahöll inte tillräckligt med rättigheter för att läsa filen.
\n
\nAnvänd en annan filhanterare för att välja en bild.
- Applikationen du använde för att dela den här filen tillhandahöll inte tillräckligt med behörigheter.
+ Appen som du använde för att dela den här filen, tillhandahöll inte tillräckligt med rättigheter.
Okänd
Tillfälligt inaktiverad
- Online
+ Uppkopplad
Ansluter\u2026
- Offline
- Otillåten
- Server ej funnen
+ Nedkopplad
+ Ej behörig
+ Servern hittades inte
Ingen anslutning
- Registreringsfel
- Användarnamn används redan
- Registrering klar
- Registrering stöds ej av server
- Ogiltigt registreringstoken
+ Registreringen misslyckades
+ Användarnamnet används redan
+ Registreringen genomförd
+ Registrering stöds inte av servern
+ Ogiltig registreringstoken
TLS-förhandling misslyckades
- Domänen kan inte verifieras
- Kränkning av policy
+ Domän kan inte verifieras
+ Policyöverträdelse
Inkompatibel server
Strömningsfel
Fel vid öppning av ström
@@ -179,44 +185,45 @@
OMEMO
Ta bort konto
Inaktivera tillfälligt
- Publicera avatarbild
- Publicera OpenPGP publik nyckel
- Ta bort OpenPGP publik nyckel
- Är du säker på att du vill ta bort din OpenPGP publik nyckel från din tillgänglighetsuppdatering?\nDina kontakter kommer inte längre att kunna skicka dig OpenPGP-krypterade meddelande.
+ Publicera visningsbild
+ Publicera publik OpenPGP-nyckel
+ Ta bort publik OpenPGP-nyckel
+ Är du säker på att du vill ta bort din publika OpenPGP-nyckel från ditt närvarotillkännagivande\?
+\nDina kontakter kommer inte längre att kunna skicka OpenPGP-krypterade meddelande till dig.
OpenPGP-nyckel har publicerats.
Aktivera konto
- Är du säker på att du vill ta bort ditt konto\? Om du tar bort ditt konto raderas hela din konversationshistorik
+ Är du säker på att du vill ta bort ditt konto\? Om du tar bort ditt konto, tas hela din konversationshistorik bort
Spela in röst
XMPP-adress
Blockera XMPP-adress
användarnamn@exempel.se
Lösenord
- Detta är inte en giltig XMPP-adress
+ Det här är inte en giltig XMPP-adress
Slut på minne. Bilden är för stor
- Vill du lägga till %s i din enhets kontakter?
- Server-info
- XEP-0313: Message Archive
+ Vill du lägga till %s i din kontaktbok\?
+ Serverinfo
+ XEP-0313: MAM
XEP-0280: Message Carbons
XEP-0352: Client State Indication
XEP-0191: Blocking Command
XEP-0237: Roster Versioning
XEP-0198: Stream Management
XEP-0215: External Service Discovery
- XEP-0163: PEP (Avatarbilder / OMEMO)
- XEP-0363: Ladda upp via HTTP
+ XEP-0163: PEP (Avatars / OMEMO)
+ XEP-0363: HTTP File Upload
XEP-0357: Push
tillgänglig
otillgänglig
- Annonsering om publik nyckel saknas
- senast sedd just nu
- senast sedd för en minut sedan
- senast sedd %d minuter sedan
- senast sedd för en timme sedan
- senast sedd %d timmar sedan
- senast sedd för en dag sedan
- senast sedd %d dagar sedan
- Krypterat meddelande. Installera OpenKeychain för att dekryptera meddelandet.
- Nytt OpenPGP krypterat meddelande hittades
+ Tillkännagivanden om offentliga nyckel saknas
+ sågs senast nyss
+ sågs senast för en minut sedan
+ sågs senast för %d minuter sedan
+ sågs senast för en timme sedan
+ sågs senast för %d timmar sedan
+ sågs senast för en dag sedan
+ sågs senast för %d dagar sedan
+ Krypterat meddelande. Installera OpenKeychain för att avkryptera meddelandet.
+ Nya OpenPGP-krypterade meddelande hittades
OpenPGP-nyckel-ID
OMEMO-fingeravtryck
v\\OMEMO-fingeravtryck
@@ -225,96 +232,102 @@
Andra enheter
Lita på OMEMO-fingeravtryck
Hämtar nycklar …
- Klar
+ Färdig
Avkryptera
Sök
- Fyll i kontakt
+ Ange kontakt
Ta bort kontakt
- Se kontaktdetaljer
+ Visa kontaktdetaljer
Blockera kontakt
Avblockera kontakt
Skapa
Välj
Kontakten finns redan
Gå med
- rum@konferens.exempel.se/användarnamn
+ rum@konferens.exempel.se/smeknamn
rum@konferens.exempel.se
Spara som bokmärke
Ta bort bokmärke
- Förstör gruppchat
+ Förstör gruppchatt
Förstör kanal
- Är du säker på att du vill förstöra den här gruppchatten?\n\nVarning: Gruppchatten kommer att tas bort helt från servern.
- Är du säker på att du vill förstöra den här publika chattgruppen?\n\nVarning: Den här gruppchatten kommer att tas bort helt från servern.
- Det gick inte att ta bort gruppchatten
- Det gick inte att ta bort kanalen
+ Är du säker på att du vill förstöra den här gruppchatten\?
+\n
+\nVarning: Gruppchatten kommer att fullständigt raderas från servern.
+ Är du säker på att du vill förstöra den här publika chattgruppen\?
+\n
+\nVarning: Den här gruppchatten kommer att fullständigt raderas från servern.
+ Det gick inte att förstöra gruppchatten
+ Det gick inte att förstöra kanalen
Redigera ämnet för gruppchatten
Ämne
- Går med i gruppchatt …
+ Går med i gruppchatt…
Lämna
- Kontakten lade till dig i sin kontaktlista
- Addera tillbaka
- %s har läst hit
- %s har läst till den här punkten
- %1$s +%2$d andra har läst till den här punkten
- Alla har läst fram till hit
+ Kontakten har lagt till dig i sin kontaktlista
+ Lägg också till kontakt
+ %s har läst fram till denna punkt
+ %s har läst fram till denna punkt
+ %1$s +%2$d andra har läst fram till denna punkt
+ Alla har läst fram till denna punkt
Publicera
Tryck på visningsbilden för att välja en bild från galleriet
Publicerar…
- Servern kunde inte publicera
+ Servern avvisade din publicering
Det gick inte att konvertera din bild
- Kunde inte spara avatarbild till disk
- (Eller tryck länge för att få tillbaks förvald)
- Din server stödjer inte publicering av visningsbilder
- privat meddelande
+ Det gick inte att spara ner visningsbilden till telefonen
+ (Långtryck för att återställa standardinställningen)
+ Din server stöder inte publicering av visningsbilder
+ viskade
till %s
Skicka privat meddelande till %s
Anslut
- Detta konto finns redan
+ Det här kontot existerar redan
Nästa
Session etablerad
Hoppa över
- Inaktivera notifieringar
+ Inaktivera aviseringar
Aktivera
- Gruppchatten kräver lösenord
- Fyll i lösenord
- Var god begär närvarouppdateringar från din kontakt först.\n\nDetta kommer att användas för att avgöra vilken chattapplikationen din kontakt använder.
+ Gruppchatten kräver ett lösenord
+ Ange lösenord
+ Vänligen begär närvarouppdateringar från din kontakt först.
+\n
+\nDet här kommer att användas för att avgöra vilken chattklient som din kontakt använder.
Begär nu
Ignorera
Varning: Att skicka detta utan ömsesidiga närvarouppdateringar kan orsaka oväntade problem.\n\nGå till \"Kontaktuppgifter\" för att verifiera dina närvaroprenumerationer.
Säkerhet
- Tillåt korrigeringar av meddelanden
- Tillåt att dina kontakter kan ändra sina meddelanden i efterhand
+ Tillåt meddelandekorrigering
+ Tillåt dina kontakter att retroaktivt redigera sina meddelanden
Expertinställningar
- Var försiktig med dem
+ Var försiktig med dessa
Om %s
Tysta timmar
Starttid
Sluttid
- Aktivera tysta timmar
- Notifieringar kommer vara tysta under tysta timmar
+ Aktivera Tysta timmar
+ Aviseringar kommer att tystas under Tysta timmar
Annat
OMEMO-fingeravtryck kopierat till urklipp
Du är avstängd från denna gruppchatt
- Denna gruppchatt är endast för medlemmar
+ Den här gruppchatten är endast till för medlemmar
Resursbegränsning
Du har blivit sparkad från den här gruppchatten
Gruppchatten stängdes ner
- Du är inte längre med i denna gruppchatt
+ Du är inte längre med i den här gruppchatten
använder konto %s
huseras hos %s
- Kontrollerar %s på webbserver
+ Kontrollerar %s på HTTP-värd
Du är inte ansluten. Försök igen senare
- Kontrollera filstorleken på %s
+ Kontrollera filstorleken för %s
Kontrollera filstorlek för %1$s på %2$s
Meddelandealternativ
- Citera
+ Citat
Klistra in som citat
- Kopiera orginal-URL
+ Kopiera den ursprungliga URL:n
Skicka igen
Fil-URL
Kopierade URL till urklipp
Kopierade XMPP-adress till urklipp
- Kopierade felmeddelande till urklipp
+ Kopierade felmeddelandet till urklipp
webbadress
Scanna 2D-streckkod
Visa 2D-streckkod
@@ -323,93 +336,94 @@
Bekräfta
Försök igen
Förgrundstjänst
- Förehindrar operativsystemet att ta ner uppkopplingen
+ Förhindrar att operativsystemet avbryter din anslutning
Skapa säkerhetskopia
- Säkerhetskopians filer lagras i %s
- Skapar filer för säkerhetskopia
+ Säkerhetskopierade filer kommer att lagras i %s
+ Skapar säkerhetskopior
Din säkerhetskopia har skapats
- Säkerhetskopians filer har lagrats i %s
+ De säkerhetskopierade filerna har lagrats i %s
Återställer säkerhetskopia
Din säkerhetskopia har återställts
Glöm inte att aktivera kontot.
Välj fil
- Tar emot %1$s (%2$d%% klart)
+ Tar emot %1$s (%2$d%% slutfört)
Ladda ner %s
Ta bort %s
fil
Öppna %s
- skickar (%1$d%% klart)
- Förbereder för delning av fil
- %s erbjuden för nedladdning
+ skickar (%1$d%% slutfört)
+ Förbereder delning av fil
+ %s erbjuds för nedladdning
Avbryt överföring
det gick inte att dela fil
filöverföring avbruten
Fil borttagen
- Ingen applikation som kunde öppna filen hittades
- Ingen applikation som kunde öppna länken hittades
- Ingen applikation som kunde visa kontakten hittades
+ Ingen app kunde hittas för att öppna filen
+ Ingen app kunde hittas för att öppna länken
+ Ingen app kunde hittas för att visa kontakten
Dynamiska etiketter
Visa skrivskyddade taggar under kontakter
- Aktivera notifieringar
+ Aktivera aviseringar
Ingen gruppchattserver hittades
Det gick inte att skapa gruppchatten
- Kontots avatarbild
+ Visningsbild för kontot
Kopiera OMEMO-fingeravtryck till urklipp
- Regenerera OMEMO-nyckel
+ Skapa ny OMEMO-nyckel
Rensa enheter
- Är du säker på att du vill ta bort alla andra enheter från OMEMO-tillkännagivandet? Nästa gång dina enheter ansluter, kommer de att tillkännage sig själva igen, men de kanske inte får meddelanden som skickas under tiden.
+ Är du säker på att du vill ta bort alla andra enheter från OMEMO-tillkännagivandet\? Nästa gång dina enheter ansluter kommer de att tillkännage sig själva igen, men kan kanske inte ta emot meddelanden som skickas under tiden.
Det finns inga användbara nycklar tillgängliga för den här kontakten.\nDet gick inte att hämta nya nycklar från servern. Kanske är det något fel på din kontakts server?
- Det finns inga användbara nycklar tillgängliga för den här kontakten.\nSe till att ni båda har närvaroprenumeration.
+ Det finns inga användbara nycklar tillgängliga för den här kontakten.
+\nSe till att båda har närvaroprenumeration.
Något gick fel
Hämtar historik från server
- Ingen mer historik på server
+ Ingen mer historik på servern
Uppdaterar…
- Lösenord bytt!
- Kunde inte byta lösenord
- Byt lösenord
+ Lösenordet ändrat!
+ Det gick inte att ändra lösenordet
+ Ändra lösenordet
Nuvarande lösenord
Nytt lösenord
- Lösenord kan inte vara tomma
+ Lösenordet får inte vara tomt
Aktivera alla konton
- Deaktivera alla konton
+ Inaktivera alla konton
Utför åtgärden med
Ingen anknytning
- Offline
+ Frånkopplad
Utstött
Medlem
Avancerat läge
Bevilja medlemsprivilegier
Återkalla medlemsprivilegier
- Bevilja administratörsbehörighet
- Återkalla administratörsbehörighet
+ Bevilja administratörsbehörigheter
+ Återkalla administratörsbehörigheter
Bevilja ägarprivilegier
Återkalla ägarprivilegier
Ta bort från gruppchatt
Ta bort från kanal
- Kunde inte ändra tillhörigheten för %s
- Förbjud från gruppchatt
- Förbjud från kanal
- Du försöker ta bort %s från en offentlig kanal. Det enda sättet att göra det är att förbjuda den användaren för alltid.
+ Kunde inte ändra anknytningen till %s
+ Blockera från gruppchatt
+ Blockera från kanal
+ Du försöker att ta bort %s från en offentlig kanal. Det enda sättet att göra det på, är att stänga av användaren för alltid.
Bannlys nu
Kunde inte ändra rollen för %s
- Privat gruppchattskonfiguration
- Publik kanalkonfiguration
- Privat, medlemsskap krävs
+ Konfiguration för privat gruppchatt
+ Konfiguration för publik kanal
+ Privat, endast medlemmar
Gör XMPP-adresser synliga för alla
Gör kanalen modererad
- Du deltar ej
+ Du deltar inte
Ändrade gruppchattalternativ!
Det gick inte att ändra alternativ för gruppchatt
Aldrig
Tills vidare
Snooza
Svara
- Läsmarkera
- Input
- Skicka med enter
+ Markera som läst
+ Inmatning
+ Skicka meddelanden med Enter-tangenten
Använd Enter-tangenten för att skicka meddelandet. Du kan alltid använda Ctrl+Enter för att skicka meddelandet, även om det här alternativet är inaktiverat.
- Visa enter-knappen
- Byt ut emoticons-tangenten mot en enter-tangent
+ Visa Enter-tangenten
+ Byt ut Emoji-tangenten mot en Enter-tangent
ljud
video
bild
@@ -417,36 +431,36 @@
PDF-dokument
Android-app
Kontakt
- Avatarbild har publicerats!
+ Visningsbilden har publicerats!
Skickar %s
Erbjuder %s
- Dölj ej anslutna
- %s skriver …
- %s har slutat skriva
- %s skriver …
- %s har slutat skriva
- Skriv-notifieringar
+ Dölj frånkopplade
+ %s skriver…
+ %s har slutat att skriva
+ %s skriver…
+ %s har slutat att skriva
+ Skrivaviseringar
Låt dina kontakter veta när du skriver meddelande till dem
Skicka position
Visa position
- Ingen applikation hittades för att visa platsdata
+ Ingen app hittades för att kunna visa platsen
Position
Konversation stängd
Lämnade privat gruppchatt
Lämnade publik kanal
- Lita inte på systemets CAs
- Alla certifikat måste manuellt godkännas
+ Lita inte på systemets certifikatutfärdare
+ Alla certifikat måste godkännas manuellt
Ta bort certifikat
- Ta bort manuellt accepterade certifikat
- Inga manuellt accepterade certifikat
- Ta bort certifikat
+ Ta bort manuellt godkända certifikat
+ Inga manuellt godkända certifikat
+ Ta bort certifikaten
Ta bort val
Avbryt
- %d certifikat borttaget
- %d certifikat borttagna
- Ersätt \"Skicka\"-knappen med snabbåtgärd
+ Ersätt \"Skicka\"-knappen med en snabbfunktion
Snabbfunktion
Ingen
Senast använd
@@ -456,175 +470,181 @@
%1$s har lämnat gruppchatten
Användarnamn
Användarnamn
- Inte ett giltigt användanamn
- Nerladdning gick fel: Server hittades inte
- Nerladdning gick fel: Filen hittades inte
- Nerladdningen gick fel: Kunder inte ansluta till server
- Nerladdning gick fel: Kunde inte skriva fil
+ Det här är inte ett giltigt användarnamn
+ Nedladdning misslyckades: Servern hittades inte
+ Nedladdning misslyckades: Filen hittades inte
+ Nedladdning misslyckades: Kunde inte ansluta till värden
+ Nedladdning misslyckades: Kunde inte skriva filen
Nedladdning misslyckades: Ogiltig fil
- Tor-nätverk ej tillgängligt
- Bind-fel
- Den här servern ansvarar inte för den här domänen
- Sönder
+ Tor-nätverket är inte tillgängligt
+ Bindningsfel
+ Servern ansvarar inte för den här domänen
+ Trasig
Tillgänglighet
- Frånvarande när enheten är låst
- Visa som frånvarande när enheten är låst
- Upptagen i ljudlöst läge
- Visa som Upptagen i ljudlöst läge
- Hantera vibrationsläge som tyst läge
- Visa som Upptagen när enheten är satt på att endast vibrera
+ Borta när enheten är låst
+ Visa som Borta när enheten är låst
+ Upptagen i tyst läge
+ Visa som upptagen när enheten är i tyst läge
+ Behandla vibrera-läget som tyst läge
+ Visa som upptagen när enheten är satt till att endast vibrera
Utökade anslutningsinställningar
- Visa val av servernamn och port vid inställning av konto
- xmpp.example.com
+ Visa värdnamn och portinställningar när du skapar ett konto
+ xmpp.exempel.se
Logga in med certifikat
- Det gick inte att analysera certifikatet
+ Det gick inte att tolka certifikatet
Arkiveringsinställningar
- Arkiveringsinställningar på servern
- Hämtar arkiveringsinställningar, vänta …
+ Arkiveringsinställningar på serversidan
+ Hämtar arkiveringsinställningar. Var god vänta…
Det gick inte att hämta arkiveringsinställningar
- CAPTCHA behövs
- Skriv i texten från bilden ovan
+ CAPTCHA krävs
+ Skriv in texten från bilden ovanför
Otillförlitlig certifikatkedja
XMPP-adressen matchar inte certifikatet
Förnya certifikat
- Misslyckades med att hämta OMEMO-nyckel!
- Verifierade OMEMO-nyckel med certifikat!
- Din enhet stödjer inte val av klientcertifikat!
+ Fel vid hämtning av OMEMO-nyckeln!
+ Verifierade OMEMO-nyckeln med certifikatet!
+ Din enhet stöder inte valet av klientcertifikat!
Anslutning
- Ansluten via Tor
- Tunnla alla anslutningar genom Tor-nätverket. Kräver Orbot
- Servernamn
+ Anslut via Tor
+ Tunnla alla anslutningar via Tor-nätverket. Kräver Orbot
+ Värdnamn
Port
Server- eller .onion-adress
- Inte ett giltigt portnummer
- Inte ett giltigt servernamn
+ Det här är inte ett giltigt portnummer
+ Det här är inte ett giltigt värdnamn
%1$d av %2$d konton anslutna
- %d meddelande
- %d meddelanden
- Ladda fler meddelanden
+ Läs in fler meddelanden
Fil delad med %s
Bild delad med %s
- Bilder som delats med %s
- Text som delats med %s
- Ge %1$s åtkomst till extern lagring
- Ge %1$s åtkomst till kameran
+ Bilder delades med %s
+ Text delades med %s
+ Bevilja %1$s åtkomst till extern lagring
+ Bevilja %1$s åtkomst till kameran
Synkronisera med kontakter
- %1$s vill ha behörighet att komma åt din adressbok för att matcha den med din XMPP-kontaktlista.\nDetta visar dina kontakters fullständiga namn och visningsbilder.\n\n%1$s kommer bara att läsa din adressbok och matcha den lokalt, utan att ladda upp något till din server.
- Notifiera för alla meddelanden
- Notis endast vid omnämnande
- Notifieringar deaktiverade
- Notifieringar pausade
+ %1$s vill ha behörighet att komma åt din adressbok för att matcha den med din XMPP-kontaktlista.
+\nDetta kommer att visa dina kontakters fullständiga namn och visningsbilder.
+\n
+\n%1$s kommer bara att läsa din adressbok och matcha den lokalt, utan att ladda upp något till din server.
+ Avisering för alla meddelanden
+ Avisering endast vid ett omnämnande
+ Aviseringar inaktiverade
+ Aviseringar pausade
Bildkomprimering
- Tips: Använd \"Välj fil\" istället för \"Välj bild\" för att skicka enskilda bilder okomprimerade, oavsett denna inställning.
+ Tips: Använd \"Välj fil\", istället för \"Välj bild\", för att skicka enskilda okomprimerade bilder, oavsett den här inställningen.
Alltid
Endast stora bilder
Batterioptimeringar aktiverade
- Din enhet använder kraftiga batterioptimeringar för %1$s, vilket kan leda till försenade aviseringar eller till och med förlust av meddelanden.\nVi rekommenderar att du inaktiverar dem.
- Din enhet använder kraftiga batterioptimeringar för %1$s, vilket kan leda till försenade aviseringar eller till och med förlust av meddelanden.
+ Din enhet använder kraftiga batterioptimeringar för %1$s, vilket kan leda till försenade aviseringar, eller till och med förlorade meddelanden.
+\nDet är rekommenderat att inaktivera batterioptimeringar för appen.
+ Din enhet använder kraftiga batterioptimeringar för %1$s, vilket kan leda till försenade aviseringar, eller till och med förlorade meddelanden.
\n
\nDu kommer nu att bli ombedd att inaktivera dem.
- Deaktivera
- The valda området är för stort
- (Inget konto aktiverat)
- Detta fält måste fyllas i
- Korrigera meddelanden
+ Inaktivera
+ Det valda området är för stort
+ (Inga aktiverade konton)
+ Detta fält är obligatoriskt
+ Korrigera meddelande
Skicka korrigerat meddelande
- Du har redan validerat den här personens fingeravtryck säkert för att bekräfta förtroendet. Genom att välja \"Klar\" bekräftar du bara att %s är en del av den här gruppchatten.
- Du har deaktiverat detta konto
+ Du har redan validerat den här personens fingeravtryck på ett säkert sätt, för att bekräfta förtroendet. Genom att välja \"Klar\" bekräftar du bara att %s är en del av den här gruppchatten.
+ Du har inaktiverat det här kontot
Säkerhetsfel: Ogiltig filåtkomst!
- Ingen applikation hittades för att dela URI
- Dela URI med …
- Acceptera och gå vidare
- En guide har skapats för kontoskapande på conversations.im.
-\nNär du väljer conversations.im som leverantör kommer du att kunna kommunicera med användare av andra leverantörer genom att ge dem din fullständiga XMPP-adress.
+ Ingen app hittades för att dela URI
+ Dela URI med…
+ Acceptera och fortsätt
+ En guide är inställd för att skapa ett konto på conversations.im.
+\nNär du väljer conversations.im som leverantör, kan du kommunicera med andra användare som använder en annan leverantör, genom att ge dem din fullständiga XMPP-adress.
Din fullständiga XMPP-adress kommer att vara: %s
Skapa konto
- Använd min egen leverantör
- Välj användarnamn
+ Använd min egna leverantör
+ Välj ditt användarnamn
Hantera tillgänglighet manuellt
- Ställ in din tillgänglighet när du redigerar ditt statusmeddelande.
+ Ange din tillgänglighet när du redigerar ditt statusmeddelande.
Statusmeddelande
- Tillgänglig
- Online
+ Tillgänglig för chatt
+ Uppkopplad
Borta
Ej tillgänglig
Upptagen
Ett säkert lösenord har genererats
- Din enhet stödjer inte deaktivering av batterioptimeringar
- Registreringfel: Försök igen senare
- Registreringsfel: Lösenordet är för svagt
+ Din enhet har inte stöd för att välja bort batterioptimering
+ Registreringen misslyckades: Försök igen senare
+ Registreringen misslyckades: Lösenordet är för svagt
Välj deltagare
- Skapar gruppchatt …
+ Skapar gruppchatt…
Bjud in igen
- Deaktivera
+ Inaktivera
Kort
Medium
Lång
- Gör användandet offentligt
+ Tillkännage användandet
Låter dina kontakter veta när du använder Conversations
- Privatliv
+ Integritet
Tema
- Välj färgschema
+ Välj färgpalett
Automatisk
Ljus
Mörk
Det gick inte att ansluta till OpenKeychain
- Denna enhet används inte längre
+ Den här enheten används inte längre
Dator
Mobiltelefon
Surfplatta
Webbläsare
Konsoll
Betalning krävs
- Ge behörighet till att använda Internet
- Jag
- Kontakt ber om tillgänglighetsuppdateringar
+ Bevilja behörighet att använda Internet
+ Mig
+ Kontakt ber om närvaroprenumeration
Tillåt
- Saknar rättigheter för access till %s
- Fjärrserver hittas inte
+ Ingen behörighet för att komma åt %s
+ Fjärrserver hittades inte
Timeout för fjärrserver
- Kunde inte uppdatera konto
+ Kunde inte uppdatera kontot
Rapportera den här XMPP-adressen för spam.
- Ta bort OMEMO identiteter
- Återskapa dina OMEMO-nycklar. Alla dina kontakter måste verifiera dig igen. Använd endast det här som en sista utväg.
+ Ta bort OMEMO-identiteter
+ Regenerera dina OMEMO-nycklar. Alla dina kontakter måste verifiera dig igen. Använd bara det här som en sista utväg.
Ta bort valda nycklar
- Du måste vara ansluten för att publicera din avatarbild.
+ Du behöver vara ansluten för att publicera din visningsbild.
Visa felmeddelande
Felmeddelande
- Databesparing
- Ditt operativsystem begränsar åtkomsten till Internet i bakgrunden för %1$s. För att få aviseringar om nya meddelanden bör du tillåta obegränsad åtkomst för %1$s, när databesparing är på.\n %1$s kommer fortfarande att anstränga sig för att spara data när det är möjligt.
- Din enhet stöder inte inaktivering av databesparing för %1$s.
+ Databesparing aktiverad
+ Ditt operativsystem begränsar %1$s från att komma åt Internet i bakgrunden. För att få aviseringar om nya meddelanden bör du tillåta %1$s obegränsad åtkomst när datasparläget är aktiverat
+\n%1$s kommer fortfarande att försöka spara data när det är möjligt.
+ Din enhet har inte stöd för att inaktivera datasparläget för %1$s.
Det gick inte att skapa en tillfällig fil
- Denna enhet har verifierats
+ Den här enheten har verifierats
Kopiera fingeravtryck
Du har verifierat alla OMEMO-nycklar i din ägo
- Streckkoden innehåller inte fingeravtryck för denna konversation.
+ Streckkoden innehåller inga fingeravtryck för den här konversationen.
Verifierade fingeravtryck
- Använd kameran för att scanna en kontakts streckkod
- Vänta medans nycklar hämtas
+ Använd kameran för att skanna en kontakts streckkod
+ Var god vänta på att nycklarna ska hämtas
Dela som streckkod
- Dela som XMPP URI
- Dela som HTTP länk
- Blint förtroende före verifiering
+ Dela som XMPP-URI
+ Dela som HTTP-länk
+ Blind tillit före verifiering
Lita på nya enheter från icke-verifierade kontakter, men begär manuell bekräftelse av nya enheter för verifierade kontakter.
- Att blint lita på OMEMO-nycklar, innebär att det skulle kunna vara någon annan eller att någon annan har fått åtkomst.
+ Att blint lita på OMEMO-nycklar, innebär att användaren skulle kunna vara någon annan, och att en tredje part kan tjuvlyssna.
Ej betrodd
Ogiltig 2D-streckkod
- Töm cache-mapp (används av kameraapplikationen)
+ Töm cache-mapp (används av kameraappen)
Rensa cache
- Rensa private lagring
- Rensa privat lagring där filer lagras (De kan om-laddas från servern)
- Jag följde denna länk från en trovärdig källa
- Du håller på att verifiera OMEMO-nyckeln för %1$s efter att du följt en länk. Detta är endast säkert om du följde länken från en trovärdig källa där endast %2$s kan ha publiserat denna länk.
- Du är på väg att verifiera OMEMO-nycklarna för ditt eget konto. Detta är bara säkert om du följde den här länken från en pålitlig källa där bara du kunde ha publicerat den här länken.
+ Rensa privat lagring
+ Rensa privat lagring där filer förvaras (De kan laddas ner på nytt från servern)
+ Jag följde den här länken från en trovärdig källa
+ Du håller på att verifiera OMEMO-nycklarna för %1$s efter att ha klickat på en länk. Detta är bara säkert om du följde den här länken från en pålitlig källa, där endast %2$s haft möjlighet att publicerat den här länken.
+ Du är på väg att verifiera OMEMO-nycklarna för ditt eget konto. Detta är bara säkert om du följde den här länken från en pålitlig källa, där bara du kunde ha publicerat den här länken.
Fortsätt
Verifiera OMEMO-nycklar
Visa inaktiva
Dölj inaktiva
- Lita ej på enhet
- Är du säker på att du vill ta bort verifieringen av den här enheten?\nDen här enheten och meddelanden från den kommer att markeras som \"Ej betrodd\".
+ Misstro enhet
+ Är du säker på att du vill ta bort verifieringen av den här enheten\?
+\nDen här enheten, och meddelanden från den, kommer att markeras som \"Ej betrodda\".
- %d sekund
- %d sekunder
@@ -650,40 +670,40 @@
- %d månader
Automatisk borttagning av meddelanden
- Ta automatiskt bort meddelanden från denna enhet som är äldre än den konfigurerade tidsramen.
+ Ta automatiskt bort meddelanden från den här enheten som är äldre än den angivna tidsramen.
Krypterar meddelande
- Hämtar inte meddelanden på grund av inställningen för borttagning av gamla meddelanden.
+ Hämtar inte meddelanden på grund av lokal lagringsperiod.
Komprimerar video
- Korresponderande konversationer är stängda.
+ Motsvarande konversationer avslutades.
Kontakt blockerad.
- Notifieringar från främlingar
- Meddela för meddelanden och samtal från främlingar.
+ Aviseringar från främlingar
+ Motta aviseringar för meddelanden och samtal från främlingar.
Mottagna meddelanden från främlingar
Blockera främling
- Blockera hel domän
- online just nu
- Försök dekryptera igen
- Sessionsfel
+ Blockera hela domänen
+ uppkopplad just nu
+ Försök att dekryptera igen
+ Session misslyckades
Nedgraderad SASL-mekanism
- Servern kräver registrering via webbplatsen
+ Servern kräver registrering på webbplatsen
Öppna webbsida
- Ingen applikation hittades för att kunna öppna webbsidan
- Se upp-notifikationer
- Visa se upp-notifikationer
+ Ingen app hittades för att kunna öppna webbplatsen
+ \"Heads-up\"-meddelanden
+ Visa \"Heads-up\"-aviseringar
Idag
Igår
- Bekräfta värdnamn med DNSSEC
+ Validera värdnamn med DNSSEC
Servercertifikat som innehåller det validerade värdnamnet anses vara verifierade
- Certifikatet innehåller ej en XMPP-adress
+ Certifikatet innehåller ingen XMPP-adress
delvis
Spela in video
Kopiera till urklipp
- Meddelande kopierat till urklipp
+ Meddelandet har kopierats till urklipp
Meddelande
Privata meddelanden är inaktiverade
- Skyddade applikationer
- För att fortsätta ta emot aviseringar även när skärmen är avstängd, måste du lägga till Conversations i listan över skyddade appar.
- Godkänn okänt certifikat?
+ Skyddade appar
+ För att fortsätta ta emot aviseringar, även när skärmen är avstängd, måste du lägga till Conversations i listan över skyddade appar.
+ Acceptera okänt certifikat\?
Servercertifikatet är inte signerat av en känd certifikatutfärdare.
Acceptera servernamn som inte matchar?
Servern kunde inte autentisera som \"%s\". Certifikatet är endast giltigt för:
@@ -693,14 +713,15 @@
QR-läsaren behöver åtkomst till kameran
Bläddra till botten
Bläddra ner efter att du har skickat ett meddelande
- Redigera Statusmeddelande
+ Redigera statusmeddelande
Redigera statusmeddelande
Inaktivera kryptering
- %1$s kan inte skicka krypterade meddelanden till %2$s. Detta kan bero på att din kontakt använder en föråldrad server eller klient som inte kan hantera OMEMO.
+ %1$s kan inte skicka krypterade meddelanden till %2$s. Detta kan bero på att din kontakt använder en föråldrad server, eller en klient som inte kan hantera OMEMO.
Det gick inte att hämta enhetslistan
Det gick inte att hämta krypteringsnycklar
- Tips: I vissa fall kan detta åtgärdas genom att lägga till varandra i era respektive kontaktlistor.
- Är du säker på att du vill inaktivera OMEMO-kryptering för den här konversationen?\nDetta gör att din serveradministratör kan läsa dina meddelanden, men det kan också vara det enda sättet att kommunicera med människor som använder äldre klienter.
+ Tips: I vissa fall kan det här åtgärdas genom att lägga till varandra i era respektive kontaktlistor.
+ Är du säker på att du vill inaktivera OMEMO-kryptering för den här konversationen\?
+\nDet innebär att din serveradministratör kan läsa dina meddelanden, men det kan också vara det enda sättet att kommunicera med personer som använder föråldrade klienter.
Inaktivera nu
Utkast:
OMEMO-kryptering
@@ -713,10 +734,10 @@
På som standard
Av som standard
Liten
- Mellan
+ Medium
Stor
- Meddelandet är inte krypterat för den här enheten.
- Misslyckades med att dekryptera OMEMO-meddelandet.
+ Meddelandet krypterades inte för den här enheten.
+ Det gick inte att dekryptera OMEMO-meddelandet.
ångra
Platsdelning är inaktiverat
Lås position
@@ -728,9 +749,9 @@
Visa plats
Dela
Det gick inte att starta inspelningen
- Var god dröj …
- Ge %1$s tillgång till mikrofonen
- Söka i meddelanden
+ Var god dröj…
+ Bevilja %1$s tillgång till mikrofonen
+ Sök meddelanden
GIF
Visa konversation
Dela plats-tillägget
@@ -738,16 +759,16 @@
Kopiera XMPP-adress
HTTP-fildelning för S3
Direktsök
- Gruppkonversationens visningsbild
- Värden stöder inte visningsbilder för gruppkonversationer
- Endast ägaren kan ändra visningsbilden för gruppkonversationen
+ Gruppchattens visningsbild
+ Värden stöder inte visningsbilder för gruppchattar
+ Endast ägaren kan ändra visningsbilden för gruppchatten
Kontaktnamn
Smeknamn
Namn
Att ange ett namn är valfritt
Gruppchattens namn
- Kunde inte att spara inspelningen
- Förgrundsservice
+ Det gick inte att spara inspelningen
+ Förgrundstjänst
Statusinformation
Anslutningsproblem
Meddelanden
@@ -760,19 +781,19 @@
Videokompression
Visa media
Deltagare
- Mediautforskare
+ Mediebläddrare
Videokvalitet
- Mellan (360p)
+ Medium (360p)
Hög (720p)
avbruten
Du håller redan på att skriva ett meddelande.
Välj ett land
telefonnummer
- Bekräfta ditt telefonnummer
+ Verifiera ditt telefonnummer
tillbaka
Ja
Nej
- Bekräftar …
+ Bekräftar…
Okänt nätverksfel.
För många försök
Du använder en föråldrad version av denna app.
@@ -916,38 +937,38 @@
- %d missat samtal
- %d missade samtal
- Denna gruppchatt har förstörts
- Öppna tangentbordet och placera markören i sökfältet vid \"Starta konversation\"-sidan
- Denna notifikationskategori används för att visa en permanent notifikation som visar att %1$s är igång.
- Inställningar för meddelandenotifikationer
- Inställningar för notifikationer om inkommande samtal
- Vikt, ljud, vibration
+ Den här gruppchatten har förstörts
+ Vid \"Starta konversation\"-skärmen, visa tangentbordet och placera markören i sökfältet
+ Den här aviseringskategorin används för att visa ett permanent meddelande som indikerar att %1$s körs.
+ Inställningar för meddelandeaviseringar
+ Aviseringsinställningar för inkommande samtal
+ Betydelse, ljud, vibrera
Kunde inte upprätta en säker anslutning.
Ogiltig inmatning
Tillfälligt otillgänglig. Försök igen om en stund.
Ingen kontakt med nätverket.
Prova igen om %s
- Begär sms …
- Pinkoden är fel.
- Pinkoden som vi har skickat dig har utgått.
+ Begär sms…
+ Den angivna PIN-koden är felaktig.
+ PIN-koden som vi skickade till dig, är inte längre giltig.
Kunde inte hitta servern.
- Klistrade automatiskt in pinkod från urklipp.
- Ange din sexsiffriga pinkod.
- Är du säker på att du vill avbryta registreringen\?
- Ange ditt telefonnummer.
+ Möjligtvis automatiskt inklistrad PIN från urklipp.
+ Var god ange din 6-siffriga pinkod.
+ Är du säker på att du vill avbryta registreringsproceduren\?
+ Var god ange ditt telefonnummer.
Sök länder
Verifiera %s
- Vi har sänt dig ett annat SMS med en sexsiffrig kod.
- Enge din sexsifftiga pinkod nedan.
+ Vi har skickat ytterligare ett SMS med en 6-siffrig kod.
+ Var god ange den 6-siffriga pinkoden nedan.
Skicka SMS igen
Skicka SMS igen (%s)
- Vänta (%s)
- Vi kommer att verifiera telefonnumret
%s
Är det okej, eller vill du ändra numret\?
+ Var god vänta (%s)
+ Vi kommer att verifiera telefonnumret
%s
Är det här okej, eller vill du ändra telefonnumret\?
%s är inte ett giltigt telefonnummer.
- Filen utelämnad av säkerhetsskäl.
- Lägre kvalitet ger mindre filer
+ Filen har utelämnats på grund av säkerhetsöverträdelse.
+ Lägre kvalitet resulterar i mindre filer
Funktionen är inte implementerad
- Felaktig landskod
+ Ogiltig landskod
Du är begränsad
Använd redigera-knappen för att ange ditt namn.
Ingen butiksapp installerad.
@@ -976,16 +997,16 @@
Kunde inte kontakta servern.
Något blev fel när din förfrågan hanterades.
Okänt svar från servern.
- Vi har sänt dig ett SMS till %s.
- Quicksy skickar ett SMS för att bekräfta ditt telefonnummer (operatörsavgifter kan tillkomma). Ange din landskod och ditt telefonnumer:
+ Vi har skickat ett SMS till %s.
+ Quicksy kommer att skicka ett SMS (operatörsavgifter kan tillkomma) för att verifiera ditt telefonnummer. Ange din landskod och ditt telefonnummer:
Missade samtal
- Använd Dela plats-plugin istället för den inbyggda kartan
- Denna notifikationskategori används för att visa en notifikation om det är problem med att kontakta ett konto.
- Denna notifikationsgrupp används för att visa notifikationer som inte ska utlösa något ljud, till exempel när man är aktiv på en annan enhet (respitperiod).
- Inkomatibel klient
+ Använd Dela plats-tillägget istället för den inbyggda kartan
+ Den här aviseringskategorin används för att visa ett meddelande om det uppstår problem med att ansluta till ett konto.
+ Den här aviseringsgruppen används för att visa meddelanden som inte ska utlösa något ljud. Till exempel när du är aktiv på en annan enhet (Nådperiod).
+ Inkompatibel klient
Synkronisera bokmärken
- Du lämnade gruppchatten av tekniska skäl
- multimedia-fil
+ Du lämnade den här gruppchatten på grund av tekniska skäl
+ multimediafil
Använd inte funktionen för återställning av säkerhetskopia för att försöka klona (köra samtidigt) en installation. Återställning av en säkerhetskopia är avsedd för migreringar eller om du har tappat bort den ursprungliga enheten.
- %1$d missat samtal från %2$d kontakt
@@ -998,5 +1019,5 @@
Kunde inte tolka inbjudan
Servern har inte stöd för att skapa inbjudningar
Det finns inget stöd för att registrera konto
- Sätt \"autojoin\"-flagga när du går med i eller lämnar en MUC och reagerar på ändringar gjorda av andra klienter.
+ Ställ in \"autojoin\"-flaggan när du går in i, eller lämnar en MUC, samt reagera på ändringar gjorda av andra klienter.
\ No newline at end of file
diff --git a/src/main/res/values-uk/strings.xml b/src/main/res/values-uk/strings.xml
index 8e5d0e49b0d58f1196db3c8eac5ce95c9d9aecfe..256b90b521b9b7a7ed7bf82f686e23fb20c2cf69 100644
--- a/src/main/res/values-uk/strings.xml
+++ b/src/main/res/values-uk/strings.xml
@@ -28,8 +28,8 @@
щойно
1 хвилину тому
%d хвилин тому
- відправляю…
- Розшифровую повідомлення. Зачекайте, будь ласка…
+ надсилання…
+ Повідомлення розшифровується. Зачекайте, будь ласка…
Повідомлення зашифроване OpenPGP
Таке прізвисько вже використовується
Неприйнятне прізвисько
@@ -68,13 +68,13 @@
Не вдалося з\'єднатися з обліковим записом
Не вдалося з\'єднатися з обліковими записами
Торкніться, щоб керувати обліковими записами
- Долучити файл
+ Прикріпити файл
Цього контакту немає у Вашому списку. Бажаєте додати його\?
Додати контакт
не вдалося надіслати
Підготовка до передачі зображення
Підготовка до передачі зображень
- Поширюю файли. Зачекайте, будь ласка…
+ Файли надсилаються. Зачекайте, будь ласка…
Стерти історію
Стерти історію розмов
Дійсно вилучити всі повідомлення цієї розмови\?
@@ -99,8 +99,8 @@
Перезапустити
Встановити
Будь ласка, встановіть OpenKeychain
- пропоную…
- чекаю…
+ пропонування…
+ очікування…
Не знайдено ключа OpenPGP
Не вдалося зашифрувати повідомлення, оскільки контакт не повідомляє свій публічний ключ.
\n
@@ -125,7 +125,7 @@
Час, протягом якого сповіщення вимкнені після виявлення активності на іншому пристрої.
Розширені
Не надсилати звіти про збої
- Надсилаючи траси стеку викликів, Ви допомагаєте розробляти застосунок
+ Надсилаючи звіти про збої, Ви допомагаєте розвивати застосунок
Підтвердження отримання повідомлень
Повідомляти співрозмовників, що Ви отримали й прочитали їхні повідомлення
Інтерфейс користувача
@@ -216,7 +216,7 @@
Цифровий підпис v\\OMEMO
Інші пристрої
Довіряти цифровим підписам OMEMO
- Отримую ключі…
+ Отримання ключів…
Зроблено
Розшифрувати
Пошук
@@ -286,7 +286,7 @@
Дозволити контактам редагувати свої повідомлення після надсилання
Експертні налаштування
Будь ласка, будьте обережними з цим
- Близько %s
+ Про %s
Години тиші
Час початку
Час завершення
@@ -363,9 +363,9 @@
Для цього контакту відсутні потрібні ключі.
\nПеревірте, що ви обмінялися інформацією про присутність.
Щось пішло не так
- Отримую історію з сервера
+ Отримання історії з сервера
Більше немає історії на сервері
- Оновлюю…
+ Оновлення…
Пароль змінено!
Не вдалося змінити пароль
Змінити пароль
@@ -415,7 +415,7 @@
аудіо
відео
зображення
- PDF документ
+ документ PDF
Програма Android
Контакт
Піктограму користувача опубліковано!
@@ -477,7 +477,7 @@
Не вдалося розпізнати сертифікат
Налаштування збереження
Налаштування збереження на стороні сервера
- Отримую налаштування збереженя. Будь ласка, зачекайте…
+ Отримання налаштувань збереження. Будь ласка, зачекайте…
Не вдалося отримати налаштування збереження
Потрібно розв\'язати головоломку
Уведіть текст із зображення вище
@@ -523,7 +523,7 @@
Це обов\'язкове поле
Відредагувати
Відредаговане повідомлення
- Ви вже довіряєте цій особі. Вибираючи «Зроблено», Ви лише підтверджуєте, що %s є учасником групи.
+ Ви вже довіряєте цифровому підпису цієї особи. Вибираючи «Зроблено», Ви лише підтверджуєте, що %s є учасником групи.
Ви вимкнули цей обліковий запис
Помилка безпеки: неправильний доступ до файлу!
Не знайдено застосунку, щоб поділитися URI
@@ -723,7 +723,7 @@
Відкріпити розташування
Скопіювати місцезнаходження
Поділитися місцезнаходженням
- Шлях
+ Напрямки
Поділитися місцезнаходженням
Показати місцезнаходження
Поділитися
@@ -865,7 +865,7 @@
Керувати правами
Шукати учасників
Файл надто великий
- Долучити
+ Прикріпити
Пошук каналу
Шукати канали
Можливе порушення приватності!
@@ -938,7 +938,7 @@
Закрити розмову
Збій %1$s
- Надсилаючи зі свого облікового запису XMPP траси стеку викликів, Ви допомагаєте розробляти %1$s.
+ Надсилаючи зі свого облікового запису XMPP звіти про збої, Ви допомагаєте розвивати %1$s.
Мелодія для вхідних викликів
Заборонити знімки екрана
Ховати вміст застосунку при перемиканні програм і заборонити знімки екрана
@@ -992,7 +992,7 @@
%1$s потребує дозволу на доступ до контактів, щоб порівняти їх з Вашими XMPP-контактами.
\nТаким чином можна буде показувати піктограми і повні імена користувачів.
\n
-\n%1$s співставлення інформації про контакти відбувається локально, нічого не завантажується на сервер.
+\n%1$s лише прочитає Вашу адресну книгу і зіставлятиме інформацію про контакти локально, нічого не завантажуючи на сервер.
Пропущені виклики
Ваша операційна система обмежує для %1$s доступ до Інтернету у фоновому режимі. Щоб отримувати сповіщення про нові повідомлення, Вам потрібно дозволити %1$s необмежений доступ, коли заощадження трафіку увімкнено.
\n%1$s намагатиметься по можливості економити трафік.
@@ -1065,4 +1065,13 @@
Ви намагаєтеся імпортувати файл резервної копії у застарілому форматі
Аудіокнига
Відновити з\'єднання на іншому вузлі
+ Ви вийшли з цього облікового запису
+ Увійти
+ Сховати сповіщення
+ Ваш контакт використовує неперевірені пристрої. Відскануйте їх QR-код, щоб виконати перевірку та запобігти активним атакам MITM.
+ Вийти
+ Ви використовуєте неперевірені пристрої. Відскануйте QR-код на інших своїх пристроях, щоб виконати перевірку та запобігти активним атакам MITM.
+ Ви вийшли
+ Повідомити про спам і заблокувати спамера
+ Повідомити про спам
diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml
index ac52eea2f3529a278ab6e1e923a3203e3fe3b832..9397cd9652c0599112799a97ee03019c5ba8a6be 100644
--- a/src/main/res/values-zh-rCN/strings.xml
+++ b/src/main/res/values-zh-rCN/strings.xml
@@ -73,7 +73,7 @@
不再询问
无法连接到账号
无法连接到多个账号
- 点击即可管理账号
+ 轻击即可管理账号
附加文件
对方不在您的联系人列表中,是否添加?
添加联系人
@@ -109,11 +109,11 @@
正在提供…
正在等待…
未找到 OpenPGP 密钥
- 无法加密消息,因为对方未公布其公钥。
+ 无法加密消息,因为您的联系人未公布其公钥。
\n
\n请通知对方设置 OpenPGP。
未找到 OpenPGP 密钥
- 无法加密消息,因为对方未公布其公钥。
+ 无法加密消息,因为您的联系人未公布其公钥。
\n
\n请通知对方设置 OpenPGP。
常规
@@ -189,7 +189,7 @@
发布 OpenPGP 公钥
移除 OpenPGP 公钥
是否确定要从在线状态公布中移除 OpenPGP 公钥?
-\n对方将无法再向您发送 OpenPGP 加密消息。
+\n您的联系人将无法再向您发送 OpenPGP 加密消息。
OpenPGP 公钥已发布。
启用账号
是否确定要删除账号?删除账号会擦除全部对话历史记录
@@ -205,11 +205,11 @@
XEP-0313:消息存档管理
XEP-0280:消息抄送
XEP-0352:客户端状态指示
- XEP-0191:屏蔽指令
+ XEP-0191:屏蔽命令
XEP-0237:花名册版本控制
XEP-0198:流管理
XEP-0215:外部服务发现
- XEP-0163:个人事件协议(头像/OMEMO)
+ XEP-0163:个人事件协议 (头像/OMEMO)
XEP-0363:HTTP 文件上传
XEP-0357:推送
有效
@@ -227,8 +227,8 @@
OpenPGP 密钥 ID
OMEMO 指纹
v\\OMEMO 指纹
- OMEMO 指纹(消息来源)
- v\\OMEMO 指纹(消息来源)
+ OMEMO 指纹 (消息来源)
+ v\\OMEMO 指纹 (消息来源)
其他设备
信任 OMEMO 指纹
正在获取密钥…
@@ -269,7 +269,7 @@
%1$s 和其他 %2$d 人已阅读至此
每个人都已阅读至此
发布
- 点击头像即可从图库中选择图片
+ 轻击头像即可从图库中选择图片
正在发布…
服务器拒绝了您的发布
无法转换图片
@@ -288,9 +288,9 @@
启用
需要密码才能进入此群聊
输入密码
- 请先向对方请求在线状态更新。
+ 请先向您的联系人请求在线状态更新。
\n
-\n这将用于确定对方正在使用的聊天应用。
+\n将用于确定对方正在使用的聊天应用。
立即请求
忽略
警告:在双方未更新在线状态的情况下发送此消息将会出现未知问题。
@@ -312,7 +312,7 @@
在进入或离开多用户聊天时设置“自动加入”标志,并回应其他客户端所做的改变。
OMEMO 指纹已复制到剪贴板
此群聊已将您封禁了
- 此群聊仅限成员参与
+ 此群聊仅成员进入
资源限制
此群聊已将您踢出了
此群聊已关闭
@@ -376,9 +376,9 @@
重新生成 OMEMO 密钥
清除设备
是否确定要从 OMEMO 公布中清除所有其他设备?下次连接时,设备将会重新公布,但可能不会收到在此期间发送的消息。
- 对方没有可用的密钥。
+ 此联系人没有可用的密钥。
\n无法从服务器获取新密钥。也许是对方的服务器出了问题?
- 对方没有可用的密钥。
+ 此联系人没有可用的密钥。
\n请确保双方都有在线状态订阅。
出了点问题
正在从服务器获取历史记录
@@ -414,7 +414,7 @@
无法更改 %s 的角色
私人群聊配置
公开频道配置
- 私人,仅限成员
+ 私人,仅成员进入
使 XMPP 地址对任何人可见
对频道进行审核
您未参与
@@ -531,9 +531,9 @@
授予 %1$s 访问相机的权限
与联系人同步
%1$s 想要访问通讯录权限来将它与 XMPP 联系人列表相匹配。
-\n这将会显示联系人的全名和头像。
+\n将会显示联系人的全名和头像。
\n
-\n%1$s 将只会读取通讯录并在本地匹配,不会上传任何东西到服务器。
+\n%1$s 将只会读取通讯录并在本地匹配,不会上传任何内容到服务器。
通知所有消息
仅在提及时通知
通知已禁用
@@ -543,9 +543,9 @@
始终
仅限大图片
电池优化已启用
- 设备正对 %1$s 实施强力电池优化,这可能会导致通知延迟甚至消息丢失。
+ 设备正对 %1$s 实施强力电池优化,可能会导致通知延迟甚至消息丢失。
\n建议禁用它们。
- 设备正对 %1$s 实施强力电池优化,这可能会导致通知延迟甚至消息丢失。
+ 设备正对 %1$s 实施强力电池优化,可能会导致通知延迟甚至消息丢失。
\n
\n现在将要求您禁用它们。
禁用
@@ -554,7 +554,7 @@
此字段是必需的
更正消息
发送更正后的消息
- 您已经安全地验证了此用户的指纹以确认信任。选择“完成”以确认 %s 加入群聊。
+ 您已经验证了此用户的指纹。选择“完成”确认 %s 是此群聊的一员。
您已禁用了此账号
安全错误:文件访问无效!
未找到可以分享 URI 的应用
@@ -609,7 +609,7 @@
远程服务器未找到
远程服务器超时
无法更新账号
- 举报此 XMPP 地址发送垃圾消息。
+ 报告此 XMPP 地址发送垃圾消息。
删除 OMEMO 身份
重新生成 OMEMO 密钥。您的所有联系人将必须再次验证您。仅将此作为最后的方法。
删除已选密钥
@@ -636,12 +636,12 @@
盲目信任的 OMEMO 密钥,表示它们可能是其他人或者某人可能冒充别人发送消息。
不信任的
二维码无效
- 清理缓存文件夹(由相机应用使用)
+ 清理缓存文件夹 (由相机应用使用)
清理缓存
清理私人存储空间
- 清理保存文件的私人存储(它们可以从服务器重新下载)
+ 清理保存文件的私人存储 (它们可以从服务器重新下载)
我从可信来源收到此链接
- 点击链接后,您将验证 %1$s 的 OMEMO 密钥。只有从可信来源(只有 %2$s 可以发布此链接)收到此链接才是安全的。
+ 单击链接后,您将验证 %1$s 的 OMEMO 密钥。只有从可信来源(只有 %2$s 可以发布此链接)收到此链接才是安全的。
您将验证自己账号的 OMEMO 密钥。只有从可信来源(只有您可以发布此链接)收到此链接才是安全的。
继续
验证 OMEMO 密钥
@@ -705,7 +705,7 @@
接受未知证书?
此服务器证书不是由已知的证书颁发机构签发的。
接受不匹配的服务器名称?
- 由于\"%s\",服务器无法验证。证书仅对此有效:
+ 由于“%s”,服务器无法验证。证书仅对此有效:
是否仍要连接?
证书详情:
仅一次
@@ -715,12 +715,12 @@
编辑状态信息
编辑状态信息
禁用加密
- %1$s 无法发送加密消息到 %2$s。这可能是由于对方使用了过时的服务器或者无法处理 OMEMO 的客户端。
+ %1$s 无法发送加密消息到 %2$s。可能是由于您的联系人使用了过时的服务器或者无法处理 OMEMO 的客户端。
无法获取设备列表
无法获取密钥
提示:某些情况下,双方可以添加到联系人列表解决此问题。
是否确定要禁用此对话的 OMEMO 加密?
-\n这将允许服务器管理员读取您的消息,但这可能是与使用过时客户端的用户交流的唯一方法。
+\n将允许服务器管理员读取您的消息,但可能是与使用过时客户端的用户交流的唯一方法。
立即禁用
草稿:
OMEMO 加密
@@ -850,7 +850,7 @@
未安装应用商店。
此频道将公开您的 XMPP 地址
电子书
- 原始(未压缩)
+ 原始 (未压缩)
打开为…
Conversations 个人资料图片
选择账号
@@ -912,7 +912,7 @@
群聊 & 频道
jabber.network
本地服务器
- 大多数用户应该选择 \"jabber.network\" 以便从整个公共 XMPP 生态系统中获得更好的建议。
+ 大多数用户应该选择“jabber.network”以便从整个公共 XMPP 生态系统中获得更好的建议。
频道发现方法
备份
关于
@@ -931,7 +931,7 @@
忽略
正在发现设备
正在响铃
- 正忙
+ 占线
无法连接通话
连接已中断
已撤回通话
@@ -1005,7 +1005,7 @@
拒绝视频切换请求
XMPP 账号
推送服务器
- 无(已停用)
+ 无 (已停用)
UnifiedPush 分发程序
接收推送消息的账号。
用户选择的推送服务器,通过 XMPP 将推送消息转送至设备。
@@ -1022,5 +1022,14 @@
请勿尝试恢复您尚未自行创建的备份!
您正尝试导入过时的备份文件格式
有声读物
- 重新连接其他主机
+ 在其他主机上重新连接
+ 您已登出此账号
+ 登入
+ 隐藏通知
+ 您的联系人使用未经验证的设备。扫描对方二维码进行验证并阻止主动式中间人攻击。
+ 登出
+ 已登出
+ 您正在使用未经验证的设备。扫描您其他设备的二维码进行验证并阻止主动式中间人攻击。
+ 报告垃圾消息并屏蔽垃圾消息发送者
+ 报告垃圾消息
diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml
index b61c2f45d3492951066cadf515ec74fa16564ab4..c69a2788e2dfdd50612a856fbed447d66e8dd7d0 100644
--- a/src/main/res/values-zh-rTW/strings.xml
+++ b/src/main/res/values-zh-rTW/strings.xml
@@ -1001,7 +1001,7 @@
提示:使用「選擇檔案」而非「選擇圖片」以傳送未壓縮的個別檔案,不論此設定為何。
您的裝置正在為 %1$s 採用強力電池效能最佳化,這可能會導致通知延遲甚至訊息遺失。
\n建議將其停用。
- 您已經安全地驗證了這個人的指紋以確認信任。透過選取「完成」以確認 %s 加入群組聊天。
+ 您已經驗證了這個人的指紋。透過選取「完成」以確認 %s 加入群組聊天。
顯示非作用中裝置
盲目信任的 OMEMO 金鑰,說明它們可能對其他人或某人可能冒充別人傳送訊息。
重新產生 OMEMO 金鑰,您的所有聯絡人都需要再次驗證,如非必要請勿採取此步驟。
@@ -1020,4 +1020,15 @@
頻道探索使用一個名為 <a href=https://search.jabber.network>search.jabber.network</a> 的第三方服務,<br><br>使用此功能會將您的 IP 位址和搜尋詞彙傳送至此服務。更多資訊請參見他們的 <a href=https://search.jabber.network/privacy>隱私權政策</a>。
群組聊天
儲存為群組聊天
+ 您已登出此帳戶
+ 登入
+ 隱藏通知
+ 在其他主機上重新連接
+ 您的聯絡人使用未驗證的設備。掃描他們的二維條碼進行驗證,以防止主動中間人攻擊。
+ 登出
+ 您正在嘗試匯入一個過時的備份檔案格式
+ 已登出
+ 您正在使用未驗證的設備。掃描您其他設備上的二維條碼進行驗證,以防止主動中間人攻擊。
+ 有聲書
+ 請勿嘗試還原非您自己建立的備份!
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 34fb7b385ae586d9a50f243e19557bcec74e530a..52ec167b6b6645a0fcd6401560d6c7bf50c6a58f 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -539,7 +539,7 @@
Correct message
Retract message
Send corrected message
- You have already validated this persons fingerprint securely to confirm trust. By selecting “Done” you are just confirming that %s is part of this group chat.
+ You have already trusted this persons fingerprint. By selecting “Done” you are just confirming that %s is part of this group chat.
You have disabled this account
You have logged out of this account
Security error: Invalid file access!
@@ -1027,5 +1027,7 @@
Log out
Log in
Your contact uses unverified devices. Scan their 2D barcode to perform verification and impede active MITM attacks.
- You are using unverified devices. Scan the 2D barcodes of your other devices to perform verification and impede active MITM attack.
+ You are using unverified devices. Scan the 2D barcode on your other devices to perform verification and impede active MITM attacks.
+ Report spam
+ Report spam and block spammer
diff --git a/src/quicksy/fastlane/metadata/android/zh-CN/full_description.txt b/src/quicksy/fastlane/metadata/android/zh-CN/full_description.txt
index a8aa68753ad8094389519dcfa10519830f982b67..c9edb69ec3cafe5ff2fe7c456661b15b369df527 100644
--- a/src/quicksy/fastlane/metadata/android/zh-CN/full_description.txt
+++ b/src/quicksy/fastlane/metadata/android/zh-CN/full_description.txt
@@ -1,14 +1,13 @@
-Quicksy 是一个流行的 Jabber/XMPP 客户端 Conversations 的衍生品,具有自动发现联系人的功能。
+Quicksy 是流行的 Jabber/XMPP 客户端 Conversations 的衍生品,具有自动发现联系人的功能。
您只需用电话号码注册,Quicksy 就会自动—根据您通讯录中的电话号码—向您推荐可能的联系人。
-从本质上讲,Quicksy 是一个成熟的 Jabber 客户端,可让您与任何公共联合服务器上的任何用户进行交流。同样,只需将 +phonenumber@quicksy.im 添加到您的联系人列表中,即可从外部联系 Quicksy 上的用户。
+从本质上讲,Quicksy 是成熟的 Jabber 客户端,可让您与任何公共联合服务器上的任何用户进行交流。同样,只需将 +phonenumber@quicksy.im 添加到您的联系人列表中,即可从外部联系 Quicksy 上的用户。
除了联系人同步之外,用户界面尽可能地接近 Conversations。这使得用户最终可以从 Quicksy 迁移到 Conversations,而无需重新了解应用程序的工作方式。
-建议的联系人包括其他 Quicksy 用户和在 Quicksy 目录中输入 Jabber ID 的普通 Jabber/XMPP 用户 (https://quicksy.im/#get-listed)。
+建议的联系人包括其他 Quicksy 用户和在 Quicksy 目录中输入 Jabber ID 的普通 Jabber/XMPP 用户(https://quicksy.im/#get-listed)。
-注意:要在 Quicksy 中输入 (https://quicksy.im/enter/) 您的 Jabber ID
-目录一次性注册费用是必需的。
+注意:要在 Quicksy 目录中输入(https://quicksy.im/enter/)您的 Jabber ID 需要缴纳一次性注册费。
-请阅读隐私政策 (https://quicksy.im/#privacy) ,了解更多信息。
+请阅读隐私政策(https://quicksy.im/#privacy),了解更多信息。
diff --git a/src/quicksy/res/values-uk/strings.xml b/src/quicksy/res/values-uk/strings.xml
index 665d4d483de3a1fa727788522d81c52b4cd1a906..3c6dfdcb490e8f5ca183fa8927d46f6563811e71 100644
--- a/src/quicksy/res/values-uk/strings.xml
+++ b/src/quicksy/res/values-uk/strings.xml
@@ -1,7 +1,7 @@
Час, протягом якого застосунок дотримується тиші після виявлення активності на іншому пристрої
- Надсилаючи траси стеку викликів, Ви допомагаєте розробляти Quicksy
+ Надсилаючи звіти про збої, Ви допомагаєте розвивати Quicksy
Повідомляти співрозмовникам, що Ви користуєтеся Quicksy
Щоб отримувати сповіщення навіть коли екран погас, необхідно додати Quicksy до списку захищених програм.
Зображення профілю для Quicksy