Detailed changes
@@ -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) {
@@ -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)
@@ -1 +1 @@
-* Support for ?register and ?register;preauth XMPP uri parameters
+* Soporte para os parámetros ?register e ?register;preauth da uri XMPP
@@ -1 +1 @@
-* Support automatic theme switching on Android 10
+* Soporte para o cambio automático de decorado en Android 10
@@ -1,2 +1,2 @@
-* Provide PDF preview on Android 5+
-* Use 12 byte IVs for OMEMO
+* Proporciona vista previa dos PDF en Android 5+
+* Usa 12 byte IVs con OMEMO
@@ -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
@@ -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)
@@ -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)
@@ -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
@@ -0,0 +1,3 @@
+* Soporte para Private DNS (DNS sobre TLS)
+* Soporte para decorar a icona no iniciador
+* Arranxo dun problema pouco común de permisos ao compartir ficheiros en Android 11+
@@ -0,0 +1,4 @@
+* 引入專家設置,以在本地伺服器上執行通道發現,而非 search.jabber.network
+* 默認啟用傳送檢查標記,並刪除相應設置
+* 默認啟用「發送按鈕顯示狀態」,並刪除相應設置
+* 將備份和前景服務設置移至主畫面
@@ -0,0 +1,3 @@
+* 修復 Jingle IBB 檔案傳輸問題
+* 修復重複更正填充數據庫的問題
+* 切換至 Last Message Correction 版本 1.1
@@ -0,0 +1,4 @@
+* 允許用戶設置自己的暱稱
+* 恢復下載 OMEMO 加密文件
+* 頻道頭像中現在使用 '#' 符號
+* Quicksy 將「總是」用作 OMEMO 加密的默認值(隱藏鎖定圖標)
@@ -0,0 +1 @@
+* 支援 ?register 和 ?register;preauth XMPP URI 參數
@@ -0,0 +1 @@
+在 Android 10 上支援自動主題切換
@@ -0,0 +1,2 @@
+* 在 Android 5+ 上提供 PDF 預覽
+* 在 OMEMO 中使用 12 字節的 IV
@@ -0,0 +1,2 @@
+* 修復在某些 Android 10 設備上的頭像選擇問題
+* 修復傳輸較大文件的問題
@@ -0,0 +1 @@
+* 音訊/視訊通話(需要伺服器支援,以 STUN 和 TURN 伺服器的形式通過 XEP-0215 可發現)
@@ -0,0 +1,2 @@
+* 語音通話的聲音回饋(撥號、通話開始、通話結束)
+* 修復重試失敗的視訊通話問題
@@ -0,0 +1,2 @@
+* 新增在視訊通話中切換攝像頭的按鈕
+* 修復平板電腦上的語音通話問題
@@ -0,0 +1,3 @@
+* 將通話圖標移至左側,以保持其他工具欄圖標的一致位置
+* 在語音通話期間顯示通話持續時間
+* 視訊/音訊通話的分開處理(同時互打電話的兩人的處理)
@@ -0,0 +1,2 @@
+* 重做憑證登錄的使用者介面
+* 新增置頂聊天的功能(加入最愛)
@@ -0,0 +1,3 @@
+* 在某些設備上減少通話中的回音
+* 修復當密碼包含特殊字符時的登錄問題
+* 在視訊通話期間在揚聲器上播放撥號和忙音
@@ -0,0 +1 @@
+* 在被呼叫方忙線時提供錄製語音訊息的選項
@@ -0,0 +1,3 @@
+* 如果音訊/視訊通話失敗,顯示求助按鈕
+* 修復一些令人困擾的崩潰問題
+* 修復使用裸 JIDs 的 Jingle 連接(檔案傳輸 + 通話)
@@ -0,0 +1,2 @@
+* 修復在某些情況下通知未顯示的問題
+* 修復與音訊/視訊通話相關的相容性問題和崩潰
@@ -0,0 +1,3 @@
+* 在音訊通話畫面中新增「返回聊天」選項
+* 改進鍵盤快捷鍵
+* 修復錯誤
@@ -0,0 +1,4 @@
+* 搜尋個別對話
+* 如果消息未能傳遞,通知使用者
+* 在重新啟動後保留來自 Quicksy 使用者的顯示名稱(暱稱)
+* 如果需要,新增從通知啟動 Orbot(洋蔥路由器)的按鈕
@@ -0,0 +1,2 @@
+* 修復在 Android <= 5 上的搜尋問題
+* 優化內存消耗
@@ -0,0 +1,3 @@
+* 在支援的伺服器上提供簡易邀請生成
+* 顯示從 Movim 發送的 GIF
+* 將頭像存儲在快取中
@@ -0,0 +1,3 @@
+* 修復使用不同 SCRAM 機制的不同帳戶時的連接問題
+* 新增對 SCRAM-SHA-512 的支援
+* 允許自己與自己進行 P2P(Jingle)文件傳輸
@@ -0,0 +1 @@
+* 對音訊/視訊通話進行了小幅度的穩定性改進
@@ -0,0 +1 @@
+* Quicksy:自動接收驗證簡訊
@@ -0,0 +1,3 @@
+* 如果離線的聯絡人之前宣告支援,則顯示通話按鈕
+* 當通話已連接時,返回按鈕不再結束通話
+* 錯誤修復
@@ -0,0 +1,4 @@
+* 可選擇來電鈴聲
+* 修復 開放金鑰匙圈 5.6+ 的 開放PGP 金鑰 ID 查找問題
+* 正確驗證 punycode TLS 證書
+* 改進 RTP 會話建立的穩定性(呼叫)
@@ -0,0 +1,2 @@
+* 使用預先存在的 OMEMO 會話驗證音訊/視訊通話
+* 改進與非 libwebrtc WebRTC 實現的相容性
@@ -0,0 +1,2 @@
+* 針對 Tor 支援進行了各種錯誤修復
+* 改進與 Dino 的通話相容性
@@ -0,0 +1 @@
+* 修復對不信任系統 CA 的用戶的 HTTP 上傳/下載問題
@@ -0,0 +1 @@
+* 修復在 Android 7.1 上的「無連線」問題
@@ -0,0 +1,2 @@
+* 總是驗證域名。不允許使用者覆寫
+* 支援花名冊預先驗證
@@ -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:花名册版本控制主要是为了在移动连接不佳的情况下节省带宽
@@ -1 +1 @@
-加密、易于使用的 XMPP 即时信使,适用于您的移动设备
+加密、易于使用的 XMPP 即时通讯软件,适用于您的移动设备
@@ -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>
@@ -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;
@@ -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;
@@ -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);
@@ -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;
}
@@ -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;
@@ -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);
@@ -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";
}
@@ -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() {
@@ -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");
}
}
@@ -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 =
@@ -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 {
@@ -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;
@@ -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"
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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">談話室の発見は<a href=https://search.jabber.network>search.jabber.network</a>というサービスを利用します.<br><br>利用するとIPアドレスと検索語はそのサービスに送信されます。詳細についてはそのサービスの<a href=https://search.jabber.network/privacy>個人情報保護方針</a>を参照してください。</string>
+ <string name="restore_warning_continued">自分で保存したバックアップしか復元しないでください!</string>
</resources>
@@ -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>
@@ -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>
@@ -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>
@@ -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 <b>OpenKeychain</b> för att kryptera och avkryptera dina publika nycklar.<br><br>Programmet är licensierat under GPLv3+, och finns tillgängligt via F-Droid och Google Play.<br><br><small>(Var god starta om %1$s efter installationen.)</small></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>
@@ -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>
@@ -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">群聊 & 频道</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>
@@ -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">頻道探索使用一個名為 <a href=https://search.jabber.network>search.jabber.network</a> 的第三方服務,<br><br>使用此功能會將您的 IP 位址和搜尋詞彙傳送至此服務。更多資訊請參見他們的 <a href=https://search.jabber.network/privacy>隱私權政策</a>。</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>
@@ -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>
@@ -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),了解更多信息。
@@ -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>