Merge branch 'master' of https://codeberg.org/iNPUTmice/Conversations

Stephen Paul Weber created

* 'master' of https://codeberg.org/iNPUTmice/Conversations: (56 commits)
  fix accepting senders both content adds
  JingleRtpConnection code clean up
  Translated using Weblate (Chinese (Simplified))
  Translated using Weblate (Romanian)
  Translated using Weblate (Ukrainian)
  Translated using Weblate (Galician)
  Translated using Weblate (Spanish)
  add SDP Offer / Answer support
  bump reporting xep and add ability to report messages
  Translated using Weblate (Chinese (Traditional))
  Translated using Weblate (Chinese (Traditional))
  Translated using Weblate (Chinese (Traditional))
  Translated using Weblate (Chinese (Simplified))
  Translated using Weblate (Romanian)
  Translated using Weblate (Ukrainian)
  Translated using Weblate (Galician)
  Translated using Weblate (Swedish)
  Translated using Weblate (Italian)
  add xep-0440 to doap file
  enable 'PEP Native Bookmarks'
  ...

Change summary

build.gradle                                                                      |  16 
fastlane/metadata/android/gl-ES/changelogs/353.txt                                |   8 
fastlane/metadata/android/gl-ES/changelogs/360.txt                                |   2 
fastlane/metadata/android/gl-ES/changelogs/362.txt                                |   2 
fastlane/metadata/android/gl-ES/changelogs/364.txt                                |   4 
fastlane/metadata/android/gl-ES/changelogs/367.txt                                |   4 
fastlane/metadata/android/gl-ES/changelogs/379.txt                                |   2 
fastlane/metadata/android/gl-ES/changelogs/42038.txt                              |   4 
fastlane/metadata/android/gl-ES/changelogs/42041.txt                              |  10 
fastlane/metadata/android/gl-ES/changelogs/4207704.txt                            |   3 
fastlane/metadata/android/zh-TW/changelogs/349.txt                                |   4 
fastlane/metadata/android/zh-TW/changelogs/351.txt                                |   3 
fastlane/metadata/android/zh-TW/changelogs/353.txt                                |   4 
fastlane/metadata/android/zh-TW/changelogs/360.txt                                |   1 
fastlane/metadata/android/zh-TW/changelogs/362.txt                                |   1 
fastlane/metadata/android/zh-TW/changelogs/364.txt                                |   2 
fastlane/metadata/android/zh-TW/changelogs/367.txt                                |   2 
fastlane/metadata/android/zh-TW/changelogs/379.txt                                |   1 
fastlane/metadata/android/zh-TW/changelogs/381.txt                                |   2 
fastlane/metadata/android/zh-TW/changelogs/382.txt                                |   2 
fastlane/metadata/android/zh-TW/changelogs/383.txt                                |   3 
fastlane/metadata/android/zh-TW/changelogs/387.txt                                |   2 
fastlane/metadata/android/zh-TW/changelogs/388.txt                                |   3 
fastlane/metadata/android/zh-TW/changelogs/390.txt                                |   1 
fastlane/metadata/android/zh-TW/changelogs/393.txt                                |   3 
fastlane/metadata/android/zh-TW/changelogs/394.txt                                |   2 
fastlane/metadata/android/zh-TW/changelogs/395.txt                                |   3 
fastlane/metadata/android/zh-TW/changelogs/398.txt                                |   4 
fastlane/metadata/android/zh-TW/changelogs/401.txt                                |   2 
fastlane/metadata/android/zh-TW/changelogs/402.txt                                |   3 
fastlane/metadata/android/zh-TW/changelogs/403.txt                                |   3 
fastlane/metadata/android/zh-TW/changelogs/404.txt                                |   1 
fastlane/metadata/android/zh-TW/changelogs/405.txt                                |   1 
fastlane/metadata/android/zh-TW/changelogs/407.txt                                |   3 
fastlane/metadata/android/zh-TW/changelogs/42000.txt                              |   4 
fastlane/metadata/android/zh-TW/changelogs/42006.txt                              |   2 
fastlane/metadata/android/zh-TW/changelogs/42010.txt                              |   2 
fastlane/metadata/android/zh-TW/changelogs/42012.txt                              |   1 
fastlane/metadata/android/zh-TW/changelogs/42013.txt                              |   1 
fastlane/metadata/android/zh-TW/changelogs/42014.txt                              |   2 
src/conversations/fastlane/metadata/android/zh-CN/full_description.txt            |  16 
src/conversations/fastlane/metadata/android/zh-CN/short_description.txt           |   2 
src/conversations/res/values-zh-rCN/strings.xml                                   |   4 
src/main/java/eu/siacs/conversations/Config.java                                  |   3 
src/main/java/eu/siacs/conversations/generator/IqGenerator.java                   |  10 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java          |   8 
src/main/java/eu/siacs/conversations/ui/BlockContactDialog.java                   |  32 
src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java                    |   2 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java                 |  18 
src/main/java/eu/siacs/conversations/xml/Namespace.java                           |   3 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java                     |   4 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java         | 624 
src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java               |  19 
src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java               |  59 
src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/IceUdpTransportInfo.java |  11 
src/main/res/menu/message_context.xml                                             |   5 
src/main/res/values-de/strings.xml                                                |   9 
src/main/res/values-es/strings.xml                                                |   9 
src/main/res/values-gl/strings.xml                                                |  11 
src/main/res/values-it/strings.xml                                                |   7 
src/main/res/values-ja/strings.xml                                                |  14 
src/main/res/values-nl/strings.xml                                                |   8 
src/main/res/values-ro-rRO/strings.xml                                            |  11 
src/main/res/values-ru/strings.xml                                                |   7 
src/main/res/values-sv/strings.xml                                                | 621 
src/main/res/values-uk/strings.xml                                                |  45 
src/main/res/values-zh-rCN/strings.xml                                            |  73 
src/main/res/values-zh-rTW/strings.xml                                            |  13 
src/main/res/values/strings.xml                                                   |   6 
src/quicksy/fastlane/metadata/android/zh-CN/full_description.txt                  |  11 
src/quicksy/res/values-uk/strings.xml                                             |   2 
71 files changed, 1,043 insertions(+), 747 deletions(-)

Detailed changes

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) {

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)

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

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)

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)

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

fastlane/metadata/android/zh-TW/changelogs/349.txt 🔗

@@ -0,0 +1,4 @@
+* 引入專家設置,以在本地伺服器上執行通道發現,而非 search.jabber.network
+* 默認啟用傳送檢查標記,並刪除相應設置
+* 默認啟用「發送按鈕顯示狀態」,並刪除相應設置
+* 將備份和前景服務設置移至主畫面

fastlane/metadata/android/zh-TW/changelogs/383.txt 🔗

@@ -0,0 +1,3 @@
+* 將通話圖標移至左側,以保持其他工具欄圖標的一致位置
+* 在語音通話期間顯示通話持續時間
+* 視訊/音訊通話的分開處理(同時互打電話的兩人的處理)

fastlane/metadata/android/zh-TW/changelogs/398.txt 🔗

@@ -0,0 +1,4 @@
+* 搜尋個別對話
+* 如果消息未能傳遞,通知使用者
+* 在重新啟動後保留來自 Quicksy 使用者的顯示名稱(暱稱)
+* 如果需要,新增從通知啟動 Orbot(洋蔥路由器)的按鈕

src/conversations/fastlane/metadata/android/zh-CN/full_description.txt 🔗

@@ -1,23 +1,23 @@
-易于使用、性能可靠、电池友好。内置支持图片、群组聊天和 e2e 加密功能。
+易于使用、性能可靠、电池友好。内置支持图片、群聊和 e2e 加密功能。
 
 设计原则:
 
 * 在不牺牲安全性和隐私性的前提下,尽可能美观易用
 * 依赖现有的、完善的协议
-* 不需要 Google 账号或特定的 Google 云通讯服务 (GCM)
+* 不需要 Google 账号或特定的 Google 云通讯服务(GCM)
 * 要求尽可能少的权限
 
 特点:
 
 * 使用 <a href="http://conversations.im/omemo/">OMEMO</a> 或 <a href="http://openpgp.org/about/">OpenPGP</a> 进行端对端加密
 * 发送和接收图片
-* 加密音视频通话 (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:花名册版本控制主要是为了在移动连接不佳的情况下节省带宽

src/conversations/res/values-zh-rCN/strings.xml 🔗

@@ -3,7 +3,7 @@
     <string name="pick_a_server">选择您的 XMPP 提供者</string>
     <string name="use_conversations.im">使用 conversations.im</string>
     <string name="create_new_account">创建新账号</string>
-    <string name="do_you_have_an_account">您已经有 XMPP 账号了吗?如果您之前使用过 Conversations 或其他 XMPP 客户端,那么您已经有这种账号了。如果没有,您可以立即创建一个。
+    <string name="do_you_have_an_account">您已经有 XMPP 账号了吗?如果您之前使用过 Conversations 或其他 XMPP 客户端,那么您已经有账号了。如果没有,您可以立即创建一个。
 \n提示:一些电子邮件服务也提供 XMPP 账号。</string>
     <string name="server_select_text">XMPP 是独立于提供者的即时通讯网络。您选择的任何 XMPP 服务器都可以使用此客户端。
 \n不过,您可以轻松地在 conversations.im 上创建账号;特别适合与 Conversations 使用的提供者。</string>
@@ -13,7 +13,7 @@
 \n向其他 XMPP 用户提供您的完整地址,就能和对方交流。</string>
     <string name="your_server_invitation">您的服务器邀请</string>
     <string name="improperly_formatted_provisioning">配置代码格式不正确</string>
-    <string name="tap_share_button_send_invite">点击分享按钮,向您的联系人发送加入 %1$s 的邀请。</string>
+    <string name="tap_share_button_send_invite">轻击分享按钮,向您的联系人发送加入 %1$s 的邀请。</string>
     <string name="if_contact_is_nearby_use_qr">如果您的联系人在附近,对方也可以扫描下方二维码接受邀请。</string>
     <string name="easy_invite_share_text">加入 %1$s 和我聊天:%2$s</string>
     <string name="share_invite_with">分享邀请至…</string>

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;

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;

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);

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;
 				}

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;

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);

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";
 }

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() {

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<PeerConnection.PeerConnectionState> stateHistory = new LinkedList<>();
     private ScheduledFuture<?> ringingTimeoutFuture;
     private final long created = System.currentTimeMillis() / 1000L;
-    private final SettableFuture<Collection<IceCandidate>> 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<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates =
                 contentMap.contents.entrySet();
         final RtpContentMap remote = getRemoteContentMap();
-        final Set<String> remoteContentIds = remote == null ? Collections.emptySet() : remote.contents.keySet();
+        final Set<String> 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<RtpContentMap> future =
                     receiveRtpContentMap(
-                            modification, this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
-            Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
-                @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<RtpContentMap> future =
                     receiveRtpContentMap(
-                            receivedContentAccept, this.omemoVerification.hasFingerprint() && hasFullTransportInfo);
-            Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
-                @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<String> currentOutgoingMediaIds = currentOutgoing == null ? Collections.emptySet() : currentOutgoing.contents.keySet();
+        final Set<String> 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<String, IceUdpTransportInfo.Candidate> candidateBuilder = new ImmutableMultimap.Builder<>();
-        for(final SessionDescription.Media media : answer.media) {
+    private static ImmutableMultimap<String, IceUdpTransportInfo.Candidate> parseCandidates(
+            final SessionDescription answer) {
+        final ImmutableMultimap.Builder<String, IceUdpTransportInfo.Candidate> 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<String, IceUdpTransportInfo.Candidate> 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.Summary> contentAddition) {
+    public synchronized void acceptContentAdd(
+            @NonNull final Set<ContentAddition.Summary> 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<Content.Senders> 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.Summary> contentAddition, final RtpContentMap incomingContentAdd) {
+    private void acceptContentAdd(
+            @NonNull final Set<ContentAddition.Summary> 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<RtpContentMap> future = prepareOutgoingContentMap(contentAcceptMap);
+            final ListenableFuture<RtpContentMap> future =
+                    prepareOutgoingContentMap(contentAcceptMap);
             Futures.addCallback(
                     future,
-                    new FutureCallback<RtpContentMap>() {
+                    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<RtpContentMap> receiveRtpContentMap(final RtpContentMap receivedContentMap, final boolean expectVerification) {
+    }
+
+    private ListenableFuture<RtpContentMap> 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<RtpContentMap> future = receiveRtpContentMap(jinglePacket, false);
         Futures.addCallback(
                 future,
-                new FutureCallback<RtpContentMap>() {
+                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<RtpContentMap>() {
+                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<String, IceUdpTransportInfo.Candidate> candidates;
+        if (includeCandidates) {
+            candidates = parseCandidates(sessionDescription);
+        } else {
+            candidates = ImmutableMultimap.of();
+        }
+        this.responderRtpContentMap = respondingRtpContentMap;
+        storePeerDtlsSetup(respondingRtpContentMap.getDtlsSetup().flip());
+        final ListenableFuture<RtpContentMap> outgoingContentMapFuture =
+                prepareOutgoingContentMap(respondingRtpContentMap);
         Futures.addCallback(
-            webRTCWrapper.getRFC3264() ? Futures.withTimeout(iceGatheringComplete, 2, TimeUnit.SECONDS, JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE) : Futures.immediateFuture(null),
-            new FutureCallback<Collection<IceCandidate>>() {
-                @Override
-                public void onSuccess(final Collection<IceCandidate> 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<RtpContentMap> outgoingContentMapFuture =
-                        prepareOutgoingContentMap(respondingRtpContentMap);
-                    Futures.addCallback(
-                        outgoingContentMapFuture,
-                        new FutureCallback<RtpContentMap>() {
-                            @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<String, IceUdpTransportInfo.Candidate> candidates;
+        if (includeCandidates) {
+            candidates = parseCandidates(sessionDescription);
+        } else {
+            candidates = ImmutableMultimap.of();
+        }
+        this.initiatorRtpContentMap = rtpContentMap;
+        final ListenableFuture<RtpContentMap> outgoingContentMapFuture =
+                encryptSessionInitiate(rtpContentMap);
         Futures.addCallback(
-            webRTCWrapper.getRFC3264() ? Futures.withTimeout(iceGatheringComplete, 2, TimeUnit.SECONDS, JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE) : Futures.immediateFuture(null),
-            new FutureCallback<Collection<IceCandidate>>() {
-                @Override
-                public void onSuccess(final Collection<IceCandidate> 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<RtpContentMap> outgoingContentMapFuture =
-                        encryptSessionInitiate(rtpContentMap);
-                    Futures.addCallback(
-                        outgoingContentMapFuture,
-                        new FutureCallback<RtpContentMap>() {
-                            @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<String, IceUdpTransportInfo.Candidate> 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");
         }
     }
 

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<String, IceUdpTransportInfo.Candidate> candidates) {
+        final ImmutableMap.Builder<String, DescriptionTransport> contentBuilder =
+                new ImmutableMap.Builder<>();
+        for (final Map.Entry<String, DescriptionTransport> 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<IceUdpTransportInfo.Credentials> allCredentials = getCredentials();
         final IceUdpTransportInfo.Credentials credentials =

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<IceCandidate> iceCandidates = new LinkedList<>();
     private final AppRTCAudioManager.AudioManagerEvents audioManagerEvents =
             new AppRTCAudioManager.AudioManagerEvents() {
@@ -119,6 +119,8 @@ public class WebRTCWrapper {
     private TrackWrapper<AudioTrack> localAudioTrack = null;
     private TrackWrapper<VideoTrack> localVideoTrack = null;
     private VideoTrack remoteVideoTrack = null;
+
+    private final SettableFuture<Void> 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> media, final List<PeerConnection.IceServer> iceServers)
+            final Set<Media> media,
+            final List<PeerConnection.IceServer> 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<PeerConnection.IceServer> iceServers, boolean rfc3264) {
+            final List<PeerConnection.IceServer> 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<PeerConnection.IceServer> iceServers) {
-        requirePeerConnection().setConfiguration(buildConfiguration(iceServers, rfc3264.get()));
+    void reconfigurePeerConnection(
+            final List<PeerConnection.IceServer> 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<SessionDescription> setLocalDescription() {
+    synchronized ListenableFuture<SessionDescription> 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<AppRTCAudioManager.AudioDevice> availableAudioDevices);
 
         void onRenegotiationNeeded();
-
-        void onIceGatheringComplete(Collection<IceCandidate> iceCandidates);
     }
 
     private abstract static class SetSdpObserver implements SdpObserver {

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<Candidate> 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;

src/main/res/menu/message_context.xml 🔗

@@ -1,6 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
 
+    <item
+        android:id="@+id/action_report_and_block"
+        android:title="@string/report_spam"
+        android:visible="false" />
+
     <item
         android:id="@+id/open_with"
         android:title="@string/open_with"

src/main/res/values-de/strings.xml 🔗

@@ -264,7 +264,7 @@
     <string name="error_saving_avatar">Profilbild kann nicht gespeichert werden</string>
     <string name="or_long_press_for_default">(Oder klicke lange, um den Standard wiederherzustellen)</string>
     <string name="error_publish_avatar_no_server_support">Dein Server unterstützt die Veröffentlichung von Profilbildern nicht</string>
-    <string name="private_message">geflüstert</string>
+    <string name="private_message">private Nachricht:</string>
     <string name="private_message_to">an %s</string>
     <string name="send_private_message_to">Private Nachricht an %s senden</string>
     <string name="connect">Verbinden</string>
@@ -1011,4 +1011,11 @@
     <string name="outdated_backup_file_format">Du versuchst, ein veraltetes Sicherungsdateiformat zu importieren</string>
     <string name="audiobook">Hörbuch</string>
     <string name="reconnect_on_other_host">Verbindung auf anderem Host wiederherstellen</string>
+    <string name="this_account_is_logged_out">Du hast dich von diesem Konto abgemeldet</string>
+    <string name="log_in">Anmelden</string>
+    <string name="hide_notification">Benachrichtigung ausblenden</string>
+    <string name="contact_uses_unverified_keys">Dein Kontakt verwendet nicht verifizierte Geräte. Scanne deren 2D-Barcode, um eine Verifizierung durchzuführen und aktive MITM-Angriffe zu verhindern.</string>
+    <string name="log_out">Abmelden</string>
+    <string name="account_state_logged_out">Abgemeldet</string>
+    <string name="unverified_devices">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.</string>
 </resources>

src/main/res/values-es/strings.xml 🔗

@@ -537,7 +537,7 @@
     <string name="this_field_is_required">Este campo es requerido</string>
     <string name="correct_message">Corregir mensaje</string>
     <string name="send_corrected_message">Enviar mensaje corregido</string>
-    <string name="no_keys_just_confirm">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. </string>
+    <string name="no_keys_just_confirm">Ya has confiado la huella digital de esta persona. Al seleccionar “Listo” solo estás confirmando que %s es parte de este chat grupal.</string>
     <string name="this_account_is_disabled">Has deshabilitado esta cuenta</string>
     <string name="security_error_invalid_file_access">Error de seguridad: ¡Acceso a archivo inválido!</string>
     <string name="no_application_to_share_uri">No se ha encontrado ninguna aplicación para compartir la URI</string>
@@ -1025,4 +1025,11 @@
     <string name="outdated_backup_file_format">Estás intentando importar un formato de copia de seguridad obsoleto</string>
     <string name="audiobook">Audiolibro</string>
     <string name="reconnect_on_other_host">Reconectarse a otros hosts</string>
+    <string name="this_account_is_logged_out">Has salido de esta cuenta</string>
+    <string name="log_in">Iniciar sesión</string>
+    <string name="hide_notification">Ocultar la notificación</string>
+    <string name="contact_uses_unverified_keys">Su contacto utiliza dispositivos no verificados. Escanea su código de barras en 2D para realizar la verificación e impedir ataques MITM activos.</string>
+    <string name="log_out">Desconectarse</string>
+    <string name="account_state_logged_out">Desconectado</string>
+    <string name="unverified_devices">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.</string>
 </resources>

src/main/res/values-gl/strings.xml 🔗

@@ -536,7 +536,7 @@
     <string name="this_field_is_required">Este campo é requerido</string>
     <string name="correct_message">Correxir mensaxe</string>
     <string name="send_corrected_message">Enviar mensaxe correxida</string>
-    <string name="no_keys_just_confirm">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.</string>
+    <string name="no_keys_just_confirm">Xa confiaches na impresión dixital destas persoas. Ao escoller \"Feito\" estás simplemente confirmando que %s é parte desta conversa en grupo.</string>
     <string name="this_account_is_disabled">Desactivou esta conta</string>
     <string name="security_error_invalid_file_access">Fallo de seguridade: Acceso non válido ao ficheiro!</string>
     <string name="no_application_to_share_uri">Non se atopou unha app para compartir URI</string>
@@ -1014,4 +1014,13 @@
     <string name="outdated_backup_file_format">Estás intentando importar un ficheiro de apoio co formato antigo</string>
     <string name="audiobook">Audiolibro</string>
     <string name="reconnect_on_other_host">Volver conectar noutro servidor</string>
+    <string name="this_account_is_logged_out">Pechaches a sesión desta conta</string>
+    <string name="log_in">Acceder</string>
+    <string name="hide_notification">Agochar notificación</string>
+    <string name="contact_uses_unverified_keys">O teu contacto usa dispositivos non verificados. Escanea o seu código de barras 2D para verficalo e impedir ataques MITM.</string>
+    <string name="log_out">Saír da sesión</string>
+    <string name="account_state_logged_out">Sesión pechada</string>
+    <string name="unverified_devices">Estás a usar dispositivos non verificados. Escanea os códigos de barras 2D nos teus outros dispositivos para verificalos e impedir ataques MITM.</string>
+    <string name="report_spam_and_block">Informar de spam e bloquear conta</string>
+    <string name="report_spam">Informar de spam</string>
 </resources>

src/main/res/values-it/strings.xml 🔗

@@ -1025,4 +1025,11 @@
     <string name="outdated_backup_file_format">Stai tentando di importare un formato di file di backup obsoleto</string>
     <string name="audiobook">Audiolibro</string>
     <string name="reconnect_on_other_host">Riconnetti su altro host</string>
+    <string name="this_account_is_logged_out">Ti sei disconnesso da questo profilo</string>
+    <string name="log_in">Accedi</string>
+    <string name="hide_notification">Nascondi notifica</string>
+    <string name="contact_uses_unverified_keys">Il tuo contatto usa dispositivi non verificati. Scansiona il suo codice a barre 2D per effettuare la verifica e impedire attacchi MITM attivi.</string>
+    <string name="log_out">Disconnetti</string>
+    <string name="account_state_logged_out">Disconnesso</string>
+    <string name="unverified_devices">Stai usando dispositivi non verificati. Scansiona il codice a barre 2D nei tuoi altri dispositivi per effettuare la verifica e impedire attacchi MITM attivi.</string>
 </resources>

src/main/res/values-ja/strings.xml 🔗

@@ -967,4 +967,18 @@
     <string name="delete_avatar">アバターを削除</string>
     <string name="audio_video_disabled_tor">Tor使用中のため通話できません</string>
     <string name="switch_to_video">ビデオ通話切替</string>
+    <string name="this_account_is_logged_out">このアカウントをログアウトしました</string>
+    <string name="action_directions">ルート</string>
+    <string name="reject_switch_to_video">ビデオ通話を却下する</string>
+    <string name="incoming_call_duration_timestamp">着信通話 (%s) · %s</string>
+    <string name="pref_up_push_account_title">XMPPアカウント</string>
+    <string name="group_chats">グループ</string>
+    <string name="outdated_backup_file_format">選択したファイルは、旧式のファイル形式ので復元できません</string>
+    <string name="search_group_chats">グループを検索</string>
+    <string name="outgoing_call_duration_timestamp">発信通話 (%s) · %s</string>
+    <string name="account_state_logged_out">ログアウトしました</string>
+    <string name="outgoing_call_timestamp">発信通話 · %s</string>
+    <string name="audiobook">オーディオブック</string>
+    <string name="channel_discover_opt_in_message">談話室の発見は&lt;a href=https://search.jabber.network&gt;search.jabber.network&lt;/a&gt;というサービスを利用します.&lt;br&gt;&lt;br&gt;利用するとIPアドレスと検索語はそのサービスに送信されます。詳細についてはそのサービスの&lt;a href=https://search.jabber.network/privacy&gt;個人情報保護方針&lt;/a&gt;を参照してください。</string>
+    <string name="restore_warning_continued">自分で保存したバックアップしか復元しないでください!</string>
 </resources>

src/main/res/values-nl/strings.xml 🔗

@@ -31,10 +31,7 @@
     <string name="minutes_ago">%d min. geleden</string>
     <plurals name="x_unread_conversations">
         <item quantity="one">%d ongelezen gesprek</item>
-
-    
         <item quantity="other">%d ongelezen gesprekken</item>
-
     </plurals>
     <string name="sending">versturen…</string>
     <string name="message_decrypting">Bericht aan het ontsleutelen. Even geduld…</string>
@@ -168,7 +165,8 @@
     <string name="mgmt_account_publish_avatar">Avatar publiceren</string>
     <string name="mgmt_account_publish_pgp">OpenPGP-publieke sleutel publiceren</string>
     <string name="unpublish_pgp">OpenPGP-publieke sleutel verwijderen</string>
-    <string name="unpublish_pgp_message">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.</string>
+    <string name="unpublish_pgp_message">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.</string>
     <string name="openpgp_has_been_published">OpenPGP-publieke sleutel gepubliceerd.</string>
     <string name="mgmt_account_enable">Account inschakelen</string>
     <string name="attach_record_voice">Stem opnemen</string>
@@ -816,4 +814,4 @@
     <string name="unable_to_enable_video">Kan video niet schakelen.</string>
     <string name="plain_text_document">Onversleuteld document</string>
     <string name="account_registrations_are_not_supported">Accountregistraties zijn niet ondersteund</string>
-    </resources>
+</resources>

src/main/res/values-ro-rRO/strings.xml 🔗

@@ -545,7 +545,7 @@
     <string name="this_field_is_required">Acest câmp este obligatoriu</string>
     <string name="correct_message">Corecție mesaj</string>
     <string name="send_corrected_message">Trimite text corectat</string>
-    <string name="no_keys_just_confirm">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.</string>
+    <string name="no_keys_just_confirm">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.</string>
     <string name="this_account_is_disabled">Ați dezactivat acest cont</string>
     <string name="security_error_invalid_file_access">Eroare de securitate.: Acces fișier invalid!</string>
     <string name="no_application_to_share_uri">Nu s-a găsit nici o aplicație care să partajeze adresa</string>
@@ -1033,4 +1033,13 @@
     <string name="outdated_backup_file_format">Încercați să importați un fișier copie de rezervă format vechi</string>
     <string name="audiobook">Carte audio</string>
     <string name="reconnect_on_other_host">Reconectat pe altă gazdă</string>
+    <string name="this_account_is_logged_out">V-ați deconectat de la acest cont</string>
+    <string name="log_in">Conectați-vă</string>
+    <string name="hide_notification">Ascunde notificare</string>
+    <string name="contact_uses_unverified_keys">Persoana de contact utilizează dispozitive neverificate. Scanați codul de bare 2D al acestora pentru a efectua verificarea și a împiedica atacurile MITM active.</string>
+    <string name="log_out">Deconectare</string>
+    <string name="account_state_logged_out">Deconectat</string>
+    <string name="unverified_devices">Folosiți dispozitive neverificate. Scanați codul de bare 2D pe celelalte dispozitive pentru a efectua verificarea și a împiedica atacurile MITM active.</string>
+    <string name="report_spam_and_block">Raportează spam și blochează spamerul</string>
+    <string name="report_spam">Raportează spam</string>
 </resources>

src/main/res/values-ru/strings.xml 🔗

@@ -1035,4 +1035,11 @@
         <item quantity="other">%1$d пропущенных вызовов от %2$d контактов</item>
     </plurals>
     <string name="audiobook">Аудиокнига</string>
+    <string name="this_account_is_logged_out">Вы вышли из этой учётной записи</string>
+    <string name="log_in">Войти</string>
+    <string name="hide_notification">Скрыть уведомление</string>
+    <string name="contact_uses_unverified_keys">Ваш контакт использует неподтверждённые устройства. Отсканируйте его штрих-код для проверки и предотвращения атаки посредника.</string>
+    <string name="log_out">Выйти</string>
+    <string name="account_state_logged_out">Деавторизован</string>
+    <string name="unverified_devices">Вы используете неподтверждённые устройства. Отсканируйте штрих-код на подтверждённом устройстве для проверки и предотвращения атаки посредника.</string>
 </resources>

src/main/res/values-sv/strings.xml 🔗

@@ -2,14 +2,14 @@
 <resources>
     <string name="action_settings">Inställningar</string>
     <string name="action_add">Ny konversation</string>
-    <string name="action_accounts">Kontoinställningar</string>
+    <string name="action_accounts">Hantera konton</string>
     <string name="action_account">Hantera konto</string>
     <string name="action_end_conversation">Stäng konversation</string>
     <string name="action_contact_details">Kontaktdetaljer</string>
     <string name="action_muc_details">Gruppchattdetaljer</string>
-    <string name="channel_details">Kanaldetaljer</string>
+    <string name="channel_details">Gruppdetaljer</string>
     <string name="action_add_account">Lägg till konto</string>
-    <string name="action_edit_contact">Ändra namn</string>
+    <string name="action_edit_contact">Redigera namn</string>
     <string name="action_add_phone_book">Lägg till i kontakter</string>
     <string name="action_delete_contact">Ta bort kontakt</string>
     <string name="action_block_contact">Blockera kontakt</string>
@@ -36,23 +36,23 @@
     <string name="sending">skickar…</string>
     <string name="message_decrypting">Avkrypterar meddelande. Vänta…</string>
     <string name="pgp_message">OpenPGP-krypterat meddelande</string>
-    <string name="nick_in_use">Nick används redan</string>
+    <string name="nick_in_use">Smeknamn används redan</string>
     <string name="invalid_muc_nick">Ogiltigt smeknamn</string>
     <string name="admin">Admin</string>
     <string name="owner">Ägare</string>
     <string name="moderator">Moderator</string>
     <string name="participant">Deltagare</string>
     <string name="visitor">Besökare</string>
-    <string name="remove_contact_text">Vill du ta bort %s från din kontaktlista? Konversationer med denna kontakt kommer inte tas bort.</string>
+    <string name="remove_contact_text">Vill du ta bort %s från din kontaktlista\? Konversationer med denna kontakt kommer inte att tas bort.</string>
     <string name="block_contact_text">Vill du blockera %s från att skicka dig meddelanden?</string>
-    <string name="unblock_contact_text">Vill du avblockera %s och tillåta denne att skicka dig meddelanden?</string>
+    <string name="unblock_contact_text">Vill du avblockera %s och tillåta användaren att skicka dig meddelanden\?</string>
     <string name="block_domain_text">Blockera alla kontakter från %s?</string>
     <string name="unblock_domain_text">Avblockera alla kontakter från %s?</string>
     <string name="contact_blocked">Kontakt blockerad</string>
     <string name="blocked">Blockerad</string>
-    <string name="remove_bookmark_text">Vill du ta bort %s som ett bokmärke? Konversationer med detta bokmärke kommer inte tas bort.</string>
-    <string name="register_account">Registrera nytt konto på servern</string>
-    <string name="change_password_on_server">Byt lösenord på server</string>
+    <string name="remove_bookmark_text">Vill du ta bort %s som ett bokmärke\? Konversationer med detta bokmärke kommer inte att tas bort.</string>
+    <string name="register_account">Registrera ett nytt konto på servern</string>
+    <string name="change_password_on_server">Byt lösenord på servern</string>
     <string name="share_with">Dela med…</string>
     <string name="start_conversation">Börja konversation</string>
     <string name="invite_contact">Bjud in kontakt</string>
@@ -60,7 +60,7 @@
     <string name="contacts">Kontakter</string>
     <string name="contact">Kontakt</string>
     <string name="cancel">Avbryt</string>
-    <string name="set">Sätt</string>
+    <string name="set">Ange</string>
     <string name="add">Lägg till</string>
     <string name="edit">Ändra</string>
     <string name="delete">Ta bort</string>
@@ -69,27 +69,29 @@
     <string name="save">Spara</string>
     <string name="ok">Ok</string>
     <string name="crash_report_title">%1$s har kraschat</string>
-    <string name="crash_report_message">Att använda ditt XMPP-konto för att skicka in \'stack traces\' hjälper den pågående utvecklingen av %1$s.</string>
+    <string name="crash_report_message">Att använda ditt XMPP-konto för att skicka in \'stack traces\', hjälper den pågående utvecklingen av %1$s.</string>
     <string name="send_now">Skicka nu</string>
     <string name="send_never">Fråga aldrig igen</string>
-    <string name="problem_connecting_to_account">Kunde inte ansluta till konto</string>
+    <string name="problem_connecting_to_account">Kunde inte ansluta till kontot</string>
     <string name="problem_connecting_to_accounts">Kunde inte ansluta till flera konton</string>
     <string name="touch_to_fix">Tryck för att hantera dina konton</string>
     <string name="attach_file">Bifoga fil</string>
-    <string name="not_in_roster">Vill du lägga till den här saknade kontakten i din kontaktlista?</string>
+    <string name="not_in_roster">Vill du lägga till den här kontakten i din kontaktlista\?</string>
     <string name="add_contact">Lägg till kontakt</string>
     <string name="send_failed">sändning misslyckades</string>
     <string name="preparing_image">Förbereder att skicka bild</string>
     <string name="preparing_images">Förbereder att skicka bilder</string>
-    <string name="sharing_files_please_wait">Delar filer. Vänta…</string>
+    <string name="sharing_files_please_wait">Delar filer. Var god vänta…</string>
     <string name="action_clear_history">Rensa historik</string>
     <string name="clear_conversation_history">Rensa konversationshistorik</string>
-    <string name="clear_histor_msg">Vill du radera alla meddelanden i den här konversationen?\n\n<b>Varning:</b> Det här påverkar inte meddelanden som finns lagrade på andra enheter eller servrar.</string>
+    <string name="clear_histor_msg">Vill du ta bort alla meddelanden i den här konversationen\?
+\n
+\n<b>Varning:</b> Det här påverkar inte meddelanden som finns lagrade på andra enheter eller servrar.</string>
     <string name="delete_file_dialog">Ta bort fil</string>
     <string name="delete_file_dialog_msg">Är du säker på att du vill ta bort den här filen\?
 \n
 \n<b>Varning:</b> Den här åtgärden kommer inte att ta bort kopior av den här filen som finns lagrad på andra enheter eller servrar. </string>
-    <string name="also_end_conversation">Stäng denna konversation efteråt</string>
+    <string name="also_end_conversation">Stäng den här konversation efteråt</string>
     <string name="choose_presence">Välj enhet</string>
     <string name="send_unencrypted_message">Skicka okrypterat meddelande</string>
     <string name="send_message">Skicka meddelande</string>
@@ -97,79 +99,83 @@
     <string name="send_omemo_message">Skicka OMEMO-krypterat meddelande</string>
     <string name="send_omemo_x509_message">Skicka v\\OMEMO-krypterat meddelande</string>
     <string name="send_pgp_message">Skicka OpenPGP-krypterat meddelande</string>
-    <string name="your_nick_has_been_changed">Nytt smeknamn används</string>
+    <string name="your_nick_has_been_changed">Nytt smeknamn är nu i bruk</string>
     <string name="send_unencrypted">Skicka okrypterat</string>
     <string name="decryption_failed">Dekrypteringen misslyckades. Du har kanske inte rätt privat nyckel.</string>
     <string name="openkeychain_required">OpenKeychain</string>
-    <string name="openkeychain_required_long"><![CDATA[%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änglig via F-Droid and Google Play.<br><br><small>(Var god och starta om %1$s efter installationen.)</small>]]></string>
+    <string name="openkeychain_required_long">%1$s använder &lt;b&gt;OpenKeychain&lt;/b&gt; för att kryptera och avkryptera dina publika nycklar.&lt;br&gt;&lt;br&gt;Programmet är licensierat under GPLv3+, och finns tillgängligt via F-Droid och Google Play.&lt;br&gt;&lt;br&gt;&lt;small&gt;(Var god starta om %1$s efter installationen.)&lt;/small&gt;</string>
     <string name="restart">Starta om</string>
     <string name="install">Installera</string>
-    <string name="openkeychain_not_installed">Installera OpenKeychain</string>
+    <string name="openkeychain_not_installed">Var god installera OpenKeychain</string>
     <string name="offering">erbjuder…</string>
     <string name="waiting">väntar…</string>
-    <string name="no_pgp_key">Ingen OpenPGP-nyckel funnen</string>
-    <string name="contact_has_no_pgp_key">Det gick inte att kryptera ditt meddelande eftersom att din kontakt inte har annonserat sin publika nyckel.\n\n<small>Vänligen be din kontakt att sätta upp OpenPGP.</small></string>
-    <string name="no_pgp_keys">Inga OpenPGP-nycklar funna</string>
-    <string name="contacts_have_no_pgp_keys">Det gick inte att kryptera ditt meddelande eftersom att din kontakt inte har annonserat sina publika nycklar.\n\n<small>Vänligen be din kontakt att sätta upp OpenPGP.</small></string>
-    <string name="pref_general">Generellt</string>
+    <string name="no_pgp_key">Ingen OpenPGP-nyckel hittades</string>
+    <string name="contact_has_no_pgp_key">Det gick inte att kryptera ditt meddelande, eftersom att din kontakt inte offentligjort sin publika nyckel.
+\n
+\n<small>Vänligen be din kontakt att sätta upp OpenPGP.</small></string>
+    <string name="no_pgp_keys">Inga OpenPGP-nycklar hittades</string>
+    <string name="contacts_have_no_pgp_keys">Det gick inte att kryptera ditt meddelande, eftersom att din kontakt inte har offentliggjort sina publika nycklar.
+\n
+\n<small>Vänligen be din kontakt att sätta upp OpenPGP.</small></string>
+    <string name="pref_general">Allmänt</string>
     <string name="pref_accept_files">Acceptera filer</string>
     <string name="pref_accept_files_summary">Acceptera automatiskt filer som är mindre än…</string>
-    <string name="pref_attachments">Bifogningar</string>
-    <string name="pref_notification_settings">Notifiering</string>
+    <string name="pref_attachments">Bilagor</string>
+    <string name="pref_notification_settings">Avisering</string>
     <string name="pref_vibrate">Vibrera</string>
-    <string name="pref_vibrate_summary">Vibrera när meddelande tagits emot</string>
-    <string name="pref_led">LED notifieringar</string>
-    <string name="pref_led_summary">Blinka med notifieringsljuset när ett meddelande tagits emot</string>
-    <string name="pref_ringtone">Meddelandesignal</string>
+    <string name="pref_vibrate_summary">Vibrera när ett nytt meddelande tas emot</string>
+    <string name="pref_led">LED-avisering</string>
+    <string name="pref_led_summary">Blinka med aviseringsljuset när ett nytt meddelande tas emot</string>
+    <string name="pref_ringtone">Ringsignal</string>
     <string name="pref_notification_sound">Aviseringsljud</string>
-    <string name="pref_notification_sound_summary">Aviseringsljud för nya meddelande</string>
+    <string name="pref_notification_sound_summary">Aviseringsljud för nya meddelanden</string>
     <string name="pref_call_ringtone_summary">Ringsignal för inkommande samtal</string>
-    <string name="pref_notification_grace_period">Notifieringsfrist</string>
-    <string name="pref_notification_grace_period_summary">Tidsgräns för hur länge notiser ska tystas efter att aktivitet har upptäckts på en av dina andra enheter.</string>
+    <string name="pref_notification_grace_period">Tidsfrist</string>
+    <string name="pref_notification_grace_period_summary">Tidsfrist för hur länge aviseringar ska vara tysta efter att aktivitet har upptäckts på en av dina andra enheter.</string>
     <string name="pref_advanced_options">Avancerat</string>
-    <string name="pref_never_send_crash">Skicka aldrig krasch-rapporter</string>
-    <string name="pref_never_send_crash_summary">Genom att skicka in kraschrapporter hjälper du den pågående utvecklingen</string>
+    <string name="pref_never_send_crash">Skicka aldrig kraschrapporter</string>
+    <string name="pref_never_send_crash_summary">Genom att skicka in kraschrapporter hjälper du till med utvecklingen av appen</string>
     <string name="pref_confirm_messages">Bekräfta meddelanden</string>
     <string name="pref_confirm_messages_summary">Låt dina kontakter veta när du har mottagit och läst deras meddelanden</string>
     <string name="pref_prevent_screenshots">Förhindra skärmdumpar</string>
-    <string name="pref_prevent_screenshots_summary">Dölj innehållet från applikationen i applikationsväxlaren och blockera skärmdumpar</string>
+    <string name="pref_prevent_screenshots_summary">Dölj innehållet från applikationen i appväxlaren, och blockera skärmdumpar</string>
     <string name="pref_ui_options">Gränssnitt</string>
-    <string name="openpgp_error">OpenKeychain genererade ett fel.</string>
-    <string name="bad_key_for_encryption">Dålig krypterings-nyckel.</string>
+    <string name="openpgp_error">OpenKeychain orsakade ett fel.</string>
+    <string name="bad_key_for_encryption">Ogiltig nyckel för kryptering.</string>
     <string name="accept">Acceptera</string>
     <string name="error">Ett fel har inträffat</string>
     <string name="recording_error">Fel</string>
     <string name="your_account">Ditt konto</string>
-    <string name="send_presence_updates">Skicka tillgänglighetsuppdatering</string>
-    <string name="receive_presence_updates">Ta emot tillgänglighetsuppdateringar</string>
-    <string name="ask_for_presence_updates">Be om tillgänglighetsuppdateringar</string>
+    <string name="send_presence_updates">Skicka närvarouppdateringar</string>
+    <string name="receive_presence_updates">Ta emot närvarouppdateringar</string>
+    <string name="ask_for_presence_updates">Be om närvarouppdateringar</string>
     <string name="attach_choose_picture">Välj bild</string>
     <string name="attach_take_picture">Ta ny bild</string>
-    <string name="preemptively_grant">Tillåt abonnemangsbegäran i förväg</string>
-    <string name="error_not_an_image_file">Filen du valt är inte en bild</string>
+    <string name="preemptively_grant">Bevilja prenumerationsförfrågan i förebyggande syfte</string>
+    <string name="error_not_an_image_file">Filen som du har valt, är inte en bild</string>
     <string name="error_compressing_image">Det gick inte att konvertera bildfilen</string>
-    <string name="error_file_not_found">Filen hittas ej</string>
-    <string name="error_io_exception">Generellt I/O-fel. Du kanske fick slut på plats?</string>
-    <string name="error_security_exception_during_image_copy">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.
+    <string name="error_file_not_found">Filen kunde ej hittas</string>
+    <string name="error_io_exception">Generellt I/O-fel. Du kanske fick slut på lagringsutrymme\?</string>
+    <string name="error_security_exception_during_image_copy">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
 \n<small>Använd en annan filhanterare för att välja en bild</small>.</string>
-    <string name="error_security_exception">Applikationen du använde för att dela den här filen tillhandahöll inte tillräckligt med behörigheter.</string>
+    <string name="error_security_exception">Appen som du använde för att dela den här filen, tillhandahöll inte tillräckligt med rättigheter.</string>
     <string name="account_status_unknown">Okänd</string>
     <string name="account_status_disabled">Tillfälligt inaktiverad</string>
-    <string name="account_status_online">Online</string>
+    <string name="account_status_online">Uppkopplad</string>
     <string name="account_status_connecting">Ansluter\u2026</string>
-    <string name="account_status_offline">Offline</string>
-    <string name="account_status_unauthorized">Otillåten</string>
-    <string name="account_status_not_found">Server ej funnen</string>
+    <string name="account_status_offline">Nedkopplad</string>
+    <string name="account_status_unauthorized">Ej behörig</string>
+    <string name="account_status_not_found">Servern hittades inte</string>
     <string name="account_status_no_internet">Ingen anslutning</string>
-    <string name="account_status_regis_fail">Registreringsfel</string>
-    <string name="account_status_regis_conflict">Användarnamn används redan</string>
-    <string name="account_status_regis_success">Registrering klar</string>
-    <string name="account_status_regis_not_sup">Registrering stöds ej av server</string>
-    <string name="account_status_regis_invalid_token">Ogiltigt registreringstoken</string>
+    <string name="account_status_regis_fail">Registreringen misslyckades</string>
+    <string name="account_status_regis_conflict">Användarnamnet används redan</string>
+    <string name="account_status_regis_success">Registreringen genomförd</string>
+    <string name="account_status_regis_not_sup">Registrering stöds inte av servern</string>
+    <string name="account_status_regis_invalid_token">Ogiltig registreringstoken</string>
     <string name="account_status_tls_error">TLS-förhandling misslyckades</string>
-    <string name="account_status_tls_error_domain">Domänen kan inte verifieras</string>
-    <string name="account_status_policy_violation">Kränkning av policy</string>
+    <string name="account_status_tls_error_domain">Domän kan inte verifieras</string>
+    <string name="account_status_policy_violation">Policyöverträdelse</string>
     <string name="account_status_incompatible_server">Inkompatibel server</string>
     <string name="account_status_stream_error">Strömningsfel</string>
     <string name="account_status_stream_opening_error">Fel vid öppning av ström</string>
@@ -179,44 +185,45 @@
     <string name="encryption_choice_omemo">OMEMO</string>
     <string name="mgmt_account_delete">Ta bort konto</string>
     <string name="mgmt_account_disable">Inaktivera tillfälligt</string>
-    <string name="mgmt_account_publish_avatar">Publicera avatarbild</string>
-    <string name="mgmt_account_publish_pgp">Publicera OpenPGP publik nyckel</string>
-    <string name="unpublish_pgp">Ta bort OpenPGP publik nyckel</string>
-    <string name="unpublish_pgp_message">Ä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.</string>
+    <string name="mgmt_account_publish_avatar">Publicera visningsbild</string>
+    <string name="mgmt_account_publish_pgp">Publicera publik OpenPGP-nyckel</string>
+    <string name="unpublish_pgp">Ta bort publik OpenPGP-nyckel</string>
+    <string name="unpublish_pgp_message">Ä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.</string>
     <string name="openpgp_has_been_published">OpenPGP-nyckel har publicerats.</string>
     <string name="mgmt_account_enable">Aktivera konto</string>
-    <string name="mgmt_account_delete_confirm_text">Är du säker på att du vill ta bort ditt konto\? Om du tar bort ditt konto raderas hela din konversationshistorik</string>
+    <string name="mgmt_account_delete_confirm_text">Är du säker på att du vill ta bort ditt konto\? Om du tar bort ditt konto, tas hela din konversationshistorik bort</string>
     <string name="attach_record_voice">Spela in röst</string>
     <string name="account_settings_jabber_id">XMPP-adress</string>
     <string name="block_jabber_id">Blockera XMPP-adress</string>
     <string name="account_settings_example_jabber_id">användarnamn@exempel.se</string>
     <string name="password">Lösenord</string>
-    <string name="invalid_jid">Detta är inte en giltig XMPP-adress</string>
+    <string name="invalid_jid">Det här är inte en giltig XMPP-adress</string>
     <string name="error_out_of_memory">Slut på minne. Bilden är för stor</string>
-    <string name="add_phone_book_text">Vill du lägga till %s i din enhets kontakter?</string>
-    <string name="server_info_show_more">Server-info</string>
-    <string name="server_info_mam">XEP-0313: Message Archive</string>
+    <string name="add_phone_book_text">Vill du lägga till %s i din kontaktbok\?</string>
+    <string name="server_info_show_more">Serverinfo</string>
+    <string name="server_info_mam">XEP-0313: MAM</string>
     <string name="server_info_carbon_messages">XEP-0280: Message Carbons</string>
     <string name="server_info_csi">XEP-0352: Client State Indication</string>
     <string name="server_info_blocking">XEP-0191: Blocking Command</string>
     <string name="server_info_roster_version">XEP-0237: Roster Versioning</string>
     <string name="server_info_stream_management">XEP-0198: Stream Management</string>
     <string name="server_info_external_service_discovery">XEP-0215: External Service Discovery</string>
-    <string name="server_info_pep">XEP-0163: PEP (Avatarbilder / OMEMO)</string>
-    <string name="server_info_http_upload">XEP-0363: Ladda upp via HTTP</string>
+    <string name="server_info_pep">XEP-0163: PEP (Avatars / OMEMO)</string>
+    <string name="server_info_http_upload">XEP-0363: HTTP File Upload</string>
     <string name="server_info_push">XEP-0357: Push</string>
     <string name="server_info_available">tillgänglig</string>
     <string name="server_info_unavailable">otillgänglig</string>
-    <string name="missing_public_keys">Annonsering om publik nyckel saknas</string>
-    <string name="last_seen_now">senast sedd just nu</string>
-    <string name="last_seen_min">senast sedd för en minut sedan</string>
-    <string name="last_seen_mins">senast sedd %d minuter sedan</string>
-    <string name="last_seen_hour">senast sedd för en timme sedan</string>
-    <string name="last_seen_hours">senast sedd %d timmar sedan</string>
-    <string name="last_seen_day">senast sedd för en dag sedan</string>
-    <string name="last_seen_days">senast sedd %d dagar sedan</string>
-    <string name="install_openkeychain">Krypterat meddelande. Installera OpenKeychain för att dekryptera meddelandet.</string>
-    <string name="openpgp_messages_found">Nytt OpenPGP krypterat meddelande hittades</string>
+    <string name="missing_public_keys">Tillkännagivanden om offentliga nyckel saknas</string>
+    <string name="last_seen_now">sågs senast nyss</string>
+    <string name="last_seen_min">sågs senast för en minut sedan</string>
+    <string name="last_seen_mins">sågs senast för %d minuter sedan</string>
+    <string name="last_seen_hour">sågs senast för en timme sedan</string>
+    <string name="last_seen_hours">sågs senast för %d timmar sedan</string>
+    <string name="last_seen_day">sågs senast för en dag sedan</string>
+    <string name="last_seen_days">sågs senast för %d dagar sedan</string>
+    <string name="install_openkeychain">Krypterat meddelande. Installera OpenKeychain för att avkryptera meddelandet.</string>
+    <string name="openpgp_messages_found">Nya OpenPGP-krypterade meddelande hittades</string>
     <string name="openpgp_key_id">OpenPGP-nyckel-ID</string>
     <string name="omemo_fingerprint">OMEMO-fingeravtryck</string>
     <string name="omemo_fingerprint_x509">v\\OMEMO-fingeravtryck</string>
@@ -225,96 +232,102 @@
     <string name="other_devices">Andra enheter</string>
     <string name="trust_omemo_fingerprints">Lita på OMEMO-fingeravtryck</string>
     <string name="fetching_keys">Hämtar nycklar …</string>
-    <string name="done">Klar</string>
+    <string name="done">Färdig</string>
     <string name="decrypt">Avkryptera</string>
     <string name="search">Sök</string>
-    <string name="enter_contact">Fyll i kontakt</string>
+    <string name="enter_contact">Ange kontakt</string>
     <string name="delete_contact">Ta bort kontakt</string>
-    <string name="view_contact_details">Se kontaktdetaljer</string>
+    <string name="view_contact_details">Visa kontaktdetaljer</string>
     <string name="block_contact">Blockera kontakt</string>
     <string name="unblock_contact">Avblockera kontakt</string>
     <string name="create">Skapa</string>
     <string name="select">Välj</string>
     <string name="contact_already_exists">Kontakten finns redan</string>
     <string name="join">Gå med</string>
-    <string name="channel_full_jid_example">rum@konferens.exempel.se/användarnamn</string>
+    <string name="channel_full_jid_example">rum@konferens.exempel.se/smeknamn</string>
     <string name="channel_bare_jid_example">rum@konferens.exempel.se</string>
     <string name="save_as_bookmark">Spara som bokmärke</string>
     <string name="delete_bookmark">Ta bort bokmärke</string>
-    <string name="destroy_room">Förstör gruppchat</string>
+    <string name="destroy_room">Förstör gruppchatt</string>
     <string name="destroy_channel">Förstör kanal</string>
-    <string name="destroy_room_dialog">Är du säker på att du vill förstöra den här gruppchatten?\n\n<b>Varning:</b> Gruppchatten kommer att tas bort helt från servern.</string>
-    <string name="destroy_channel_dialog">Är du säker på att du vill förstöra den här publika chattgruppen?\n\n<b>Varning:</b> Den här gruppchatten kommer att tas bort helt från servern.</string>
-    <string name="could_not_destroy_room">Det gick inte att ta bort gruppchatten</string>
-    <string name="could_not_destroy_channel">Det gick inte att ta bort kanalen</string>
+    <string name="destroy_room_dialog">Är du säker på att du vill förstöra den här gruppchatten\?
+\n
+\n<b>Varning:</b> Gruppchatten kommer att fullständigt raderas från servern.</string>
+    <string name="destroy_channel_dialog">Är du säker på att du vill förstöra den här publika chattgruppen\?
+\n
+\n<b>Varning:</b> Den här gruppchatten kommer att fullständigt raderas från servern.</string>
+    <string name="could_not_destroy_room">Det gick inte att förstöra gruppchatten</string>
+    <string name="could_not_destroy_channel">Det gick inte att förstöra kanalen</string>
     <string name="action_edit_subject">Redigera ämnet för gruppchatten</string>
     <string name="topic">Ämne</string>
-    <string name="joining_conference">Går med i gruppchatt …</string>
+    <string name="joining_conference">Går med i gruppchatt…</string>
     <string name="leave">Lämna</string>
-    <string name="contact_added_you">Kontakten lade till dig i sin kontaktlista</string>
-    <string name="add_back">Addera tillbaka</string>
-    <string name="contact_has_read_up_to_this_point">%s har läst hit</string>
-    <string name="contacts_have_read_up_to_this_point">%s har läst till den här punkten</string>
-    <string name="contacts_and_n_more_have_read_up_to_this_point">%1$s +%2$d andra har läst till den här punkten</string>
-    <string name="everyone_has_read_up_to_this_point">Alla har läst fram till hit</string>
+    <string name="contact_added_you">Kontakten har lagt till dig i sin kontaktlista</string>
+    <string name="add_back">Lägg också till kontakt</string>
+    <string name="contact_has_read_up_to_this_point">%s har läst fram till denna punkt</string>
+    <string name="contacts_have_read_up_to_this_point">%s har läst fram till denna punkt</string>
+    <string name="contacts_and_n_more_have_read_up_to_this_point">%1$s +%2$d andra har läst fram till denna punkt</string>
+    <string name="everyone_has_read_up_to_this_point">Alla har läst fram till denna punkt</string>
     <string name="publish">Publicera</string>
     <string name="touch_to_choose_picture">Tryck på visningsbilden för att välja en bild från galleriet</string>
     <string name="publishing">Publicerar…</string>
-    <string name="error_publish_avatar_server_reject">Servern kunde inte publicera</string>
+    <string name="error_publish_avatar_server_reject">Servern avvisade din publicering</string>
     <string name="error_publish_avatar_converting">Det gick inte att konvertera din bild</string>
-    <string name="error_saving_avatar">Kunde inte spara avatarbild till disk</string>
-    <string name="or_long_press_for_default">(Eller tryck länge för att få tillbaks förvald)</string>
-    <string name="error_publish_avatar_no_server_support">Din server stödjer inte publicering av visningsbilder</string>
-    <string name="private_message">privat meddelande</string>
+    <string name="error_saving_avatar">Det gick inte att spara ner visningsbilden till telefonen</string>
+    <string name="or_long_press_for_default">(Långtryck för att återställa standardinställningen)</string>
+    <string name="error_publish_avatar_no_server_support">Din server stöder inte publicering av visningsbilder</string>
+    <string name="private_message">viskade</string>
     <string name="private_message_to">till %s</string>
     <string name="send_private_message_to">Skicka privat meddelande till %s</string>
     <string name="connect">Anslut</string>
-    <string name="account_already_exists">Detta konto finns redan</string>
+    <string name="account_already_exists">Det här kontot existerar redan</string>
     <string name="next">Nästa</string>
     <string name="server_info_session_established">Session etablerad</string>
     <string name="skip">Hoppa över</string>
-    <string name="disable_notifications">Inaktivera notifieringar</string>
+    <string name="disable_notifications">Inaktivera aviseringar</string>
     <string name="enable">Aktivera</string>
-    <string name="conference_requires_password">Gruppchatten kräver lösenord</string>
-    <string name="enter_password">Fyll i lösenord</string>
-    <string name="request_presence_updates">Var god begär närvarouppdateringar från din kontakt först.\n\n<small>Detta kommer att användas för att avgöra vilken chattapplikationen din kontakt använder</small>.</string>
+    <string name="conference_requires_password">Gruppchatten kräver ett lösenord</string>
+    <string name="enter_password">Ange lösenord</string>
+    <string name="request_presence_updates">Vänligen begär närvarouppdateringar från din kontakt först.
+\n
+\n<small>Det här kommer att användas för att avgöra vilken chattklient som din kontakt använder</small>.</string>
     <string name="request_now">Begär nu</string>
     <string name="ignore">Ignorera</string>
     <string name="without_mutual_presence_updates"><b>Varning:</b> Att skicka detta utan ömsesidiga närvarouppdateringar kan orsaka oväntade problem.\n\n<small>Gå till \"Kontaktuppgifter\" för att verifiera dina närvaroprenumerationer.</small></string>
     <string name="pref_security_settings">Säkerhet</string>
-    <string name="pref_allow_message_correction">Tillåt korrigeringar av meddelanden</string>
-    <string name="pref_allow_message_correction_summary">Tillåt att dina kontakter kan ändra sina meddelanden i efterhand</string>
+    <string name="pref_allow_message_correction">Tillåt meddelandekorrigering</string>
+    <string name="pref_allow_message_correction_summary">Tillåt dina kontakter att retroaktivt redigera sina meddelanden</string>
     <string name="pref_expert_options">Expertinställningar</string>
-    <string name="pref_expert_options_summary">Var försiktig med dem</string>
+    <string name="pref_expert_options_summary">Var försiktig med dessa</string>
     <string name="title_activity_about_x">Om %s</string>
     <string name="title_pref_quiet_hours">Tysta timmar</string>
     <string name="title_pref_quiet_hours_start_time">Starttid</string>
     <string name="title_pref_quiet_hours_end_time">Sluttid</string>
-    <string name="title_pref_enable_quiet_hours">Aktivera tysta timmar</string>
-    <string name="pref_quiet_hours_summary">Notifieringar kommer vara tysta under tysta timmar</string>
+    <string name="title_pref_enable_quiet_hours">Aktivera Tysta timmar</string>
+    <string name="pref_quiet_hours_summary">Aviseringar kommer att tystas under Tysta timmar</string>
     <string name="pref_expert_options_other">Annat</string>
     <string name="toast_message_omemo_fingerprint">OMEMO-fingeravtryck kopierat till urklipp</string>
     <string name="conference_banned">Du är avstängd från denna gruppchatt</string>
-    <string name="conference_members_only">Denna gruppchatt är endast för medlemmar</string>
+    <string name="conference_members_only">Den här gruppchatten är endast till för medlemmar</string>
     <string name="conference_resource_constraint">Resursbegränsning</string>
     <string name="conference_kicked">Du har blivit sparkad från den här gruppchatten</string>
     <string name="conference_shutdown">Gruppchatten stängdes ner</string>
-    <string name="conference_unknown_error">Du är inte längre med i denna gruppchatt</string>
+    <string name="conference_unknown_error">Du är inte längre med i den här gruppchatten</string>
     <string name="using_account">använder konto %s</string>
     <string name="hosted_on">huseras hos %s</string>
-    <string name="checking_x">Kontrollerar %s på webbserver</string>
+    <string name="checking_x">Kontrollerar %s på HTTP-värd</string>
     <string name="not_connected_try_again">Du är inte ansluten. Försök igen senare</string>
-    <string name="check_x_filesize">Kontrollera filstorleken på %s</string>
+    <string name="check_x_filesize">Kontrollera filstorleken för %s</string>
     <string name="check_x_filesize_on_host">Kontrollera filstorlek för %1$s på %2$s</string>
     <string name="message_options">Meddelandealternativ</string>
-    <string name="quote">Citera</string>
+    <string name="quote">Citat</string>
     <string name="paste_as_quote">Klistra in som citat</string>
-    <string name="copy_original_url">Kopiera orginal-URL</string>
+    <string name="copy_original_url">Kopiera den ursprungliga URL:n</string>
     <string name="send_again">Skicka igen</string>
     <string name="file_url">Fil-URL</string>
     <string name="url_copied_to_clipboard">Kopierade URL till urklipp</string>
     <string name="jabber_id_copied_to_clipboard">Kopierade XMPP-adress till urklipp</string>
-    <string name="error_message_copied_to_clipboard">Kopierade felmeddelande till urklipp</string>
+    <string name="error_message_copied_to_clipboard">Kopierade felmeddelandet till urklipp</string>
     <string name="web_address">webbadress</string>
     <string name="scan_qr_code">Scanna 2D-streckkod</string>
     <string name="show_qr_code">Visa 2D-streckkod</string>
@@ -323,93 +336,94 @@
     <string name="confirm">Bekräfta</string>
     <string name="try_again">Försök igen</string>
     <string name="pref_keep_foreground_service">Förgrundstjänst</string>
-    <string name="pref_keep_foreground_service_summary">Förehindrar operativsystemet att ta ner uppkopplingen</string>
+    <string name="pref_keep_foreground_service_summary">Förhindrar att operativsystemet avbryter din anslutning</string>
     <string name="pref_create_backup">Skapa säkerhetskopia</string>
-    <string name="pref_create_backup_summary">Säkerhetskopians filer lagras i %s</string>
-    <string name="notification_create_backup_title">Skapar filer för säkerhetskopia</string>
+    <string name="pref_create_backup_summary">Säkerhetskopierade filer kommer att lagras i %s</string>
+    <string name="notification_create_backup_title">Skapar säkerhetskopior</string>
     <string name="notification_backup_created_title">Din säkerhetskopia har skapats</string>
-    <string name="notification_backup_created_subtitle">Säkerhetskopians filer har lagrats i %s</string>
+    <string name="notification_backup_created_subtitle">De säkerhetskopierade filerna har lagrats i %s</string>
     <string name="restoring_backup">Återställer säkerhetskopia</string>
     <string name="notification_restored_backup_title">Din säkerhetskopia har återställts</string>
     <string name="notification_restored_backup_subtitle">Glöm inte att aktivera kontot.</string>
     <string name="choose_file">Välj fil</string>
-    <string name="receiving_x_file">Tar emot %1$s (%2$d%% klart)</string>
+    <string name="receiving_x_file">Tar emot %1$s (%2$d%% slutfört)</string>
     <string name="download_x_file">Ladda ner %s</string>
     <string name="delete_x_file">Ta bort %s</string>
     <string name="file">fil</string>
     <string name="open_x_file">Öppna %s</string>
-    <string name="sending_file">skickar (%1$d%% klart)</string>
-    <string name="preparing_file">Förbereder för delning av fil</string>
-    <string name="x_file_offered_for_download">%s erbjuden för nedladdning</string>
+    <string name="sending_file">skickar (%1$d%% slutfört)</string>
+    <string name="preparing_file">Förbereder delning av fil</string>
+    <string name="x_file_offered_for_download">%s erbjuds för nedladdning</string>
     <string name="cancel_transmission">Avbryt överföring</string>
     <string name="file_transmission_failed">det gick inte att dela fil</string>
     <string name="file_transmission_cancelled">filöverföring avbruten</string>
     <string name="file_deleted">Fil borttagen</string>
-    <string name="no_application_found_to_open_file">Ingen applikation som kunde öppna filen hittades</string>
-    <string name="no_application_found_to_open_link">Ingen applikation som kunde öppna länken hittades</string>
-    <string name="no_application_found_to_view_contact">Ingen applikation som kunde visa kontakten hittades</string>
+    <string name="no_application_found_to_open_file">Ingen app kunde hittas för att öppna filen</string>
+    <string name="no_application_found_to_open_link">Ingen app kunde hittas för att öppna länken</string>
+    <string name="no_application_found_to_view_contact">Ingen app kunde hittas för att visa kontakten</string>
     <string name="pref_show_dynamic_tags">Dynamiska etiketter</string>
     <string name="pref_show_dynamic_tags_summary">Visa skrivskyddade taggar under kontakter</string>
-    <string name="enable_notifications">Aktivera notifieringar</string>
+    <string name="enable_notifications">Aktivera aviseringar</string>
     <string name="no_conference_server_found">Ingen gruppchattserver hittades</string>
     <string name="conference_creation_failed">Det gick inte att skapa gruppchatten</string>
-    <string name="account_image_description">Kontots avatarbild</string>
+    <string name="account_image_description">Visningsbild för kontot</string>
     <string name="copy_omemo_clipboard_description">Kopiera OMEMO-fingeravtryck till urklipp</string>
-    <string name="regenerate_omemo_key">Regenerera OMEMO-nyckel</string>
+    <string name="regenerate_omemo_key">Skapa ny OMEMO-nyckel</string>
     <string name="clear_other_devices">Rensa enheter</string>
-    <string name="clear_other_devices_desc">Ä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.</string>
+    <string name="clear_other_devices_desc">Ä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.</string>
     <string name="error_no_keys_to_trust_server_error">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?</string>
-    <string name="error_no_keys_to_trust_presence">Det finns inga användbara nycklar tillgängliga för den här kontakten.\nSe till att ni båda har närvaroprenumeration.</string>
+    <string name="error_no_keys_to_trust_presence">Det finns inga användbara nycklar tillgängliga för den här kontakten.
+\nSe till att båda har närvaroprenumeration.</string>
     <string name="error_trustkeys_title">Något gick fel</string>
     <string name="fetching_history_from_server">Hämtar historik från server</string>
-    <string name="no_more_history_on_server">Ingen mer historik på server</string>
+    <string name="no_more_history_on_server">Ingen mer historik på servern</string>
     <string name="updating">Uppdaterar…</string>
-    <string name="password_changed">Lösenord bytt!</string>
-    <string name="could_not_change_password">Kunde inte byta lösenord</string>
-    <string name="change_password">Byt lösenord</string>
+    <string name="password_changed">Lösenordet ändrat!</string>
+    <string name="could_not_change_password">Det gick inte att ändra lösenordet</string>
+    <string name="change_password">Ändra lösenordet</string>
     <string name="current_password">Nuvarande lösenord</string>
     <string name="new_password">Nytt lösenord</string>
-    <string name="password_should_not_be_empty">Lösenord kan inte vara tomma</string>
+    <string name="password_should_not_be_empty">Lösenordet får inte vara tomt</string>
     <string name="enable_all_accounts">Aktivera alla konton</string>
-    <string name="disable_all_accounts">Deaktivera alla konton</string>
+    <string name="disable_all_accounts">Inaktivera alla konton</string>
     <string name="perform_action_with">Utför åtgärden med</string>
     <string name="no_affiliation">Ingen anknytning</string>
-    <string name="no_role">Offline</string>
+    <string name="no_role">Frånkopplad</string>
     <string name="outcast">Utstött</string>
     <string name="member">Medlem</string>
     <string name="advanced_mode">Avancerat läge</string>
     <string name="grant_membership">Bevilja medlemsprivilegier</string>
     <string name="remove_membership">Återkalla medlemsprivilegier</string>
-    <string name="grant_admin_privileges">Bevilja administratörsbehörighet</string>
-    <string name="remove_admin_privileges">Återkalla administratörsbehörighet</string>
+    <string name="grant_admin_privileges">Bevilja administratörsbehörigheter</string>
+    <string name="remove_admin_privileges">Återkalla administratörsbehörigheter</string>
     <string name="grant_owner_privileges">Bevilja ägarprivilegier</string>
     <string name="remove_owner_privileges">Återkalla ägarprivilegier</string>
     <string name="remove_from_room">Ta bort från gruppchatt</string>
     <string name="remove_from_channel">Ta bort från kanal</string>
-    <string name="could_not_change_affiliation">Kunde inte ändra tillhörigheten för %s</string>
-    <string name="ban_from_conference">Förbjud från gruppchatt</string>
-    <string name="ban_from_channel">Förbjud från kanal</string>
-    <string name="removing_from_public_conference">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.</string>
+    <string name="could_not_change_affiliation">Kunde inte ändra anknytningen till %s</string>
+    <string name="ban_from_conference">Blockera från gruppchatt</string>
+    <string name="ban_from_channel">Blockera från kanal</string>
+    <string name="removing_from_public_conference">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.</string>
     <string name="ban_now">Bannlys nu</string>
     <string name="could_not_change_role">Kunde inte ändra rollen för %s</string>
-    <string name="conference_options">Privat gruppchattskonfiguration</string>
-    <string name="channel_options">Publik kanalkonfiguration</string>
-    <string name="members_only">Privat, medlemsskap krävs</string>
+    <string name="conference_options">Konfiguration för privat gruppchatt</string>
+    <string name="channel_options">Konfiguration för publik kanal</string>
+    <string name="members_only">Privat, endast medlemmar</string>
     <string name="non_anonymous">Gör XMPP-adresser synliga för alla</string>
     <string name="moderated">Gör kanalen modererad</string>
-    <string name="you_are_not_participating">Du deltar ej</string>
+    <string name="you_are_not_participating">Du deltar inte</string>
     <string name="modified_conference_options">Ändrade gruppchattalternativ!</string>
     <string name="could_not_modify_conference_options">Det gick inte att ändra alternativ för gruppchatt</string>
     <string name="never">Aldrig</string>
     <string name="until_further_notice">Tills vidare</string>
     <string name="snooze">Snooza</string>
     <string name="reply">Svara</string>
-    <string name="mark_as_read">Läsmarkera</string>
-    <string name="pref_input_options">Input</string>
-    <string name="pref_enter_is_send">Skicka med enter</string>
+    <string name="mark_as_read">Markera som läst</string>
+    <string name="pref_input_options">Inmatning</string>
+    <string name="pref_enter_is_send">Skicka meddelanden med Enter-tangenten</string>
     <string name="pref_enter_is_send_summary">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.</string>
-    <string name="pref_display_enter_key">Visa enter-knappen</string>
-    <string name="pref_display_enter_key_summary">Byt ut emoticons-tangenten mot en enter-tangent</string>
+    <string name="pref_display_enter_key">Visa Enter-tangenten</string>
+    <string name="pref_display_enter_key_summary">Byt ut Emoji-tangenten mot en Enter-tangent</string>
     <string name="audio">ljud</string>
     <string name="video">video</string>
     <string name="image">bild</string>
@@ -417,36 +431,36 @@
     <string name="pdf_document">PDF-dokument</string>
     <string name="apk">Android-app</string>
     <string name="vcard">Kontakt</string>
-    <string name="avatar_has_been_published">Avatarbild har publicerats!</string>
+    <string name="avatar_has_been_published">Visningsbilden har publicerats!</string>
     <string name="sending_x_file">Skickar %s</string>
     <string name="offering_x_file">Erbjuder %s</string>
-    <string name="hide_offline">Dölj ej anslutna</string>
-    <string name="contact_is_typing">%s skriver …</string>
-    <string name="contact_has_stopped_typing">%s har slutat skriva</string>
-    <string name="contacts_are_typing">%s skriver …</string>
-    <string name="contacts_have_stopped_typing">%s har slutat skriva</string>
-    <string name="pref_chat_states">Skriv-notifieringar</string>
+    <string name="hide_offline">Dölj frånkopplade</string>
+    <string name="contact_is_typing">%s skriver…</string>
+    <string name="contact_has_stopped_typing">%s har slutat att skriva</string>
+    <string name="contacts_are_typing">%s skriver…</string>
+    <string name="contacts_have_stopped_typing">%s har slutat att skriva</string>
+    <string name="pref_chat_states">Skrivaviseringar</string>
     <string name="pref_chat_states_summary">Låt dina kontakter veta när du skriver meddelande till dem</string>
     <string name="send_location">Skicka position</string>
     <string name="show_location">Visa position</string>
-    <string name="no_application_found_to_display_location">Ingen applikation hittades för att visa platsdata</string>
+    <string name="no_application_found_to_display_location">Ingen app hittades för att kunna visa platsen</string>
     <string name="location">Position</string>
     <string name="title_undo_swipe_out_conversation">Konversation stängd</string>
     <string name="title_undo_swipe_out_group_chat">Lämnade privat gruppchatt</string>
     <string name="title_undo_swipe_out_channel">Lämnade publik kanal</string>
-    <string name="pref_dont_trust_system_cas_title">Lita inte på systemets CAs</string>
-    <string name="pref_dont_trust_system_cas_summary">Alla certifikat måste manuellt godkännas</string>
+    <string name="pref_dont_trust_system_cas_title">Lita inte på systemets certifikatutfärdare</string>
+    <string name="pref_dont_trust_system_cas_summary">Alla certifikat måste godkännas manuellt</string>
     <string name="pref_remove_trusted_certificates_title">Ta bort certifikat</string>
-    <string name="pref_remove_trusted_certificates_summary">Ta bort manuellt accepterade certifikat</string>
-    <string name="toast_no_trusted_certs">Inga manuellt accepterade certifikat</string>
-    <string name="dialog_manage_certs_title">Ta bort certifikat</string>
+    <string name="pref_remove_trusted_certificates_summary">Ta bort manuellt godkända certifikat</string>
+    <string name="toast_no_trusted_certs">Inga manuellt godkända certifikat</string>
+    <string name="dialog_manage_certs_title">Ta bort certifikaten</string>
     <string name="dialog_manage_certs_positivebutton">Ta bort val</string>
     <string name="dialog_manage_certs_negativebutton">Avbryt</string>
     <plurals name="toast_delete_certificates">
         <item quantity="one">%d certifikat borttaget</item>
         <item quantity="other">%d certifikat borttagna</item>
     </plurals>
-    <string name="pref_quick_action_summary">Ersätt \"Skicka\"-knappen med snabbåtgärd</string>
+    <string name="pref_quick_action_summary">Ersätt \"Skicka\"-knappen med en snabbfunktion</string>
     <string name="pref_quick_action">Snabbfunktion</string>
     <string name="none">Ingen</string>
     <string name="recently_used">Senast använd</string>
@@ -456,175 +470,181 @@
     <string name="user_has_left_conference">%1$s har lämnat gruppchatten</string>
     <string name="username">Användarnamn</string>
     <string name="username_hint">Användarnamn</string>
-    <string name="invalid_username">Inte ett giltigt användanamn</string>
-    <string name="download_failed_server_not_found">Nerladdning gick fel: Server hittades inte</string>
-    <string name="download_failed_file_not_found">Nerladdning gick fel: Filen hittades inte</string>
-    <string name="download_failed_could_not_connect">Nerladdningen gick fel: Kunder inte ansluta till server</string>
-    <string name="download_failed_could_not_write_file">Nerladdning gick fel: Kunde inte skriva fil</string>
+    <string name="invalid_username">Det här är inte ett giltigt användarnamn</string>
+    <string name="download_failed_server_not_found">Nedladdning misslyckades: Servern hittades inte</string>
+    <string name="download_failed_file_not_found">Nedladdning misslyckades: Filen hittades inte</string>
+    <string name="download_failed_could_not_connect">Nedladdning misslyckades: Kunde inte ansluta till värden</string>
+    <string name="download_failed_could_not_write_file">Nedladdning misslyckades: Kunde inte skriva filen</string>
     <string name="download_failed_invalid_file">Nedladdning misslyckades: Ogiltig fil</string>
-    <string name="account_status_tor_unavailable">Tor-nätverk ej tillgängligt</string>
-    <string name="account_status_bind_failure">Bind-fel</string>
-    <string name="account_status_host_unknown">Den här servern ansvarar inte för den här domänen</string>
-    <string name="server_info_broken">Sönder</string>
+    <string name="account_status_tor_unavailable">Tor-nätverket är inte tillgängligt</string>
+    <string name="account_status_bind_failure">Bindningsfel</string>
+    <string name="account_status_host_unknown">Servern ansvarar inte för den här domänen</string>
+    <string name="server_info_broken">Trasig</string>
     <string name="pref_presence_settings">Tillgänglighet</string>
-    <string name="pref_away_when_screen_off">Frånvarande när enheten är låst</string>
-    <string name="pref_away_when_screen_off_summary">Visa som frånvarande när enheten är låst</string>
-    <string name="pref_dnd_on_silent_mode">Upptagen i ljudlöst läge</string>
-    <string name="pref_dnd_on_silent_mode_summary">Visa som Upptagen i ljudlöst läge</string>
-    <string name="pref_treat_vibrate_as_silent">Hantera vibrationsläge som tyst läge</string>
-    <string name="pref_treat_vibrate_as_dnd_summary">Visa som Upptagen när enheten är satt på att endast vibrera</string>
+    <string name="pref_away_when_screen_off">Borta när enheten är låst</string>
+    <string name="pref_away_when_screen_off_summary">Visa som Borta när enheten är låst</string>
+    <string name="pref_dnd_on_silent_mode">Upptagen i tyst läge</string>
+    <string name="pref_dnd_on_silent_mode_summary">Visa som upptagen när enheten är i tyst läge</string>
+    <string name="pref_treat_vibrate_as_silent">Behandla vibrera-läget som tyst läge</string>
+    <string name="pref_treat_vibrate_as_dnd_summary">Visa som upptagen när enheten är satt till att endast vibrera</string>
     <string name="pref_show_connection_options">Utökade anslutningsinställningar</string>
-    <string name="pref_show_connection_options_summary">Visa val av servernamn och port vid inställning av konto</string>
-    <string name="hostname_example">xmpp.example.com</string>
+    <string name="pref_show_connection_options_summary">Visa värdnamn och portinställningar när du skapar ett konto</string>
+    <string name="hostname_example">xmpp.exempel.se</string>
     <string name="action_add_account_with_certificate">Logga in med certifikat</string>
-    <string name="unable_to_parse_certificate">Det gick inte att analysera certifikatet</string>
+    <string name="unable_to_parse_certificate">Det gick inte att tolka certifikatet</string>
     <string name="mam_prefs">Arkiveringsinställningar</string>
-    <string name="server_side_mam_prefs">Arkiveringsinställningar på servern</string>
-    <string name="fetching_mam_prefs">Hämtar arkiveringsinställningar, vänta …</string>
+    <string name="server_side_mam_prefs">Arkiveringsinställningar på serversidan</string>
+    <string name="fetching_mam_prefs">Hämtar arkiveringsinställningar. Var god vänta…</string>
     <string name="unable_to_fetch_mam_prefs">Det gick inte att hämta arkiveringsinställningar</string>
-    <string name="captcha_required">CAPTCHA behövs</string>
-    <string name="captcha_hint">Skriv i texten från bilden ovan</string>
+    <string name="captcha_required">CAPTCHA krävs</string>
+    <string name="captcha_hint">Skriv in texten från bilden ovanför</string>
     <string name="certificate_chain_is_not_trusted">Otillförlitlig certifikatkedja</string>
     <string name="jid_does_not_match_certificate">XMPP-adressen matchar inte certifikatet</string>
     <string name="action_renew_certificate">Förnya certifikat</string>
-    <string name="error_fetching_omemo_key">Misslyckades med att hämta OMEMO-nyckel!</string>
-    <string name="verified_omemo_key_with_certificate">Verifierade OMEMO-nyckel med certifikat!</string>
-    <string name="device_does_not_support_certificates">Din enhet stödjer inte val av klientcertifikat!</string>
+    <string name="error_fetching_omemo_key">Fel vid hämtning av OMEMO-nyckeln!</string>
+    <string name="verified_omemo_key_with_certificate">Verifierade OMEMO-nyckeln med certifikatet!</string>
+    <string name="device_does_not_support_certificates">Din enhet stöder inte valet av klientcertifikat!</string>
     <string name="pref_connection_options">Anslutning</string>
-    <string name="pref_use_tor">Ansluten via Tor</string>
-    <string name="pref_use_tor_summary">Tunnla alla anslutningar genom Tor-nätverket. Kräver Orbot</string>
-    <string name="account_settings_hostname">Servernamn</string>
+    <string name="pref_use_tor">Anslut via Tor</string>
+    <string name="pref_use_tor_summary">Tunnla alla anslutningar via Tor-nätverket. Kräver Orbot</string>
+    <string name="account_settings_hostname">Värdnamn</string>
     <string name="account_settings_port">Port</string>
     <string name="hostname_or_onion">Server- eller .onion-adress</string>
-    <string name="not_a_valid_port">Inte ett giltigt portnummer</string>
-    <string name="not_valid_hostname">Inte ett giltigt servernamn</string>
+    <string name="not_a_valid_port">Det här är inte ett giltigt portnummer</string>
+    <string name="not_valid_hostname">Det här är inte ett giltigt värdnamn</string>
     <string name="connected_accounts">%1$d av %2$d konton anslutna</string>
     <plurals name="x_messages">
         <item quantity="one">%d meddelande</item>
         <item quantity="other">%d meddelanden</item>
     </plurals>
-    <string name="load_more_messages">Ladda fler meddelanden</string>
+    <string name="load_more_messages">Läs in fler meddelanden</string>
     <string name="shared_file_with_x">Fil delad med %s</string>
     <string name="shared_image_with_x">Bild delad med %s</string>
-    <string name="shared_images_with_x">Bilder som delats med %s</string>
-    <string name="shared_text_with_x">Text som delats med %s</string>
-    <string name="no_storage_permission">Ge %1$s åtkomst till extern lagring</string>
-    <string name="no_camera_permission">Ge %1$s åtkomst till kameran</string>
+    <string name="shared_images_with_x">Bilder delades med %s</string>
+    <string name="shared_text_with_x">Text delades med %s</string>
+    <string name="no_storage_permission">Bevilja %1$s åtkomst till extern lagring</string>
+    <string name="no_camera_permission">Bevilja %1$s åtkomst till kameran</string>
     <string name="sync_with_contacts">Synkronisera med kontakter</string>
-    <string name="sync_with_contacts_long">%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.</string>
-    <string name="notify_on_all_messages">Notifiera för alla meddelanden</string>
-    <string name="notify_only_when_highlighted">Notis endast vid omnämnande</string>
-    <string name="notify_never">Notifieringar deaktiverade</string>
-    <string name="notify_paused">Notifieringar pausade</string>
+    <string name="sync_with_contacts_long">%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.</string>
+    <string name="notify_on_all_messages">Avisering för alla meddelanden</string>
+    <string name="notify_only_when_highlighted">Avisering endast vid ett omnämnande</string>
+    <string name="notify_never">Aviseringar inaktiverade</string>
+    <string name="notify_paused">Aviseringar pausade</string>
     <string name="pref_picture_compression">Bildkomprimering</string>
-    <string name="pref_picture_compression_summary">Tips: Använd \"Välj fil\" istället för \"Välj bild\" för att skicka enskilda bilder okomprimerade, oavsett denna inställning.</string>
+    <string name="pref_picture_compression_summary">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.</string>
     <string name="always">Alltid</string>
     <string name="large_images_only">Endast stora bilder</string>
     <string name="battery_optimizations_enabled">Batterioptimeringar aktiverade</string>
-    <string name="battery_optimizations_enabled_explained">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.</string>
-    <string name="battery_optimizations_enabled_dialog">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.
+    <string name="battery_optimizations_enabled_explained">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.</string>
+    <string name="battery_optimizations_enabled_dialog">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.</string>
-    <string name="disable">Deaktivera</string>
-    <string name="selection_too_large">The valda området är för stort</string>
-    <string name="no_accounts">(Inget konto aktiverat)</string>
-    <string name="this_field_is_required">Detta fält måste fyllas i</string>
-    <string name="correct_message">Korrigera meddelanden</string>
+    <string name="disable">Inaktivera</string>
+    <string name="selection_too_large">Det valda området är för stort</string>
+    <string name="no_accounts">(Inga aktiverade konton)</string>
+    <string name="this_field_is_required">Detta fält är obligatoriskt</string>
+    <string name="correct_message">Korrigera meddelande</string>
     <string name="send_corrected_message">Skicka korrigerat meddelande</string>
-    <string name="no_keys_just_confirm">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.</string>
-    <string name="this_account_is_disabled">Du har deaktiverat detta konto</string>
+    <string name="no_keys_just_confirm">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.</string>
+    <string name="this_account_is_disabled">Du har inaktiverat det här kontot</string>
     <string name="security_error_invalid_file_access">Säkerhetsfel: Ogiltig filåtkomst!</string>
-    <string name="no_application_to_share_uri">Ingen applikation hittades för att dela URI</string>
-    <string name="share_uri_with">Dela URI med …</string>
-    <string name="agree_and_continue">Acceptera och gå vidare</string>
-    <string name="magic_create_text">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.</string>
+    <string name="no_application_to_share_uri">Ingen app hittades för att dela URI</string>
+    <string name="share_uri_with">Dela URI med…</string>
+    <string name="agree_and_continue">Acceptera och fortsätt</string>
+    <string name="magic_create_text">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.</string>
     <string name="your_full_jid_will_be">Din fullständiga XMPP-adress kommer att vara: %s</string>
     <string name="create_account">Skapa konto</string>
-    <string name="use_own_provider">Använd min egen leverantör</string>
-    <string name="pick_your_username">Välj användarnamn</string>
+    <string name="use_own_provider">Använd min egna leverantör</string>
+    <string name="pick_your_username">Välj ditt användarnamn</string>
     <string name="pref_manually_change_presence">Hantera tillgänglighet manuellt</string>
-    <string name="pref_manually_change_presence_summary">Ställ in din tillgänglighet när du redigerar ditt statusmeddelande.</string>
+    <string name="pref_manually_change_presence_summary">Ange din tillgänglighet när du redigerar ditt statusmeddelande.</string>
     <string name="status_message">Statusmeddelande</string>
-    <string name="presence_chat">Tillgänglig</string>
-    <string name="presence_online">Online</string>
+    <string name="presence_chat">Tillgänglig för chatt</string>
+    <string name="presence_online">Uppkopplad</string>
     <string name="presence_away">Borta</string>
     <string name="presence_xa">Ej tillgänglig</string>
     <string name="presence_dnd">Upptagen</string>
     <string name="secure_password_generated">Ett säkert lösenord har genererats</string>
-    <string name="device_does_not_support_battery_op">Din enhet stödjer inte deaktivering av batterioptimeringar</string>
-    <string name="registration_please_wait">Registreringfel: Försök igen senare</string>
-    <string name="registration_password_too_weak">Registreringsfel: Lösenordet är för svagt</string>
+    <string name="device_does_not_support_battery_op">Din enhet har inte stöd för att välja bort batterioptimering</string>
+    <string name="registration_please_wait">Registreringen misslyckades: Försök igen senare</string>
+    <string name="registration_password_too_weak">Registreringen misslyckades: Lösenordet är för svagt</string>
     <string name="choose_participants">Välj deltagare</string>
-    <string name="creating_conference">Skapar gruppchatt …</string>
+    <string name="creating_conference">Skapar gruppchatt…</string>
     <string name="invite_again">Bjud in igen</string>
-    <string name="gp_disable">Deaktivera</string>
+    <string name="gp_disable">Inaktivera</string>
     <string name="gp_short">Kort</string>
     <string name="gp_medium">Medium</string>
     <string name="gp_long">Lång</string>
-    <string name="pref_broadcast_last_activity">Gör användandet offentligt</string>
+    <string name="pref_broadcast_last_activity">Tillkännage användandet</string>
     <string name="pref_broadcast_last_activity_summary">Låter dina kontakter veta när du använder Conversations</string>
-    <string name="pref_privacy">Privatliv</string>
+    <string name="pref_privacy">Integritet</string>
     <string name="pref_theme_options">Tema</string>
-    <string name="pref_theme_options_summary">Välj färgschema</string>
+    <string name="pref_theme_options_summary">Välj färgpalett</string>
     <string name="pref_theme_automatic">Automatisk</string>
     <string name="pref_theme_light">Ljus</string>
     <string name="pref_theme_dark">Mörk</string>
     <string name="unable_to_connect_to_keychain">Det gick inte att ansluta till OpenKeychain</string>
-    <string name="this_device_is_no_longer_in_use">Denna enhet används inte längre</string>
+    <string name="this_device_is_no_longer_in_use">Den här enheten används inte längre</string>
     <string name="type_pc">Dator</string>
     <string name="type_phone">Mobiltelefon</string>
     <string name="type_tablet">Surfplatta</string>
     <string name="type_web">Webbläsare</string>
     <string name="type_console">Konsoll</string>
     <string name="payment_required">Betalning krävs</string>
-    <string name="missing_internet_permission">Ge behörighet till att använda Internet</string>
-    <string name="me">Jag</string>
-    <string name="contact_asks_for_presence_subscription">Kontakt ber om tillgänglighetsuppdateringar</string>
+    <string name="missing_internet_permission">Bevilja behörighet att använda Internet</string>
+    <string name="me">Mig</string>
+    <string name="contact_asks_for_presence_subscription">Kontakt ber om närvaroprenumeration</string>
     <string name="allow">Tillåt</string>
-    <string name="no_permission_to_access_x">Saknar rättigheter för access till %s</string>
-    <string name="remote_server_not_found">Fjärrserver hittas inte</string>
+    <string name="no_permission_to_access_x">Ingen behörighet för att komma åt %s</string>
+    <string name="remote_server_not_found">Fjärrserver hittades inte</string>
     <string name="remote_server_timeout">Timeout för fjärrserver</string>
-    <string name="unable_to_update_account">Kunde inte uppdatera konto</string>
+    <string name="unable_to_update_account">Kunde inte uppdatera kontot</string>
     <string name="report_jid_as_spammer">Rapportera den här XMPP-adressen för spam.</string>
-    <string name="pref_delete_omemo_identities">Ta bort OMEMO identiteter</string>
-    <string name="pref_delete_omemo_identities_summary">Återskapa dina OMEMO-nycklar. Alla dina kontakter måste verifiera dig igen. Använd endast det här som en sista utväg.</string>
+    <string name="pref_delete_omemo_identities">Ta bort OMEMO-identiteter</string>
+    <string name="pref_delete_omemo_identities_summary">Regenerera dina OMEMO-nycklar. Alla dina kontakter måste verifiera dig igen. Använd bara det här som en sista utväg.</string>
     <string name="delete_selected_keys">Ta bort valda nycklar</string>
-    <string name="error_publish_avatar_offline">Du måste vara ansluten för att publicera din avatarbild.</string>
+    <string name="error_publish_avatar_offline">Du behöver vara ansluten för att publicera din visningsbild.</string>
     <string name="show_error_message">Visa felmeddelande</string>
     <string name="error_message">Felmeddelande</string>
-    <string name="data_saver_enabled">Databesparing</string>
-    <string name="data_saver_enabled_explained">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.</string>
-    <string name="device_does_not_support_data_saver">Din enhet stöder inte inaktivering av databesparing för %1$s.</string>
+    <string name="data_saver_enabled">Databesparing aktiverad</string>
+    <string name="data_saver_enabled_explained">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.</string>
+    <string name="device_does_not_support_data_saver">Din enhet har inte stöd för att inaktivera datasparläget för %1$s.</string>
     <string name="error_unable_to_create_temporary_file">Det gick inte att skapa en tillfällig fil</string>
-    <string name="this_device_has_been_verified">Denna enhet har verifierats</string>
+    <string name="this_device_has_been_verified">Den här enheten har verifierats</string>
     <string name="copy_fingerprint">Kopiera fingeravtryck</string>
     <string name="all_omemo_keys_have_been_verified">Du har verifierat alla OMEMO-nycklar i din ägo</string>
-    <string name="barcode_does_not_contain_fingerprints_for_this_conversation">Streckkoden innehåller inte fingeravtryck för denna konversation.</string>
+    <string name="barcode_does_not_contain_fingerprints_for_this_conversation">Streckkoden innehåller inga fingeravtryck för den här konversationen.</string>
     <string name="verified_fingerprints">Verifierade fingeravtryck</string>
-    <string name="use_camera_icon_to_scan_barcode">Använd kameran för att scanna en kontakts streckkod</string>
-    <string name="please_wait_for_keys_to_be_fetched">Vänta medans nycklar hämtas</string>
+    <string name="use_camera_icon_to_scan_barcode">Använd kameran för att skanna en kontakts streckkod</string>
+    <string name="please_wait_for_keys_to_be_fetched">Var god vänta på att nycklarna ska hämtas</string>
     <string name="share_as_barcode">Dela som streckkod</string>
-    <string name="share_as_uri">Dela som XMPP URI</string>
-    <string name="share_as_http">Dela som HTTP länk</string>
-    <string name="pref_blind_trust_before_verification">Blint förtroende före verifiering</string>
+    <string name="share_as_uri">Dela som XMPP-URI</string>
+    <string name="share_as_http">Dela som HTTP-länk</string>
+    <string name="pref_blind_trust_before_verification">Blind tillit före verifiering</string>
     <string name="pref_blind_trust_before_verification_summary">Lita på nya enheter från icke-verifierade kontakter, men begär manuell bekräftelse av nya enheter för verifierade kontakter.</string>
-    <string name="blindly_trusted_omemo_keys">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.</string>
+    <string name="blindly_trusted_omemo_keys">Att blint lita på OMEMO-nycklar, innebär att användaren skulle kunna vara någon annan, och att en tredje part kan tjuvlyssna.</string>
     <string name="not_trusted">Ej betrodd</string>
     <string name="invalid_barcode">Ogiltig 2D-streckkod</string>
-    <string name="pref_clean_cache_summary">Töm cache-mapp (används av kameraapplikationen)</string>
+    <string name="pref_clean_cache_summary">Töm cache-mapp (används av kameraappen)</string>
     <string name="pref_clean_cache">Rensa cache</string>
-    <string name="pref_clean_private_storage">Rensa private lagring</string>
-    <string name="pref_clean_private_storage_summary">Rensa privat lagring där filer lagras (De kan om-laddas från servern)</string>
-    <string name="i_followed_this_link_from_a_trusted_source">Jag följde denna länk från en trovärdig källa</string>
-    <string name="verifying_omemo_keys_trusted_source">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.</string>
-    <string name="verifying_omemo_keys_trusted_source_account">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.</string>
+    <string name="pref_clean_private_storage">Rensa privat lagring</string>
+    <string name="pref_clean_private_storage_summary">Rensa privat lagring där filer förvaras (De kan laddas ner på nytt från servern)</string>
+    <string name="i_followed_this_link_from_a_trusted_source">Jag följde den här länken från en trovärdig källa</string>
+    <string name="verifying_omemo_keys_trusted_source">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.</string>
+    <string name="verifying_omemo_keys_trusted_source_account">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.</string>
     <string name="continue_btn">Fortsätt</string>
     <string name="verify_omemo_keys">Verifiera OMEMO-nycklar</string>
     <string name="show_inactive_devices">Visa inaktiva</string>
     <string name="hide_inactive_devices">Dölj inaktiva</string>
-    <string name="distrust_omemo_key">Lita ej på enhet</string>
-    <string name="distrust_omemo_key_text">Ä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\".</string>
+    <string name="distrust_omemo_key">Misstro enhet</string>
+    <string name="distrust_omemo_key_text">Ä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\".</string>
     <plurals name="seconds">
         <item quantity="one">%d sekund</item>
         <item quantity="other">%d sekunder</item>
@@ -650,40 +670,40 @@
         <item quantity="other">%d månader</item>
     </plurals>
     <string name="pref_automatically_delete_messages">Automatisk borttagning av meddelanden</string>
-    <string name="pref_automatically_delete_messages_description">Ta automatiskt bort meddelanden från denna enhet som är äldre än den konfigurerade tidsramen.</string>
+    <string name="pref_automatically_delete_messages_description">Ta automatiskt bort meddelanden från den här enheten som är äldre än den angivna tidsramen.</string>
     <string name="encrypting_message">Krypterar meddelande</string>
-    <string name="not_fetching_history_retention_period">Hämtar inte meddelanden på grund av inställningen för borttagning av gamla meddelanden.</string>
+    <string name="not_fetching_history_retention_period">Hämtar inte meddelanden på grund av lokal lagringsperiod.</string>
     <string name="transcoding_video">Komprimerar video</string>
-    <string name="corresponding_conversations_closed">Korresponderande konversationer är stängda.</string>
+    <string name="corresponding_conversations_closed">Motsvarande konversationer avslutades.</string>
     <string name="contact_blocked_past_tense">Kontakt blockerad.</string>
-    <string name="pref_notifications_from_strangers">Notifieringar från främlingar</string>
-    <string name="pref_notifications_from_strangers_summary">Meddela för meddelanden och samtal från främlingar.</string>
+    <string name="pref_notifications_from_strangers">Aviseringar från främlingar</string>
+    <string name="pref_notifications_from_strangers_summary">Motta aviseringar för meddelanden och samtal från främlingar.</string>
     <string name="received_message_from_stranger">Mottagna meddelanden från främlingar</string>
     <string name="block_stranger">Blockera främling</string>
-    <string name="block_entire_domain">Blockera hel domän</string>
-    <string name="online_right_now">online just nu</string>
-    <string name="retry_decryption">Försök dekryptera igen</string>
-    <string name="session_failure">Sessionsfel</string>
+    <string name="block_entire_domain">Blockera hela domänen</string>
+    <string name="online_right_now">uppkopplad just nu</string>
+    <string name="retry_decryption">Försök att dekryptera igen</string>
+    <string name="session_failure">Session misslyckades</string>
     <string name="sasl_downgrade">Nedgraderad SASL-mekanism</string>
-    <string name="account_status_regis_web">Servern kräver registrering via webbplatsen</string>
+    <string name="account_status_regis_web">Servern kräver registrering på webbplatsen</string>
     <string name="open_website">Öppna webbsida</string>
-    <string name="application_found_to_open_website">Ingen applikation hittades för att kunna öppna webbsidan</string>
-    <string name="pref_headsup_notifications">Se upp-notifikationer</string>
-    <string name="pref_headsup_notifications_summary">Visa se upp-notifikationer</string>
+    <string name="application_found_to_open_website">Ingen app hittades för att kunna öppna webbplatsen</string>
+    <string name="pref_headsup_notifications">\"Heads-up\"-meddelanden</string>
+    <string name="pref_headsup_notifications_summary">Visa \"Heads-up\"-aviseringar</string>
     <string name="today">Idag</string>
     <string name="yesterday">Igår</string>
-    <string name="pref_validate_hostname">Bekräfta värdnamn med DNSSEC</string>
+    <string name="pref_validate_hostname">Validera värdnamn med DNSSEC</string>
     <string name="pref_validate_hostname_summary">Servercertifikat som innehåller det validerade värdnamnet anses vara verifierade</string>
-    <string name="certificate_does_not_contain_jid">Certifikatet innehåller ej en XMPP-adress</string>
+    <string name="certificate_does_not_contain_jid">Certifikatet innehåller ingen XMPP-adress</string>
     <string name="server_info_partial">delvis</string>
     <string name="attach_record_video">Spela in video</string>
     <string name="copy_to_clipboard">Kopiera till urklipp</string>
-    <string name="message_copied_to_clipboard">Meddelande kopierat till urklipp</string>
+    <string name="message_copied_to_clipboard">Meddelandet har kopierats till urklipp</string>
     <string name="message">Meddelande</string>
     <string name="private_messages_are_disabled">Privata meddelanden är inaktiverade</string>
-    <string name="huawei_protected_apps">Skyddade applikationer</string>
-    <string name="huawei_protected_apps_summary">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.</string>
-    <string name="mtm_accept_cert">Godkänn okänt certifikat?</string>
+    <string name="huawei_protected_apps">Skyddade appar</string>
+    <string name="huawei_protected_apps_summary">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.</string>
+    <string name="mtm_accept_cert">Acceptera okänt certifikat\?</string>
     <string name="mtm_trust_anchor">Servercertifikatet är inte signerat av en känd certifikatutfärdare.</string>
     <string name="mtm_accept_servername">Acceptera servernamn som inte matchar?</string>
     <string name="mtm_hostname_mismatch">Servern kunde inte autentisera som \"%s\". Certifikatet är endast giltigt för:</string>
@@ -693,14 +713,15 @@
     <string name="qr_code_scanner_needs_access_to_camera">QR-läsaren behöver åtkomst till kameran</string>
     <string name="pref_scroll_to_bottom">Bläddra till botten</string>
     <string name="pref_scroll_to_bottom_summary">Bläddra ner efter att du har skickat ett meddelande</string>
-    <string name="edit_status_message_title">Redigera Statusmeddelande</string>
+    <string name="edit_status_message_title">Redigera statusmeddelande</string>
     <string name="edit_status_message">Redigera statusmeddelande</string>
     <string name="disable_encryption">Inaktivera kryptering</string>
-    <string name="error_trustkey_general">%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.</string>
+    <string name="error_trustkey_general">%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.</string>
     <string name="error_trustkey_device_list">Det gick inte att hämta enhetslistan</string>
     <string name="error_trustkey_bundle">Det gick inte att hämta krypteringsnycklar</string>
-    <string name="error_trustkey_hint_mutual">Tips: I vissa fall kan detta åtgärdas genom att lägga till varandra i era respektive kontaktlistor.</string>
-    <string name="disable_encryption_message">Ä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.</string>
+    <string name="error_trustkey_hint_mutual">Tips: I vissa fall kan det här åtgärdas genom att lägga till varandra i era respektive kontaktlistor.</string>
+    <string name="disable_encryption_message">Ä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.</string>
     <string name="disable_now">Inaktivera nu</string>
     <string name="draft">Utkast:</string>
     <string name="pref_omemo_setting">OMEMO-kryptering</string>
@@ -713,10 +734,10 @@
     <string name="default_on">På som standard</string>
     <string name="default_off">Av som standard</string>
     <string name="small">Liten</string>
-    <string name="medium">Mellan</string>
+    <string name="medium">Medium</string>
     <string name="large">Stor</string>
-    <string name="not_encrypted_for_this_device">Meddelandet är inte krypterat för den här enheten.</string>
-    <string name="omemo_decryption_failed">Misslyckades med att dekryptera OMEMO-meddelandet.</string>
+    <string name="not_encrypted_for_this_device">Meddelandet krypterades inte för den här enheten.</string>
+    <string name="omemo_decryption_failed">Det gick inte att dekryptera OMEMO-meddelandet.</string>
     <string name="undo">ångra</string>
     <string name="location_disabled">Platsdelning är inaktiverat</string>
     <string name="action_fix_to_location">Lås position</string>
@@ -728,9 +749,9 @@
     <string name="title_activity_show_location">Visa plats</string>
     <string name="share">Dela</string>
     <string name="unable_to_start_recording">Det gick inte att starta inspelningen</string>
-    <string name="please_wait">Var god dröj …</string>
-    <string name="no_microphone_permission">Ge %1$s tillgång till mikrofonen</string>
-    <string name="search_messages">Söka i meddelanden</string>
+    <string name="please_wait">Var god dröj…</string>
+    <string name="no_microphone_permission">Bevilja %1$s tillgång till mikrofonen</string>
+    <string name="search_messages">Sök meddelanden</string>
     <string name="gif">GIF</string>
     <string name="view_conversation">Visa konversation</string>
     <string name="pref_use_share_location_plugin">Dela plats-tillägget</string>
@@ -738,16 +759,16 @@
     <string name="copy_jabber_id">Kopiera XMPP-adress</string>
     <string name="p1_s3_filetransfer">HTTP-fildelning för S3</string>
     <string name="pref_start_search">Direktsök</string>
-    <string name="group_chat_avatar">Gruppkonversationens visningsbild</string>
-    <string name="host_does_not_support_group_chat_avatars">Värden stöder inte visningsbilder för gruppkonversationer</string>
-    <string name="only_the_owner_can_change_group_chat_avatar">Endast ägaren kan ändra visningsbilden för gruppkonversationen</string>
+    <string name="group_chat_avatar">Gruppchattens visningsbild</string>
+    <string name="host_does_not_support_group_chat_avatars">Värden stöder inte visningsbilder för gruppchattar</string>
+    <string name="only_the_owner_can_change_group_chat_avatar">Endast ägaren kan ändra visningsbilden för gruppchatten</string>
     <string name="contact_name">Kontaktnamn</string>
     <string name="nickname">Smeknamn</string>
     <string name="group_chat_name">Namn</string>
     <string name="providing_a_name_is_optional">Att ange ett namn är valfritt</string>
     <string name="create_dialog_group_chat_name">Gruppchattens namn</string>
-    <string name="unable_to_save_recording">Kunde inte att spara inspelningen</string>
-    <string name="foreground_service_channel_name">Förgrundsservice</string>
+    <string name="unable_to_save_recording">Det gick inte att spara inspelningen</string>
+    <string name="foreground_service_channel_name">Förgrundstjänst</string>
     <string name="notification_group_status_information">Statusinformation</string>
     <string name="error_channel_name">Anslutningsproblem</string>
     <string name="notification_group_messages">Meddelanden</string>

src/main/res/values-uk/strings.xml 🔗

@@ -28,8 +28,8 @@
     <string name="just_now">щойно</string>
     <string name="minute_ago">1 хвилину тому</string>
     <string name="minutes_ago">%d хвилин тому</string>
-    <string name="sending">відправляю…</string>
-    <string name="message_decrypting">Розшифровую повідомлення. Зачекайте, будь ласка…</string>
+    <string name="sending">надсилання…</string>
+    <string name="message_decrypting">Повідомлення розшифровується. Зачекайте, будь ласка…</string>
     <string name="pgp_message">Повідомлення зашифроване OpenPGP</string>
     <string name="nick_in_use">Таке прізвисько вже використовується</string>
     <string name="invalid_muc_nick">Неприйнятне прізвисько</string>
@@ -68,13 +68,13 @@
     <string name="problem_connecting_to_account">Не вдалося з\'єднатися з обліковим записом</string>
     <string name="problem_connecting_to_accounts">Не вдалося з\'єднатися з обліковими записами</string>
     <string name="touch_to_fix">Торкніться, щоб керувати обліковими записами</string>
-    <string name="attach_file">Долучити файл</string>
+    <string name="attach_file">Прикріпити файл</string>
     <string name="not_in_roster">Цього контакту немає у Вашому списку. Бажаєте додати його\?</string>
     <string name="add_contact">Додати контакт</string>
     <string name="send_failed">не вдалося надіслати</string>
     <string name="preparing_image">Підготовка до передачі зображення</string>
     <string name="preparing_images">Підготовка до передачі зображень</string>
-    <string name="sharing_files_please_wait">Поширюю файли. Зачекайте, будь ласка…</string>
+    <string name="sharing_files_please_wait">Файли надсилаються. Зачекайте, будь ласка…</string>
     <string name="action_clear_history">Стерти історію</string>
     <string name="clear_conversation_history">Стерти історію розмов</string>
     <string name="clear_histor_msg">Дійсно вилучити всі повідомлення цієї розмови\?
@@ -99,8 +99,8 @@
     <string name="restart">Перезапустити</string>
     <string name="install">Встановити</string>
     <string name="openkeychain_not_installed">Будь ласка, встановіть OpenKeychain</string>
-    <string name="offering">пропоную…</string>
-    <string name="waiting">чекаю…</string>
+    <string name="offering">пропонування…</string>
+    <string name="waiting">очікування…</string>
     <string name="no_pgp_key">Не знайдено ключа OpenPGP</string>
     <string name="contact_has_no_pgp_key">Не вдалося зашифрувати повідомлення, оскільки контакт не повідомляє свій публічний ключ.
 \n
@@ -125,7 +125,7 @@
     <string name="pref_notification_grace_period_summary">Час, протягом якого сповіщення вимкнені після виявлення активності на іншому пристрої.</string>
     <string name="pref_advanced_options">Розширені</string>
     <string name="pref_never_send_crash">Не надсилати звіти про збої</string>
-    <string name="pref_never_send_crash_summary">Надсилаючи траси стеку викликів, Ви допомагаєте розробляти застосунок</string>
+    <string name="pref_never_send_crash_summary">Надсилаючи звіти про збої, Ви допомагаєте розвивати застосунок</string>
     <string name="pref_confirm_messages">Підтвердження отримання повідомлень</string>
     <string name="pref_confirm_messages_summary">Повідомляти співрозмовників, що Ви отримали й прочитали їхні повідомлення</string>
     <string name="pref_ui_options">Інтерфейс користувача</string>
@@ -216,7 +216,7 @@
     <string name="omemo_fingerprint_x509">Цифровий підпис v\\OMEMO</string>
     <string name="other_devices">Інші пристрої</string>
     <string name="trust_omemo_fingerprints">Довіряти цифровим підписам OMEMO</string>
-    <string name="fetching_keys">Отримую ключі…</string>
+    <string name="fetching_keys">Отримання ключів…</string>
     <string name="done">Зроблено</string>
     <string name="decrypt">Розшифрувати</string>
     <string name="search">Пошук</string>
@@ -286,7 +286,7 @@
     <string name="pref_allow_message_correction_summary">Дозволити контактам редагувати свої повідомлення після надсилання</string>
     <string name="pref_expert_options">Експертні налаштування</string>
     <string name="pref_expert_options_summary">Будь ласка, будьте обережними з цим</string>
-    <string name="title_activity_about_x">Близько %s</string>
+    <string name="title_activity_about_x">Про %s</string>
     <string name="title_pref_quiet_hours">Години тиші</string>
     <string name="title_pref_quiet_hours_start_time">Час початку</string>
     <string name="title_pref_quiet_hours_end_time">Час завершення</string>
@@ -363,9 +363,9 @@
     <string name="error_no_keys_to_trust_presence">Для цього контакту відсутні потрібні ключі.
 \nПеревірте, що ви обмінялися інформацією про присутність.</string>
     <string name="error_trustkeys_title">Щось пішло не так</string>
-    <string name="fetching_history_from_server">Отримую історію з сервера</string>
+    <string name="fetching_history_from_server">Отримання історії з сервера</string>
     <string name="no_more_history_on_server">Більше немає історії на сервері</string>
-    <string name="updating">Оновлюю…</string>
+    <string name="updating">Оновлення…</string>
     <string name="password_changed">Пароль змінено!</string>
     <string name="could_not_change_password">Не вдалося змінити пароль</string>
     <string name="change_password">Змінити пароль</string>
@@ -415,7 +415,7 @@
     <string name="audio">аудіо</string>
     <string name="video">відео</string>
     <string name="image">зображення</string>
-    <string name="pdf_document">PDF документ</string>
+    <string name="pdf_document">документ PDF</string>
     <string name="apk">Програма Android</string>
     <string name="vcard">Контакт</string>
     <string name="avatar_has_been_published">Піктограму користувача опубліковано!</string>
@@ -477,7 +477,7 @@
     <string name="unable_to_parse_certificate">Не вдалося розпізнати сертифікат</string>
     <string name="mam_prefs">Налаштування збереження</string>
     <string name="server_side_mam_prefs">Налаштування збереження на стороні сервера</string>
-    <string name="fetching_mam_prefs">Отримую налаштування збереженя. Будь ласка, зачекайте…</string>
+    <string name="fetching_mam_prefs">Отримання налаштувань збереження. Будь ласка, зачекайте…</string>
     <string name="unable_to_fetch_mam_prefs">Не вдалося отримати налаштування збереження</string>
     <string name="captcha_required">Потрібно розв\'язати головоломку</string>
     <string name="captcha_hint">Уведіть текст із зображення вище</string>
@@ -523,7 +523,7 @@
     <string name="this_field_is_required">Це обов\'язкове поле</string>
     <string name="correct_message">Відредагувати</string>
     <string name="send_corrected_message">Відредаговане повідомлення</string>
-    <string name="no_keys_just_confirm">Ви вже довіряєте цій особі. Вибираючи «Зроблено», Ви лише підтверджуєте, що %s є учасником групи.</string>
+    <string name="no_keys_just_confirm">Ви вже довіряєте цифровому підпису цієї особи. Вибираючи «Зроблено», Ви лише підтверджуєте, що %s є учасником групи.</string>
     <string name="this_account_is_disabled">Ви вимкнули цей обліковий запис</string>
     <string name="security_error_invalid_file_access">Помилка безпеки: неправильний доступ до файлу!</string>
     <string name="no_application_to_share_uri">Не знайдено застосунку, щоб поділитися URI</string>
@@ -723,7 +723,7 @@
     <string name="action_unfix_from_location">Відкріпити розташування</string>
     <string name="action_copy_location">Скопіювати місцезнаходження</string>
     <string name="action_share_location">Поділитися місцезнаходженням</string>
-    <string name="action_directions">Шлях</string>
+    <string name="action_directions">Напрямки</string>
     <string name="title_activity_share_location">Поділитися місцезнаходженням</string>
     <string name="title_activity_show_location">Показати місцезнаходження</string>
     <string name="share">Поділитися</string>
@@ -865,7 +865,7 @@
     <string name="manage_permission">Керувати правами</string>
     <string name="search_participants">Шукати учасників</string>
     <string name="file_too_large">Файл надто великий</string>
-    <string name="attach">Долучити</string>
+    <string name="attach">Прикріпити</string>
     <string name="discover_channels">Пошук каналу</string>
     <string name="search_channels">Шукати канали</string>
     <string name="channel_discovery_opt_in_title">Можливе порушення приватності!</string>
@@ -938,7 +938,7 @@
     </plurals>
     <string name="action_end_conversation">Закрити розмову</string>
     <string name="crash_report_title">Збій %1$s</string>
-    <string name="crash_report_message">Надсилаючи зі свого облікового запису XMPP траси стеку викликів, Ви допомагаєте розробляти %1$s.</string>
+    <string name="crash_report_message">Надсилаючи зі свого облікового запису XMPP звіти про збої, Ви допомагаєте розвивати %1$s.</string>
     <string name="pref_call_ringtone_summary">Мелодія для вхідних викликів</string>
     <string name="pref_prevent_screenshots">Заборонити знімки екрана</string>
     <string name="pref_prevent_screenshots_summary">Ховати вміст застосунку при перемиканні програм і заборонити знімки екрана</string>
@@ -992,7 +992,7 @@
     <string name="sync_with_contacts_long">%1$s потребує дозволу на доступ до контактів, щоб порівняти їх з Вашими XMPP-контактами.
 \nТаким чином можна буде показувати піктограми і повні імена користувачів.
 \n
-\n%1$s співставлення інформації про контакти відбувається локально, нічого не завантажується на сервер.</string>
+\n%1$s лише прочитає Вашу адресну книгу і зіставлятиме інформацію про контакти локально, нічого не завантажуючи на сервер.</string>
     <string name="missed_calls_channel_name">Пропущені виклики</string>
     <string name="data_saver_enabled_explained">Ваша операційна система обмежує для %1$s доступ до Інтернету у фоновому режимі. Щоб отримувати сповіщення про нові повідомлення, Вам потрібно дозволити %1$s необмежений доступ, коли заощадження трафіку увімкнено.
 \n%1$s намагатиметься по можливості економити трафік.</string>
@@ -1065,4 +1065,13 @@
     <string name="outdated_backup_file_format">Ви намагаєтеся імпортувати файл резервної копії у застарілому форматі</string>
     <string name="audiobook">Аудіокнига</string>
     <string name="reconnect_on_other_host">Відновити з\'єднання на іншому вузлі</string>
+    <string name="this_account_is_logged_out">Ви вийшли з цього облікового запису</string>
+    <string name="log_in">Увійти</string>
+    <string name="hide_notification">Сховати сповіщення</string>
+    <string name="contact_uses_unverified_keys">Ваш контакт використовує неперевірені пристрої. Відскануйте їх QR-код, щоб виконати перевірку та запобігти активним атакам MITM.</string>
+    <string name="log_out">Вийти</string>
+    <string name="unverified_devices">Ви використовуєте неперевірені пристрої. Відскануйте QR-код на інших своїх пристроях, щоб виконати перевірку та запобігти активним атакам MITM.</string>
+    <string name="account_state_logged_out">Ви вийшли</string>
+    <string name="report_spam_and_block">Повідомити про спам і заблокувати спамера</string>
+    <string name="report_spam">Повідомити про спам</string>
 </resources>

src/main/res/values-zh-rCN/strings.xml 🔗

@@ -73,7 +73,7 @@
     <string name="send_never">不再询问</string>
     <string name="problem_connecting_to_account">无法连接到账号</string>
     <string name="problem_connecting_to_accounts">无法连接到多个账号</string>
-    <string name="touch_to_fix">点击即可管理账号</string>
+    <string name="touch_to_fix">轻击即可管理账号</string>
     <string name="attach_file">附加文件</string>
     <string name="not_in_roster">对方不在您的联系人列表中,是否添加?</string>
     <string name="add_contact">添加联系人</string>
@@ -109,11 +109,11 @@
     <string name="offering">正在提供…</string>
     <string name="waiting">正在等待…</string>
     <string name="no_pgp_key">未找到 OpenPGP 密钥</string>
-    <string name="contact_has_no_pgp_key">无法加密消息,因为对方未公布其公钥。
+    <string name="contact_has_no_pgp_key">无法加密消息,因为您的联系人未公布其公钥。
 \n
 \n<small>请通知对方设置 OpenPGP。</small></string>
     <string name="no_pgp_keys">未找到 OpenPGP 密钥</string>
-    <string name="contacts_have_no_pgp_keys">无法加密消息,因为对方未公布其公钥。
+    <string name="contacts_have_no_pgp_keys">无法加密消息,因为您的联系人未公布其公钥。
 \n
 \n<small>请通知对方设置 OpenPGP。</small></string>
     <string name="pref_general">常规</string>
@@ -189,7 +189,7 @@
     <string name="mgmt_account_publish_pgp">发布 OpenPGP 公钥</string>
     <string name="unpublish_pgp">移除 OpenPGP 公钥</string>
     <string name="unpublish_pgp_message">是否确定要从在线状态公布中移除 OpenPGP 公钥?
-\n对方将无法再向您发送 OpenPGP 加密消息。</string>
+\n您的联系人将无法再向您发送 OpenPGP 加密消息。</string>
     <string name="openpgp_has_been_published">OpenPGP 公钥已发布。</string>
     <string name="mgmt_account_enable">启用账号</string>
     <string name="mgmt_account_delete_confirm_text">是否确定要删除账号?删除账号会擦除全部对话历史记录</string>
@@ -205,11 +205,11 @@
     <string name="server_info_mam">XEP-0313:消息存档管理</string>
     <string name="server_info_carbon_messages">XEP-0280:消息抄送</string>
     <string name="server_info_csi">XEP-0352:客户端状态指示</string>
-    <string name="server_info_blocking">XEP-0191:屏蔽指令</string>
+    <string name="server_info_blocking">XEP-0191:屏蔽命令</string>
     <string name="server_info_roster_version">XEP-0237:花名册版本控制</string>
     <string name="server_info_stream_management">XEP-0198:流管理</string>
     <string name="server_info_external_service_discovery">XEP-0215:外部服务发现</string>
-    <string name="server_info_pep">XEP-0163:个人事件协议(头像/OMEMO)</string>
+    <string name="server_info_pep">XEP-0163:个人事件协议 (头像/OMEMO)</string>
     <string name="server_info_http_upload">XEP-0363:HTTP 文件上传</string>
     <string name="server_info_push">XEP-0357:推送</string>
     <string name="server_info_available">有效</string>
@@ -227,8 +227,8 @@
     <string name="openpgp_key_id">OpenPGP 密钥 ID</string>
     <string name="omemo_fingerprint">OMEMO 指纹</string>
     <string name="omemo_fingerprint_x509">v\\OMEMO 指纹</string>
-    <string name="omemo_fingerprint_selected_message">OMEMO 指纹(消息来源)</string>
-    <string name="omemo_fingerprint_x509_selected_message">v\\OMEMO 指纹(消息来源)</string>
+    <string name="omemo_fingerprint_selected_message">OMEMO 指纹 (消息来源)</string>
+    <string name="omemo_fingerprint_x509_selected_message">v\\OMEMO 指纹 (消息来源)</string>
     <string name="other_devices">其他设备</string>
     <string name="trust_omemo_fingerprints">信任 OMEMO 指纹</string>
     <string name="fetching_keys">正在获取密钥…</string>
@@ -269,7 +269,7 @@
     <string name="contacts_and_n_more_have_read_up_to_this_point">%1$s 和其他 %2$d 人已阅读至此</string>
     <string name="everyone_has_read_up_to_this_point">每个人都已阅读至此</string>
     <string name="publish">发布</string>
-    <string name="touch_to_choose_picture">点击头像即可从图库中选择图片</string>
+    <string name="touch_to_choose_picture">轻击头像即可从图库中选择图片</string>
     <string name="publishing">正在发布…</string>
     <string name="error_publish_avatar_server_reject">服务器拒绝了您的发布</string>
     <string name="error_publish_avatar_converting">无法转换图片</string>
@@ -288,9 +288,9 @@
     <string name="enable">启用</string>
     <string name="conference_requires_password">需要密码才能进入此群聊</string>
     <string name="enter_password">输入密码</string>
-    <string name="request_presence_updates">请先向对方请求在线状态更新。
+    <string name="request_presence_updates">请先向您的联系人请求在线状态更新。
 \n
-\n<small>这将用于确定对方正在使用的聊天应用</small>。</string>
+\n<small>将用于确定对方正在使用的聊天应用</small>。</string>
     <string name="request_now">立即请求</string>
     <string name="ignore">忽略</string>
     <string name="without_mutual_presence_updates"><b>警告:</b>在双方未更新在线状态的情况下发送此消息将会出现未知问题。
@@ -312,7 +312,7 @@
     <string name="pref_autojoin_summary">在进入或离开多用户聊天时设置“自动加入”标志,并回应其他客户端所做的改变。</string>
     <string name="toast_message_omemo_fingerprint">OMEMO 指纹已复制到剪贴板</string>
     <string name="conference_banned">此群聊已将您封禁了</string>
-    <string name="conference_members_only">此群聊仅限成员参与</string>
+    <string name="conference_members_only">此群聊仅成员进入</string>
     <string name="conference_resource_constraint">资源限制</string>
     <string name="conference_kicked">此群聊已将您踢出了</string>
     <string name="conference_shutdown">此群聊已关闭</string>
@@ -376,9 +376,9 @@
     <string name="regenerate_omemo_key">重新生成 OMEMO 密钥</string>
     <string name="clear_other_devices">清除设备</string>
     <string name="clear_other_devices_desc">是否确定要从 OMEMO 公布中清除所有其他设备?下次连接时,设备将会重新公布,但可能不会收到在此期间发送的消息。</string>
-    <string name="error_no_keys_to_trust_server_error">对方没有可用的密钥。
+    <string name="error_no_keys_to_trust_server_error">此联系人没有可用的密钥。
 \n无法从服务器获取新密钥。也许是对方的服务器出了问题?</string>
-    <string name="error_no_keys_to_trust_presence">对方没有可用的密钥。
+    <string name="error_no_keys_to_trust_presence">此联系人没有可用的密钥。
 \n请确保双方都有在线状态订阅。</string>
     <string name="error_trustkeys_title">出了点问题</string>
     <string name="fetching_history_from_server">正在从服务器获取历史记录</string>
@@ -414,7 +414,7 @@
     <string name="could_not_change_role">无法更改 %s 的角色</string>
     <string name="conference_options">私人群聊配置</string>
     <string name="channel_options">公开频道配置</string>
-    <string name="members_only">私人,仅限成员</string>
+    <string name="members_only">私人,仅成员进入</string>
     <string name="non_anonymous">使 XMPP 地址对任何人可见</string>
     <string name="moderated">对频道进行审核</string>
     <string name="you_are_not_participating">您未参与</string>
@@ -531,9 +531,9 @@
     <string name="no_camera_permission">授予 %1$s 访问相机的权限</string>
     <string name="sync_with_contacts">与联系人同步</string>
     <string name="sync_with_contacts_long">%1$s 想要访问通讯录权限来将它与 XMPP 联系人列表相匹配。
-\n这将会显示联系人的全名和头像。
+\n将会显示联系人的全名和头像。
 \n
-\n%1$s 将只会读取通讯录并在本地匹配,不会上传任何东西到服务器。</string>
+\n%1$s 将只会读取通讯录并在本地匹配,不会上传任何内容到服务器。</string>
     <string name="notify_on_all_messages">通知所有消息</string>
     <string name="notify_only_when_highlighted">仅在提及时通知</string>
     <string name="notify_never">通知已禁用</string>
@@ -543,9 +543,9 @@
     <string name="always">始终</string>
     <string name="large_images_only">仅限大图片</string>
     <string name="battery_optimizations_enabled">电池优化已启用</string>
-    <string name="battery_optimizations_enabled_explained">设备正对 %1$s 实施强力电池优化,这可能会导致通知延迟甚至消息丢失。
+    <string name="battery_optimizations_enabled_explained">设备正对 %1$s 实施强力电池优化,可能会导致通知延迟甚至消息丢失。
 \n建议禁用它们。</string>
-    <string name="battery_optimizations_enabled_dialog">设备正对 %1$s 实施强力电池优化,这可能会导致通知延迟甚至消息丢失。
+    <string name="battery_optimizations_enabled_dialog">设备正对 %1$s 实施强力电池优化,可能会导致通知延迟甚至消息丢失。
 \n
 \n现在将要求您禁用它们。</string>
     <string name="disable">禁用</string>
@@ -554,7 +554,7 @@
     <string name="this_field_is_required">此字段是必需的</string>
     <string name="correct_message">更正消息</string>
     <string name="send_corrected_message">发送更正后的消息</string>
-    <string name="no_keys_just_confirm">您已经安全地验证了此用户的指纹以确认信任。选择“完成”以确认 %s 加入群聊。</string>
+    <string name="no_keys_just_confirm">您已经验证了此用户的指纹。选择“完成”确认 %s 是此群聊的一员。</string>
     <string name="this_account_is_disabled">您已禁用了此账号</string>
     <string name="security_error_invalid_file_access">安全错误:文件访问无效!</string>
     <string name="no_application_to_share_uri">未找到可以分享 URI 的应用</string>
@@ -609,7 +609,7 @@
     <string name="remote_server_not_found">远程服务器未找到</string>
     <string name="remote_server_timeout">远程服务器超时</string>
     <string name="unable_to_update_account">无法更新账号</string>
-    <string name="report_jid_as_spammer">举报此 XMPP 地址发送垃圾消息。</string>
+    <string name="report_jid_as_spammer">报告此 XMPP 地址发送垃圾消息。</string>
     <string name="pref_delete_omemo_identities">删除 OMEMO 身份</string>
     <string name="pref_delete_omemo_identities_summary">重新生成 OMEMO 密钥。您的所有联系人将必须再次验证您。仅将此作为最后的方法。</string>
     <string name="delete_selected_keys">删除已选密钥</string>
@@ -636,12 +636,12 @@
     <string name="blindly_trusted_omemo_keys">盲目信任的 OMEMO 密钥,表示它们可能是其他人或者某人可能冒充别人发送消息。</string>
     <string name="not_trusted">不信任的</string>
     <string name="invalid_barcode">二维码无效</string>
-    <string name="pref_clean_cache_summary">清理缓存文件夹(由相机应用使用)</string>
+    <string name="pref_clean_cache_summary">清理缓存文件夹 (由相机应用使用)</string>
     <string name="pref_clean_cache">清理缓存</string>
     <string name="pref_clean_private_storage">清理私人存储空间</string>
-    <string name="pref_clean_private_storage_summary">清理保存文件的私人存储(它们可以从服务器重新下载)</string>
+    <string name="pref_clean_private_storage_summary">清理保存文件的私人存储 (它们可以从服务器重新下载)</string>
     <string name="i_followed_this_link_from_a_trusted_source">我从可信来源收到此链接</string>
-    <string name="verifying_omemo_keys_trusted_source">点击链接后,您将验证 %1$s 的 OMEMO 密钥。只有从可信来源(只有 %2$s 可以发布此链接)收到此链接才是安全的。</string>
+    <string name="verifying_omemo_keys_trusted_source">单击链接后,您将验证 %1$s 的 OMEMO 密钥。只有从可信来源(只有 %2$s 可以发布此链接)收到此链接才是安全的。</string>
     <string name="verifying_omemo_keys_trusted_source_account">您将验证自己账号的 OMEMO 密钥。只有从可信来源(只有您可以发布此链接)收到此链接才是安全的。</string>
     <string name="continue_btn">继续</string>
     <string name="verify_omemo_keys">验证 OMEMO 密钥</string>
@@ -705,7 +705,7 @@
     <string name="mtm_accept_cert">接受未知证书?</string>
     <string name="mtm_trust_anchor">此服务器证书不是由已知的证书颁发机构签发的。</string>
     <string name="mtm_accept_servername">接受不匹配的服务器名称?</string>
-    <string name="mtm_hostname_mismatch">由于\"%s\",服务器无法验证。证书仅对此有效:</string>
+    <string name="mtm_hostname_mismatch">由于“%s”,服务器无法验证。证书仅对此有效:</string>
     <string name="mtm_connect_anyway">是否仍要连接?</string>
     <string name="mtm_cert_details">证书详情:</string>
     <string name="once">仅一次</string>
@@ -715,12 +715,12 @@
     <string name="edit_status_message_title">编辑状态信息</string>
     <string name="edit_status_message">编辑状态信息</string>
     <string name="disable_encryption">禁用加密</string>
-    <string name="error_trustkey_general">%1$s 无法发送加密消息到 %2$s。这可能是由于对方使用了过时的服务器或者无法处理 OMEMO 的客户端。</string>
+    <string name="error_trustkey_general">%1$s 无法发送加密消息到 %2$s。可能是由于您的联系人使用了过时的服务器或者无法处理 OMEMO 的客户端。</string>
     <string name="error_trustkey_device_list">无法获取设备列表</string>
     <string name="error_trustkey_bundle">无法获取密钥</string>
     <string name="error_trustkey_hint_mutual">提示:某些情况下,双方可以添加到联系人列表解决此问题。</string>
     <string name="disable_encryption_message">是否确定要禁用此对话的 OMEMO 加密?
-\n这将允许服务器管理员读取您的消息,但这可能是与使用过时客户端的用户交流的唯一方法。</string>
+\n将允许服务器管理员读取您的消息,但可能是与使用过时客户端的用户交流的唯一方法。</string>
     <string name="disable_now">立即禁用</string>
     <string name="draft">草稿:</string>
     <string name="pref_omemo_setting">OMEMO 加密</string>
@@ -850,7 +850,7 @@
     <string name="no_market_app_installed">未安装应用商店。</string>
     <string name="group_chat_will_make_your_jabber_id_public">此频道将公开您的 XMPP 地址</string>
     <string name="ebook">电子书</string>
-    <string name="video_original">原始(未压缩)</string>
+    <string name="video_original">原始 (未压缩)</string>
     <string name="open_with">打开为…</string>
     <string name="set_profile_picture">Conversations 个人资料图片</string>
     <string name="choose_account">选择账号</string>
@@ -912,7 +912,7 @@
     <string name="group_chats_and_channels">群聊 &amp; 频道</string>
     <string name="jabber_network">jabber.network</string>
     <string name="local_server">本地服务器</string>
-    <string name="pref_channel_discovery_summary">大多数用户应该选择 \"jabber.network\" 以便从整个公共 XMPP 生态系统中获得更好的建议。</string>
+    <string name="pref_channel_discovery_summary">大多数用户应该选择“jabber.network”以便从整个公共 XMPP 生态系统中获得更好的建议。</string>
     <string name="pref_channel_discovery">频道发现方法</string>
     <string name="backup">备份</string>
     <string name="category_about">关于</string>
@@ -931,7 +931,7 @@
     <string name="dismiss_call">忽略</string>
     <string name="rtp_state_finding_device">正在发现设备</string>
     <string name="rtp_state_ringing">正在响铃</string>
-    <string name="rtp_state_declined_or_busy">正忙</string>
+    <string name="rtp_state_declined_or_busy">占线</string>
     <string name="rtp_state_connectivity_error">无法连接通话</string>
     <string name="rtp_state_connectivity_lost_error">连接已中断</string>
     <string name="rtp_state_retracted">已撤回通话</string>
@@ -1005,7 +1005,7 @@
     <string name="reject_switch_to_video">拒绝视频切换请求</string>
     <string name="pref_up_push_account_title">XMPP 账号</string>
     <string name="pref_up_push_server_title">推送服务器</string>
-    <string name="no_account_deactivated">无(已停用)</string>
+    <string name="no_account_deactivated">无 (已停用)</string>
     <string name="unified_push_distributor">UnifiedPush 分发程序</string>
     <string name="pref_up_push_account_summary">接收推送消息的账号。</string>
     <string name="pref_up_push_server_summary">用户选择的推送服务器,通过 XMPP 将推送消息转送至设备。</string>
@@ -1022,5 +1022,14 @@
     <string name="restore_warning_continued">请勿尝试恢复您尚未自行创建的备份!</string>
     <string name="outdated_backup_file_format">您正尝试导入过时的备份文件格式</string>
     <string name="audiobook">有声读物</string>
-    <string name="reconnect_on_other_host">重新连接其他主机</string>
+    <string name="reconnect_on_other_host">在其他主机上重新连接</string>
+    <string name="this_account_is_logged_out">您已登出此账号</string>
+    <string name="log_in">登入</string>
+    <string name="hide_notification">隐藏通知</string>
+    <string name="contact_uses_unverified_keys">您的联系人使用未经验证的设备。扫描对方二维码进行验证并阻止主动式中间人攻击。</string>
+    <string name="log_out">登出</string>
+    <string name="account_state_logged_out">已登出</string>
+    <string name="unverified_devices">您正在使用未经验证的设备。扫描您其他设备的二维码进行验证并阻止主动式中间人攻击。</string>
+    <string name="report_spam_and_block">报告垃圾消息并屏蔽垃圾消息发送者</string>
+    <string name="report_spam">报告垃圾消息</string>
 </resources>

src/main/res/values-zh-rTW/strings.xml 🔗

@@ -1001,7 +1001,7 @@
     <string name="pref_picture_compression_summary">提示:使用「選擇檔案」而非「選擇圖片」以傳送未壓縮的個別檔案,不論此設定為何。</string>
     <string name="battery_optimizations_enabled_explained">您的裝置正在為 %1$s 採用強力電池效能最佳化,這可能會導致通知延遲甚至訊息遺失。
 \n建議將其停用。</string>
-    <string name="no_keys_just_confirm">您已經安全地驗證了這個人的指紋以確認信任。透過選取「完成」以確認 %s 加入群組聊天。</string>
+    <string name="no_keys_just_confirm">您已經驗證了這個人的指紋。透過選取「完成」以確認 %s 加入群組聊天。</string>
     <string name="show_inactive_devices">顯示非作用中裝置</string>
     <string name="blindly_trusted_omemo_keys">盲目信任的 OMEMO 金鑰,說明它們可能對其他人或某人可能冒充別人傳送訊息。</string>
     <string name="pref_delete_omemo_identities_summary">重新產生 OMEMO 金鑰,您的所有聯絡人都需要再次驗證,如非必要請勿採取此步驟。</string>
@@ -1020,4 +1020,15 @@
     <string name="channel_discover_opt_in_message">頻道探索使用一個名為 &lt;a href=https://search.jabber.network&gt;search.jabber.network&lt;/a&gt; 的第三方服務,&lt;br&gt;&lt;br&gt;使用此功能會將您的 IP 位址和搜尋詞彙傳送至此服務。更多資訊請參見他們的 &lt;a href=https://search.jabber.network/privacy&gt;隱私權政策&lt;/a&gt;。</string>
     <string name="group_chats">群組聊天</string>
     <string name="save_as_group_chat">儲存為群組聊天</string>
+    <string name="this_account_is_logged_out">您已登出此帳戶</string>
+    <string name="log_in">登入</string>
+    <string name="hide_notification">隱藏通知</string>
+    <string name="reconnect_on_other_host">在其他主機上重新連接</string>
+    <string name="contact_uses_unverified_keys">您的聯絡人使用未驗證的設備。掃描他們的二維條碼進行驗證,以防止主動中間人攻擊。</string>
+    <string name="log_out">登出</string>
+    <string name="outdated_backup_file_format">您正在嘗試匯入一個過時的備份檔案格式</string>
+    <string name="account_state_logged_out">已登出</string>
+    <string name="unverified_devices">您正在使用未驗證的設備。掃描您其他設備上的二維條碼進行驗證,以防止主動中間人攻擊。</string>
+    <string name="audiobook">有聲書</string>
+    <string name="restore_warning_continued">請勿嘗試還原非您自己建立的備份!</string>
 </resources>

src/main/res/values/strings.xml 🔗

@@ -539,7 +539,7 @@
     <string name="correct_message">Correct message</string>
     <string name="retract_message">Retract message</string>
     <string name="send_corrected_message">Send corrected message</string>
-    <string name="no_keys_just_confirm">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.</string>
+    <string name="no_keys_just_confirm">You have already trusted this persons fingerprint. By selecting “Done” you are just confirming that %s is part of this group chat.</string>
     <string name="this_account_is_disabled">You have disabled this account</string>
     <string name="this_account_is_logged_out">You have logged out of this account</string>
     <string name="security_error_invalid_file_access">Security error: Invalid file access!</string>
@@ -1027,5 +1027,7 @@
     <string name="log_out">Log out</string>
     <string name="log_in">Log in</string>
     <string name="contact_uses_unverified_keys">Your contact uses unverified devices. Scan their 2D barcode to perform verification and impede active MITM attacks.</string>
-    <string name="unverified_devices">You are using unverified devices. Scan the 2D barcodes of your other devices to perform verification and impede active MITM attack.</string>
+    <string name="unverified_devices">You are using unverified devices. Scan the 2D barcode on your other devices to perform verification and impede active MITM attacks.</string>
+    <string name="report_spam">Report spam</string>
+    <string name="report_spam_and_block">Report spam and block spammer</string>
 </resources>

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),了解更多信息。

src/quicksy/res/values-uk/strings.xml 🔗

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <string name="pref_notification_grace_period_summary">Час, протягом якого застосунок дотримується тиші після виявлення активності на іншому пристрої</string>
-    <string name="pref_never_send_crash_summary">Надсилаючи траси стеку викликів, Ви допомагаєте розробляти Quicksy</string>
+    <string name="pref_never_send_crash_summary">Надсилаючи звіти про збої, Ви допомагаєте розвивати Quicksy</string>
     <string name="pref_broadcast_last_activity_summary">Повідомляти співрозмовникам, що Ви користуєтеся Quicksy</string>
     <string name="huawei_protected_apps_summary">Щоб отримувати сповіщення навіть коли екран погас, необхідно додати Quicksy до списку захищених програм.</string>
     <string name="set_profile_picture">Зображення профілю для Quicksy</string>