Merge tag '2.9.8' into develop

This commit is contained in:
Geno 2021-03-17 10:17:05 +01:00
commit f8e69f9fc2
26 changed files with 699 additions and 239 deletions

View File

@ -1,5 +1,10 @@
# Changelog # Changelog
### Version 2.9.8
* Verify A/V calls with preexisting OMEMO sessions
* Improve compatibility with non libwebrtc WebRTC implementations
### Version 2.9.7 ### Version 2.9.7
* Ability to select incoming call ringtone * Ability to select incoming call ringtone

View File

@ -66,7 +66,7 @@ dependencies {
implementation "com.wefika:flowlayout:0.4.1" implementation "com.wefika:flowlayout:0.4.1"
implementation 'net.ypresto.androidtranscoder:android-transcoder:0.3.0' implementation 'net.ypresto.androidtranscoder:android-transcoder:0.3.0'
implementation 'org.jxmpp:jxmpp-jid:0.6.4' implementation 'org.jxmpp:jxmpp-jid:0.6.4'
implementation 'org.osmdroid:osmdroid-android:6.1.5' implementation 'org.osmdroid:osmdroid-android:6.1.10'
implementation 'org.hsluv:hsluv:0.2' implementation 'org.hsluv:hsluv:0.2'
implementation 'org.conscrypt:conscrypt-android:2.2.1' implementation 'org.conscrypt:conscrypt-android:2.2.1'
implementation 'me.drakeet.support:toastcompat:1.1.0' implementation 'me.drakeet.support:toastcompat:1.1.0'
@ -91,8 +91,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 29 targetSdkVersion 29
versionCode 42000 versionCode 42006
versionName "2.9.7" versionName "2.9.8"
archivesBaseName += "-$versionName" archivesBaseName += "-$versionName"
applicationId "eu.sum7.conversations" applicationId "eu.sum7.conversations"
resValue "string", "applicationId", applicationId resValue "string", "applicationId", applicationId

View File

@ -0,0 +1,2 @@
* Verify A/V calls with preexisting OMEMO sessions
* Improve compatibility with non libwebrtc WebRTC implementations

View File

@ -1,16 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="pick_a_server">Vælg din XMPP udbyder</string> <string name="pick_a_server">Vælg din XMPP-udbyder</string>
<string name="use_conversations.im">Brug conversations.im</string> <string name="use_conversations.im">Brug conversations.im</string>
<string name="create_new_account">Opret ny konto</string> <string name="create_new_account">Opret ny konto</string>
<string name="do_you_have_an_account">Har du allerede en XMPP-konto? Dette kan være tilfældet, hvis du allerede bruger en anden XMPP-klient eller har brugt Conversations før. Hvis ikke, kan du nu oprette en ny XMPP-konto.\nTip: Nogle e-mail-udbydere leverer også XMPP-konti.</string> <string name="do_you_have_an_account">Har du allerede en XMPP-konto? Dette kan være tilfældet, hvis du allerede bruger en anden XMPP-klient eller har brugt Conversations før. Hvis ikke, kan du lige nu oprette en ny XMPP-konto.\nTip: Nogle e-mail-udbydere leverer også XMPP-konti.</string>
<string name="server_select_text">XMPP er et udbyderuafhængigt onlinemeddelelsesnetværk. Du kan bruge denne klient med hvilken XMPP-server du end vælger.\nMen for din nemhedsskyld har vi gjort vi det let at oprette en konto på conversations.im¹; en udbyder, der er specielt velegnet til brug med Conversations.</string> <string name="server_select_text">XMPP er et udbyderuafhængigt onlinemeddelelsesnetværk. Du kan bruge denne klient med hvilken XMPP-server du end vælger.\nMen for din nemhedsskyld har vi gjort vi det let at oprette en konto på conversations.im¹; en udbyder, der er specielt velegnet til brug med Conversations.</string>
<string name="magic_create_text_on_x">Du er blevet inviteret til %1$s. Vi guider dig gennem processen med at oprette en konto.\nNår du vælger %1$s som udbyder, kan du kommunikere med brugere fra andre udbydere ved at give dem din fulde XMPP-adresse.</string> <string name="magic_create_text_on_x">Du er blevet inviteret til %1$s. Vi guider dig gennem processen med at oprette en konto.\nNår du vælger %1$s som udbyder, kan du kommunikere med brugere fra andre udbydere ved at give dem din fulde XMPP-adresse.</string>
<string name="magic_create_text_fixed">Du er blevet inviteret til %1$s. Der er allerede valgt et brugernavn til dig. Vi guider dig gennem processen med at oprette en konto.\nDu vil være i stand til at kommunikere med brugere fra andre udbydere ved at give dem din fulde XMPP-adresse.</string> <string name="magic_create_text_fixed">Du er blevet inviteret til %1$s. Der er allerede valgt et brugernavn til dig. Vi guider dig gennem processen med at oprette en konto.\nDu vil være i stand til at kommunikere med brugere fra andre udbydere ved at give dem din fulde XMPP-adresse.</string>
<string name="your_server_invitation">Din server invitation</string> <string name="your_server_invitation">Din server invitation</string>
<string name="improperly_formatted_provisioning">Forkert formateret klargøringskode</string> <string name="improperly_formatted_provisioning">Forkert formateret klargøringskode</string>
<string name="tap_share_button_send_invite">Tryk på deleknappen for at sende din kontakt en invitation til %1$s.</string> <string name="tap_share_button_send_invite">Tryk på deleknappen for at sende din kontakt en invitation til %1$s.</string>
<string name="if_contact_is_nearby_use_qr">Hvis din kontakt er i nærheden, kan de også scanne koden nedenfor for at acceptere din invitation.</string> <string name="if_contact_is_nearby_use_qr">Hvis din kontakt er i nærheden, kan de også skanne koden nedenfor for at acceptere din invitation.</string>
<string name="easy_invite_share_text">Deltag %1$s og chat med mig: %2$s</string> <string name="easy_invite_share_text">Deltag med %1$s og chat med mig: %2$s</string>
<string name="share_invite_with">Del invitation med...</string> <string name="share_invite_with">Del invitation med...</string>
</resources> </resources>

View File

@ -101,6 +101,7 @@ public final class Config {
public static final boolean REMOVE_BROKEN_DEVICES = false; public static final boolean REMOVE_BROKEN_DEVICES = false;
public static final boolean OMEMO_PADDING = false; public static final boolean OMEMO_PADDING = false;
public static final boolean PUT_AUTH_TAG_INTO_KEY = true; 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 USE_BOOKMARKS2 = false;

View File

@ -8,6 +8,8 @@ import android.util.Pair;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.IdentityKeyPair;
@ -49,9 +51,15 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.SerialSingleThreadExecutor; import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.jingle.OmemoVerification;
import eu.siacs.conversations.xmpp.jingle.OmemoVerifiedRtpContentMap;
import eu.siacs.conversations.xmpp.jingle.RtpContentMap;
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
import eu.siacs.conversations.xmpp.jingle.stanzas.OmemoVerifiedIceUdpTransportInfo;
import eu.siacs.conversations.xmpp.pep.PublishOptions; import eu.siacs.conversations.xmpp.pep.PublishOptions;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
@ -1198,6 +1206,99 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}); });
} }
private OmemoVerifiedIceUdpTransportInfo encrypt(final IceUdpTransportInfo element, final XmppAxolotlSession session) throws CryptoFailedException {
final OmemoVerifiedIceUdpTransportInfo transportInfo = new OmemoVerifiedIceUdpTransportInfo();
transportInfo.setAttributes(element.getAttributes());
for (final Element child : element.getChildren()) {
if ("fingerprint".equals(child.getName()) && Namespace.JINGLE_APPS_DTLS.equals(child.getNamespace())) {
final Element fingerprint = new Element("fingerprint", Namespace.OMEMO_DTLS_SRTP_VERIFICATION);
fingerprint.setAttribute("setup", child.getAttribute("setup"));
fingerprint.setAttribute("hash", child.getAttribute("hash"));
final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(account.getJid().asBareJid(), getOwnDeviceId());
final String content = child.getContent();
axolotlMessage.encrypt(content);
axolotlMessage.addDevice(session);
fingerprint.addChild(axolotlMessage.toElement());
transportInfo.addChild(fingerprint);
} else {
transportInfo.addChild(child);
}
}
return transportInfo;
}
public OmemoVerifiedPayload<OmemoVerifiedRtpContentMap> encrypt(final RtpContentMap rtpContentMap, final Jid jid, final int deviceId) throws CryptoFailedException {
final SignalProtocolAddress address = new SignalProtocolAddress(jid.asBareJid().toString(), deviceId);
final XmppAxolotlSession session = sessions.get(address);
if (session == null) {
throw new CryptoFailedException(String.format("No session found for %d", deviceId));
}
final ImmutableMap.Builder<String, RtpContentMap.DescriptionTransport> descriptionTransportBuilder = new ImmutableMap.Builder<>();
final OmemoVerification omemoVerification = new OmemoVerification();
omemoVerification.setDeviceId(deviceId);
omemoVerification.setSessionFingerprint(session.getFingerprint());
for (final Map.Entry<String, RtpContentMap.DescriptionTransport> content : rtpContentMap.contents.entrySet()) {
final RtpContentMap.DescriptionTransport descriptionTransport = content.getValue();
final OmemoVerifiedIceUdpTransportInfo encryptedTransportInfo = encrypt(descriptionTransport.transport, session);
descriptionTransportBuilder.put(
content.getKey(),
new RtpContentMap.DescriptionTransport(descriptionTransport.description, encryptedTransportInfo)
);
}
return new OmemoVerifiedPayload<>(
omemoVerification,
new OmemoVerifiedRtpContentMap(rtpContentMap.group, descriptionTransportBuilder.build())
);
}
public OmemoVerifiedPayload<RtpContentMap> decrypt(OmemoVerifiedRtpContentMap omemoVerifiedRtpContentMap, final Jid from) throws CryptoFailedException {
final ImmutableMap.Builder<String, RtpContentMap.DescriptionTransport> descriptionTransportBuilder = new ImmutableMap.Builder<>();
final OmemoVerification omemoVerification = new OmemoVerification();
for (final Map.Entry<String, RtpContentMap.DescriptionTransport> content : omemoVerifiedRtpContentMap.contents.entrySet()) {
final RtpContentMap.DescriptionTransport descriptionTransport = content.getValue();
final OmemoVerifiedPayload<IceUdpTransportInfo> decryptedTransport = decrypt((OmemoVerifiedIceUdpTransportInfo) descriptionTransport.transport, from);
omemoVerification.setOrEnsureEqual(decryptedTransport);
descriptionTransportBuilder.put(
content.getKey(),
new RtpContentMap.DescriptionTransport(descriptionTransport.description, decryptedTransport.payload)
);
}
processPostponed();
return new OmemoVerifiedPayload<>(
omemoVerification,
new RtpContentMap(omemoVerifiedRtpContentMap.group, descriptionTransportBuilder.build())
);
}
private OmemoVerifiedPayload<IceUdpTransportInfo> decrypt(final OmemoVerifiedIceUdpTransportInfo verifiedIceUdpTransportInfo, final Jid from) throws CryptoFailedException {
final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo();
transportInfo.setAttributes(verifiedIceUdpTransportInfo.getAttributes());
final OmemoVerification omemoVerification = new OmemoVerification();
for (final Element child : verifiedIceUdpTransportInfo.getChildren()) {
if ("fingerprint".equals(child.getName()) && Namespace.OMEMO_DTLS_SRTP_VERIFICATION.equals(child.getNamespace())) {
final Element fingerprint = new Element("fingerprint", Namespace.JINGLE_APPS_DTLS);
fingerprint.setAttribute("setup", child.getAttribute("setup"));
fingerprint.setAttribute("hash", child.getAttribute("hash"));
final Element encrypted = child.findChildEnsureSingle(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX);
final XmppAxolotlMessage xmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, from.asBareJid());
final XmppAxolotlSession session = getReceivingSession(xmppAxolotlMessage);
final XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintext = xmppAxolotlMessage.decrypt(session, getOwnDeviceId());
final Integer preKeyId = session.getPreKeyIdAndReset();
if (preKeyId != null) {
postponedSessions.add(session);
}
fingerprint.setContent(plaintext.getPlaintext());
omemoVerification.setDeviceId(session.getRemoteAddress().getDeviceId());
omemoVerification.setSessionFingerprint(plaintext.getFingerprint());
transportInfo.addChild(fingerprint);
} else {
transportInfo.addChild(child);
}
}
return new OmemoVerifiedPayload<>(omemoVerification, transportInfo);
}
public void prepareKeyTransportMessage(final Conversation conversation, final OnMessageCreatedCallback onMessageCreatedCallback) { public void prepareKeyTransportMessage(final Conversation conversation, final OnMessageCreatedCallback onMessageCreatedCallback) {
executor.execute(new Runnable() { executor.execute(new Runnable() {
@Override @Override
@ -1318,7 +1419,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
} else { } else {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": nothing to flush. Not republishing key"); Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": nothing to flush. Not republishing key");
} }
if (trustedOrPreviouslyResponded(session)) { if (trustedOrPreviouslyResponded(session) && Config.AUTOMATICALLY_COMPLETE_SESSIONS) {
completeSession(session); completeSession(session);
} }
} }
@ -1333,7 +1434,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
final Iterator<XmppAxolotlSession> iterator = postponedSessions.iterator(); final Iterator<XmppAxolotlSession> iterator = postponedSessions.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final XmppAxolotlSession session = iterator.next(); final XmppAxolotlSession session = iterator.next();
if (trustedOrPreviouslyResponded(session)) { if (trustedOrPreviouslyResponded(session) && Config.AUTOMATICALLY_COMPLETE_SESSIONS) {
completeSession(session); completeSession(session);
} }
iterator.remove(); iterator.remove();
@ -1565,4 +1666,28 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
} }
} }
} }
public static class OmemoVerifiedPayload<T> {
private final int deviceId;
private final String fingerprint;
private final T payload;
private OmemoVerifiedPayload(OmemoVerification omemoVerification, T payload) {
this.deviceId = omemoVerification.getDeviceId();
this.fingerprint = omemoVerification.getFingerprint();
this.payload = payload;
}
public int getDeviceId() {
return deviceId;
}
public String getFingerprint() {
return fingerprint;
}
public T getPayload() {
return payload;
}
}
} }

View File

@ -59,7 +59,7 @@ public class XmppAxolotlMessage {
switch (keyElement.getName()) { switch (keyElement.getName()) {
case KEYTAG: case KEYTAG:
try { try {
Integer recipientId = Integer.parseInt(keyElement.getAttribute(REMOTEID)); int recipientId = Integer.parseInt(keyElement.getAttribute(REMOTEID));
byte[] key = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT); byte[] key = Base64.decode(keyElement.getContent().trim(), Base64.DEFAULT);
boolean isPreKey = keyElement.getAttributeAsBoolean("prekey"); boolean isPreKey = keyElement.getAttributeAsBoolean("prekey");
this.keys.add(new XmppAxolotlSession.AxolotlKey(recipientId, key, isPreKey)); this.keys.add(new XmppAxolotlSession.AxolotlKey(recipientId, key, isPreKey));
@ -145,7 +145,7 @@ public class XmppAxolotlMessage {
return ciphertext != null; return ciphertext != null;
} }
void encrypt(String plaintext) throws CryptoFailedException { void encrypt(final String plaintext) throws CryptoFailedException {
try { try {
SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE); SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);

View File

@ -488,13 +488,11 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (conversation.getMucOptions().isSelf(counterpart)) { if (conversation.getMucOptions().isSelf(counterpart)) {
status = Message.STATUS_SEND_RECEIVED; status = Message.STATUS_SEND_RECEIVED;
isCarbon = true; //not really carbon but received from another resource isCarbon = true; //not really carbon but received from another resource
//TODO this would be the place to change the body after something like mod_pastebin if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status, serverMsgId, body)) {
if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status, serverMsgId)) {
return; return;
} else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) { } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) {
LocalizedContent localizedBody = packet.getBody(); if (body != null) {
if (localizedBody != null) { Message message = conversation.findSentMessageWithBody(body.content);
Message message = conversation.findSentMessageWithBody(localizedBody.content);
if (message != null) { if (message != null) {
mXmppConnectionService.markMessage(message, status); mXmppConnectionService.markMessage(message, status);
return; return;

View File

@ -136,6 +136,7 @@ import eu.siacs.conversations.utils.TorServiceUtils;
import eu.siacs.conversations.utils.WakeLockHelper; import eu.siacs.conversations.utils.WakeLockHelper;
import eu.siacs.conversations.utils.XmppUri; import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.LocalizedContent;
import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnBindListener; import eu.siacs.conversations.xmpp.OnBindListener;
@ -691,6 +692,7 @@ public class XmppConnectionService extends Service {
} }
case TorServiceUtils.ACTION_STATUS: case TorServiceUtils.ACTION_STATUS:
final String status = intent.getStringExtra(TorServiceUtils.EXTRA_STATUS); final String status = intent.getStringExtra(TorServiceUtils.EXTRA_STATUS);
//TODO port and host are in 'extras' - but this may not be a reliable source?
if ("ON".equals(status)) { if ("ON".equals(status)) {
handleOrbotStartedEvent(); handleOrbotStartedEvent();
return START_STICKY; return START_STICKY;
@ -3949,16 +3951,28 @@ public class XmppConnectionService extends Service {
return null; return null;
} }
public boolean markMessage(Conversation conversation, String uuid, int status, String serverMessageId) { public boolean markMessage(final Conversation conversation, final String uuid, final int status, final String serverMessageId) {
return markMessage(conversation, uuid, status, serverMessageId, null);
}
public boolean markMessage(final Conversation conversation, final String uuid, final int status, final String serverMessageId, final LocalizedContent body) {
if (uuid == null) { if (uuid == null) {
return false; return false;
} else { } else {
Message message = conversation.findSentMessageWithUuid(uuid); final Message message = conversation.findSentMessageWithUuid(uuid);
if (message != null) { if (message != null) {
if (message.getServerMsgId() == null) { if (message.getServerMsgId() == null) {
message.setServerMsgId(serverMessageId); message.setServerMsgId(serverMessageId);
} }
if (message.getEncryption() == Message.ENCRYPTION_NONE && isBodyModified(message, body)) {
message.setBody(body.content);
if (body.count > 1) {
message.setBodyLanguage(body.language);
}
markMessage(message, status, null, true);
} else {
markMessage(message, status); markMessage(message, status);
}
return true; return true;
} else { } else {
return false; return false;
@ -3966,12 +3980,23 @@ public class XmppConnectionService extends Service {
} }
} }
private static boolean isBodyModified(final Message message, final LocalizedContent body) {
if (body == null || body.content == null) {
return false;
}
return !body.content.equals(message.getBody());
}
public void markMessage(Message message, int status) { public void markMessage(Message message, int status) {
markMessage(message, status, null); markMessage(message, status, null);
} }
public void markMessage(final Message message, final int status, final String errorMessage) { public void markMessage(final Message message, final int status, final String errorMessage) {
markMessage(message, status, errorMessage, false);
}
public void markMessage(final Message message, final int status, final String errorMessage, final boolean includeBody) {
final int oldStatus = message.getStatus(); final int oldStatus = message.getStatus();
if (status == Message.STATUS_SEND_FAILED && (oldStatus == Message.STATUS_SEND_RECEIVED || oldStatus == Message.STATUS_SEND_DISPLAYED)) { if (status == Message.STATUS_SEND_FAILED && (oldStatus == Message.STATUS_SEND_RECEIVED || oldStatus == Message.STATUS_SEND_DISPLAYED)) {
return; return;
@ -3981,7 +4006,7 @@ public class XmppConnectionService extends Service {
} }
message.setErrorMessage(errorMessage); message.setErrorMessage(errorMessage);
message.setStatus(status); message.setStatus(status);
databaseBackend.updateMessage(message, false); databaseBackend.updateMessage(message, includeBody);
updateConversationUi(); updateConversationUi();
if (oldStatus != status && status == Message.STATUS_SEND_FAILED) { if (oldStatus != status && status == Message.STATUS_SEND_FAILED) {
mNotificationService.pushFailedDelivery(message); mNotificationService.pushFailedDelivery(message);

View File

@ -389,6 +389,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
if (state != null) { if (state != null) {
Log.d(Config.LOGTAG, "restored last state from intent extra"); Log.d(Config.LOGTAG, "restored last state from intent extra");
updateButtonConfiguration(state); updateButtonConfiguration(state);
updateVerifiedShield(false);
updateStateDisplay(state); updateStateDisplay(state);
updateProfilePicture(state); updateProfilePicture(state);
invalidateOptionsMenu(); invalidateOptionsMenu();
@ -488,8 +489,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
return; return;
} }
//TODO apparently this method is not getting called on Android 10 when using the task switcher //TODO apparently this method is not getting called on Android 10 when using the task switcher
final boolean emptyReference = rtpConnectionReference == null || rtpConnectionReference.get() == null; if (emptyReference(rtpConnectionReference) && xmppConnectionService != null) {
if (emptyReference && xmppConnectionService != null) {
retractSessionProposal(); retractSessionProposal();
} }
} }
@ -558,6 +558,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
} }
this.rtpConnectionReference = reference; this.rtpConnectionReference = reference;
final RtpEndUserState currentState = requireRtpConnection().getEndUserState(); final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
final boolean verified = requireRtpConnection().isVerified();
if (currentState == RtpEndUserState.ENDED) { if (currentState == RtpEndUserState.ENDED) {
reference.get().throwStateTransitionException(); reference.get().throwStateTransitionException();
finish(); finish();
@ -573,6 +574,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
binding.with.setText(getWith().getDisplayName()); binding.with.setText(getWith().getDisplayName());
updateVideoViews(currentState); updateVideoViews(currentState);
updateStateDisplay(currentState, media); updateStateDisplay(currentState, media);
updateVerifiedShield(verified && STATES_SHOWING_SWITCH_TO_CHAT.contains(currentState));
updateButtonConfiguration(currentState, media); updateButtonConfiguration(currentState, media);
updateProfilePicture(currentState); updateProfilePicture(currentState);
invalidateOptionsMenu(); invalidateOptionsMenu();
@ -591,6 +593,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
updateStateDisplay(state); updateStateDisplay(state);
updateProfilePicture(state); updateProfilePicture(state);
updateCallDuration(); updateCallDuration();
updateVerifiedShield(false);
invalidateOptionsMenu(); invalidateOptionsMenu();
binding.with.setText(account.getRoster().getContact(with).getDisplayName()); binding.with.setText(account.getRoster().getContact(with).getDisplayName());
} }
@ -672,6 +675,14 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
} }
} }
private void updateVerifiedShield(final boolean verified) {
if (isPictureInPicture()) {
this.binding.verified.setVisibility(View.GONE);
return;
}
this.binding.verified.setVisibility(verified ? View.VISIBLE : View.GONE);
}
private void updateProfilePicture(final RtpEndUserState state) { private void updateProfilePicture(final RtpEndUserState state) {
updateProfilePicture(state, null); updateProfilePicture(state, null);
} }
@ -1065,7 +1076,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
updateRtpSessionProposalState(account, with, state); updateRtpSessionProposalState(account, with, state);
return; return;
} }
if (this.rtpConnectionReference == null) { if (emptyReference(this.rtpConnectionReference)) {
if (END_CARD.contains(state)) { if (END_CARD.contains(state)) {
Log.d(Config.LOGTAG, "not reinitializing session"); Log.d(Config.LOGTAG, "not reinitializing session");
return; return;
@ -1075,6 +1086,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
return; return;
} }
final AbstractJingleConnection.Id id = requireRtpConnection().getId(); final AbstractJingleConnection.Id id = requireRtpConnection().getId();
final boolean verified = requireRtpConnection().isVerified();
final Set<Media> media = getMedia(); final Set<Media> media = getMedia();
final Contact contact = getWith(); final Contact contact = getWith();
if (account == id.account && id.with.equals(with) && id.sessionId.equals(sessionId)) { if (account == id.account && id.with.equals(with) && id.sessionId.equals(sessionId)) {
@ -1084,6 +1096,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
} }
runOnUiThread(() -> { runOnUiThread(() -> {
updateStateDisplay(state, media); updateStateDisplay(state, media);
updateVerifiedShield(verified && STATES_SHOWING_SWITCH_TO_CHAT.contains(state));
updateButtonConfiguration(state, media); updateButtonConfiguration(state, media);
updateVideoViews(state); updateVideoViews(state);
updateProfilePicture(state, contact); updateProfilePicture(state, contact);
@ -1133,6 +1146,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
} }
if (Jid.ofEscaped(withExtra).asBareJid().equals(with)) { if (Jid.ofEscaped(withExtra).asBareJid().equals(with)) {
runOnUiThread(() -> { runOnUiThread(() -> {
updateVerifiedShield(false);
updateStateDisplay(state); updateStateDisplay(state);
updateButtonConfiguration(state); updateButtonConfiguration(state);
updateProfilePicture(state); updateProfilePicture(state);
@ -1160,4 +1174,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
intent.putExtra(EXTRA_LAST_ACTION, media.contains(Media.VIDEO) ? ACTION_MAKE_VIDEO_CALL : ACTION_MAKE_VOICE_CALL); intent.putExtra(EXTRA_LAST_ACTION, media.contains(Media.VIDEO) ? ACTION_MAKE_VIDEO_CALL : ACTION_MAKE_VOICE_CALL);
setIntent(intent); setIntent(intent);
} }
private static boolean emptyReference(final WeakReference<?> weakReference) {
return weakReference == null || weakReference.get() == null;
}
} }

View File

@ -53,4 +53,5 @@ public final class Namespace {
public static final String INVITE = "urn:xmpp:invite"; public static final String INVITE = "urn:xmpp:invite";
public static final String PARS = "urn:xmpp:pars:0"; public static final String PARS = "urn:xmpp:pars:0";
public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite"; public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite";
public static final String OMEMO_DTLS_SRTP_VERIFICATION = "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification";
} }

View File

@ -30,6 +30,9 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.CryptoFailedException;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational; import eu.siacs.conversations.entities.Conversational;
@ -43,6 +46,7 @@ import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.jingle.stanzas.Group; import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo; import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
import eu.siacs.conversations.xmpp.jingle.stanzas.Proceed;
import eu.siacs.conversations.xmpp.jingle.stanzas.Propose; import eu.siacs.conversations.xmpp.jingle.stanzas.Propose;
import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription; import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
@ -123,6 +127,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this); private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this);
private final ArrayDeque<Set<Map.Entry<String, RtpContentMap.DescriptionTransport>>> pendingIceCandidates = new ArrayDeque<>(); private final ArrayDeque<Set<Map.Entry<String, RtpContentMap.DescriptionTransport>>> pendingIceCandidates = new ArrayDeque<>();
private final OmemoVerification omemoVerification = new OmemoVerification();
private final Message message; private final Message message;
private State state = State.NULL; private State state = State.NULL;
private StateTransitionException stateTransitionException; private StateTransitionException stateTransitionException;
@ -290,6 +295,25 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
} }
private RtpContentMap receiveRtpContentMap(final JinglePacket jinglePacket, final boolean expectVerification) {
final RtpContentMap receivedContentMap = RtpContentMap.of(jinglePacket);
if (receivedContentMap instanceof OmemoVerifiedRtpContentMap) {
final AxolotlService.OmemoVerifiedPayload<RtpContentMap> omemoVerifiedPayload;
try {
omemoVerifiedPayload = id.account.getAxolotlService().decrypt((OmemoVerifiedRtpContentMap) receivedContentMap, id.with);
} catch (final CryptoFailedException e) {
throw new SecurityException("Unable to verify DTLS Fingerprint with OMEMO", e);
}
this.omemoVerification.setOrEnsureEqual(omemoVerifiedPayload);
Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": received verifiable DTLS fingerprint via "+this.omemoVerification);
return omemoVerifiedPayload.getPayload();
} else if (expectVerification) {
throw new SecurityException("DTLS fingerprint was unexpectedly not verifiable");
} else {
return receivedContentMap;
}
}
private void receiveSessionInitiate(final JinglePacket jinglePacket) { private void receiveSessionInitiate(final JinglePacket jinglePacket) {
if (isInitiator()) { if (isInitiator()) {
Log.d(Config.LOGTAG, String.format("%s: received session-initiate even though we were initiating", id.account.getJid().asBareJid())); Log.d(Config.LOGTAG, String.format("%s: received session-initiate even though we were initiating", id.account.getJid().asBareJid()));
@ -298,7 +322,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
final RtpContentMap contentMap; final RtpContentMap contentMap;
try { try {
contentMap = RtpContentMap.of(jinglePacket); contentMap = receiveRtpContentMap(jinglePacket, false);
contentMap.requireContentDescriptions(); contentMap.requireContentDescriptions();
contentMap.requireDTLSFingerprint(); contentMap.requireDTLSFingerprint();
} catch (final RuntimeException e) { } catch (final RuntimeException e) {
@ -328,7 +352,11 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
if (transition(target, () -> this.initiatorRtpContentMap = contentMap)) { if (transition(target, () -> this.initiatorRtpContentMap = contentMap)) {
respondOk(jinglePacket); respondOk(jinglePacket);
pendingIceCandidates.push(contentMap.contents.entrySet());
final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates = contentMap.contents.entrySet();
if (candidates.size() > 0) {
pendingIceCandidates.push(candidates);
}
if (target == State.SESSION_INITIALIZED_PRE_APPROVED) { if (target == State.SESSION_INITIALIZED_PRE_APPROVED) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": automatically accepting session-initiate"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": automatically accepting session-initiate");
sendSessionAccept(); sendSessionAccept();
@ -350,7 +378,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
final RtpContentMap contentMap; final RtpContentMap contentMap;
try { try {
contentMap = RtpContentMap.of(jinglePacket); contentMap = receiveRtpContentMap(jinglePacket, this.omemoVerification.hasFingerprint());
contentMap.requireContentDescriptions(); contentMap.requireContentDescriptions();
contentMap.requireDTLSFingerprint(); contentMap.requireDTLSFingerprint();
} catch (final RuntimeException e) { } catch (final RuntimeException e) {
@ -469,7 +497,20 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void sendSessionAccept(final RtpContentMap rtpContentMap) { private void sendSessionAccept(final RtpContentMap rtpContentMap) {
this.responderRtpContentMap = rtpContentMap; this.responderRtpContentMap = rtpContentMap;
this.transitionOrThrow(State.SESSION_ACCEPTED); this.transitionOrThrow(State.SESSION_ACCEPTED);
final JinglePacket sessionAccept = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_ACCEPT, id.sessionId); final RtpContentMap outgoingContentMap;
if (this.omemoVerification.hasDeviceId()) {
final AxolotlService.OmemoVerifiedPayload<OmemoVerifiedRtpContentMap> verifiedPayload;
try {
verifiedPayload = id.account.getAxolotlService().encrypt(rtpContentMap, id.with, omemoVerification.getDeviceId());
outgoingContentMap = verifiedPayload.getPayload();
this.omemoVerification.setOrEnsureEqual(verifiedPayload);
} catch (final Exception e) {
throw new SecurityException("Unable to verify DTLS Fingerprint with OMEMO", e);
}
} else {
outgoingContentMap = rtpContentMap;
}
final JinglePacket sessionAccept = outgoingContentMap.toJinglePacket(JinglePacket.Action.SESSION_ACCEPT, id.sessionId);
send(sessionAccept); send(sessionAccept);
} }
@ -480,7 +521,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
receivePropose(from, Propose.upgrade(message), serverMessageId, timestamp); receivePropose(from, Propose.upgrade(message), serverMessageId, timestamp);
break; break;
case "proceed": case "proceed":
receiveProceed(from, serverMessageId, timestamp); receiveProceed(from, Proceed.upgrade(message), serverMessageId, timestamp);
break; break;
case "retract": case "retract":
receiveRetract(from, serverMessageId, timestamp); receiveRetract(from, serverMessageId, timestamp);
@ -621,7 +662,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
} }
private void receiveProceed(final Jid from, final String serverMsgId, final long timestamp) { private void receiveProceed(final Jid from, final Proceed proceed, final String serverMsgId, final long timestamp) {
final Set<Media> media = Preconditions.checkNotNull(this.proposedMedia, "Proposed media has to be set before handling proceed"); final Set<Media> media = Preconditions.checkNotNull(this.proposedMedia, "Proposed media has to be set before handling proceed");
Preconditions.checkState(media.size() > 0, "Proposed media should not be empty"); Preconditions.checkState(media.size() > 0, "Proposed media should not be empty");
if (from.equals(id.with)) { if (from.equals(id.with)) {
@ -631,6 +672,15 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
this.message.setServerMsgId(serverMsgId); this.message.setServerMsgId(serverMsgId);
} }
this.message.setTime(timestamp); this.message.setTime(timestamp);
final Integer remoteDeviceId = proceed.getDeviceId();
if (isOmemoEnabled()) {
this.omemoVerification.setDeviceId(remoteDeviceId);
} else {
if (remoteDeviceId != null) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid()+": remote party signaled support for OMEMO verification but we have OMEMO disabled");
}
this.omemoVerification.setDeviceId(null);
}
this.sendSessionInitiate(media, State.SESSION_INITIALIZED_PRE_APPROVED); this.sendSessionInitiate(media, State.SESSION_INITIALIZED_PRE_APPROVED);
} else { } else {
Log.d(Config.LOGTAG, String.format("%s: ignoring proceed because already in %s", id.account.getJid().asBareJid(), this.state)); Log.d(Config.LOGTAG, String.format("%s: ignoring proceed because already in %s", id.account.getJid().asBareJid(), this.state));
@ -716,13 +766,31 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
} }
private void sendSessionInitiate(RtpContentMap rtpContentMap, final State targetState) { private void sendSessionInitiate(final RtpContentMap rtpContentMap, final State targetState) {
this.initiatorRtpContentMap = rtpContentMap; this.initiatorRtpContentMap = rtpContentMap;
this.transitionOrThrow(targetState); this.transitionOrThrow(targetState);
final JinglePacket sessionInitiate = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_INITIATE, id.sessionId); //TODO do on background thread?
final RtpContentMap outgoingContentMap = encryptSessionInitiate(rtpContentMap);
final JinglePacket sessionInitiate = outgoingContentMap.toJinglePacket(JinglePacket.Action.SESSION_INITIATE, id.sessionId);
send(sessionInitiate); send(sessionInitiate);
} }
private RtpContentMap encryptSessionInitiate(final RtpContentMap rtpContentMap) {
if (this.omemoVerification.hasDeviceId()) {
final AxolotlService.OmemoVerifiedPayload<OmemoVerifiedRtpContentMap> verifiedPayload;
try {
verifiedPayload = id.account.getAxolotlService().encrypt(rtpContentMap, id.with, omemoVerification.getDeviceId());
} catch (final CryptoFailedException e) {
Log.w(Config.LOGTAG,id.account.getJid().asBareJid()+": unable to use OMEMO DTLS verification on outgoing session initiate. falling back", e);
return rtpContentMap;
}
this.omemoVerification.setSessionFingerprint(verifiedPayload.getFingerprint());
return verifiedPayload.getPayload();
} else {
return rtpContentMap;
}
}
private void sendSessionTerminate(final Reason reason) { private void sendSessionTerminate(final Reason reason) {
sendSessionTerminate(reason, null); sendSessionTerminate(reason, null);
} }
@ -911,6 +979,15 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
public boolean isVerified() {
final String fingerprint = this.omemoVerification.getFingerprint();
if (fingerprint == null) {
return false;
}
final FingerprintStatus status = id.account.getAxolotlService().getFingerprintTrust(fingerprint);
return status != null && status.getTrust() == FingerprintStatus.Trust.VERIFIED;
}
public synchronized void acceptCall() { public synchronized void acceptCall() {
switch (this.state) { switch (this.state) {
case PROPOSED: case PROPOSED:
@ -1055,16 +1132,29 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void sendJingleMessage(final String action, final Jid to) { private void sendJingleMessage(final String action, final Jid to) {
final MessagePacket messagePacket = new MessagePacket(); final MessagePacket messagePacket = new MessagePacket();
if ("proceed".equals(action)) {
messagePacket.setId(JINGLE_MESSAGE_PROCEED_ID_PREFIX + id.sessionId);
}
messagePacket.setType(MessagePacket.TYPE_CHAT); //we want to carbon copy those messagePacket.setType(MessagePacket.TYPE_CHAT); //we want to carbon copy those
messagePacket.setTo(to); messagePacket.setTo(to);
messagePacket.addChild(action, Namespace.JINGLE_MESSAGE).setAttribute("id", id.sessionId); final Element intent = messagePacket.addChild(action, Namespace.JINGLE_MESSAGE).setAttribute("id", id.sessionId);
if ("proceed".equals(action)) {
messagePacket.setId(JINGLE_MESSAGE_PROCEED_ID_PREFIX + id.sessionId);
if (isOmemoEnabled()) {
final int deviceId = id.account.getAxolotlService().getOwnDeviceId();
final Element device = intent.addChild("device", Namespace.OMEMO_DTLS_SRTP_VERIFICATION);
device.setAttribute("id", deviceId);
}
}
messagePacket.addChild("store", "urn:xmpp:hints"); messagePacket.addChild("store", "urn:xmpp:hints");
xmppConnectionService.sendMessagePacket(id.account, messagePacket); xmppConnectionService.sendMessagePacket(id.account, messagePacket);
} }
private boolean isOmemoEnabled() {
final Conversational conversational = message.getConversation();
if (conversational instanceof Conversation) {
return ((Conversation) conversational).getNextEncryption() == Message.ENCRYPTION_AXOLOTL;
}
return false;
}
private void acceptCallFromSessionInitialized() { private void acceptCallFromSessionInitialized() {
xmppConnectionService.getNotificationService().cancelIncomingCallNotification(); xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
sendSessionAccept(); sendSessionAccept();

View File

@ -0,0 +1,83 @@
package eu.siacs.conversations.xmpp.jingle;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import java.util.concurrent.atomic.AtomicBoolean;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
public class OmemoVerification {
private final AtomicBoolean deviceIdWritten = new AtomicBoolean(false);
private final AtomicBoolean sessionFingerprintWritten = new AtomicBoolean(false);
private Integer deviceId;
private String sessionFingerprint;
public void setDeviceId(final Integer id) {
if (deviceIdWritten.compareAndSet(false, true)) {
this.deviceId = id;
return;
}
throw new IllegalStateException("Device Id has already been set");
}
public int getDeviceId() {
Preconditions.checkNotNull(this.deviceId, "Device ID is null");
return this.deviceId;
}
public boolean hasDeviceId() {
return this.deviceId != null;
}
public void setSessionFingerprint(final String fingerprint) {
Preconditions.checkNotNull(fingerprint, "Session fingerprint must not be null");
if (sessionFingerprintWritten.compareAndSet(false, true)) {
this.sessionFingerprint = fingerprint;
return;
}
throw new IllegalStateException("Session fingerprint has already been set");
}
public String getFingerprint() {
return this.sessionFingerprint;
}
public void setOrEnsureEqual(AxolotlService.OmemoVerifiedPayload<?> omemoVerifiedPayload) {
setOrEnsureEqual(omemoVerifiedPayload.getDeviceId(), omemoVerifiedPayload.getFingerprint());
}
public void setOrEnsureEqual(final int deviceId, final String sessionFingerprint) {
Preconditions.checkNotNull(sessionFingerprint, "Session fingerprint must not be null");
if (this.deviceIdWritten.get() || this.sessionFingerprintWritten.get()) {
if (this.sessionFingerprint == null) {
throw new IllegalStateException("No session fingerprint has been previously provided");
}
if (!sessionFingerprint.equals(this.sessionFingerprint)) {
throw new IllegalStateException("Session Fingerprints did not match");
}
if (this.deviceId == null) {
throw new IllegalStateException("No Device Id has been previously provided");
}
if (this.deviceId != deviceId) {
throw new IllegalStateException("Device Ids did not match");
}
} else {
this.setSessionFingerprint(sessionFingerprint);
this.setDeviceId(deviceId);
}
}
public boolean hasFingerprint() {
return this.sessionFingerprint != null;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("deviceId", deviceId)
.add("fingerprint", sessionFingerprint)
.toString();
}
}

View File

@ -0,0 +1,19 @@
package eu.siacs.conversations.xmpp.jingle;
import java.util.Map;
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
import eu.siacs.conversations.xmpp.jingle.stanzas.OmemoVerifiedIceUdpTransportInfo;
public class OmemoVerifiedRtpContentMap extends RtpContentMap {
public OmemoVerifiedRtpContentMap(Group group, Map<String, DescriptionTransport> contents) {
super(group, contents);
for(final DescriptionTransport descriptionTransport : contents.values()) {
if (descriptionTransport.transport instanceof OmemoVerifiedIceUdpTransportInfo) {
((OmemoVerifiedIceUdpTransportInfo) descriptionTransport.transport).ensureNoPlaintextFingerprint();
continue;
}
throw new IllegalStateException("OmemoVerifiedRtpContentMap contains non-verified transport info");
}
}
}

View File

@ -14,6 +14,7 @@ import com.google.common.collect.Sets;
import org.checkerframework.checker.nullness.compatqual.NullableDecl; import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -25,6 +26,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.GenericTransportInfo;
import eu.siacs.conversations.xmpp.jingle.stanzas.Group; import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo; import eu.siacs.conversations.xmpp.jingle.stanzas.IceUdpTransportInfo;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
import eu.siacs.conversations.xmpp.jingle.stanzas.OmemoVerifiedIceUdpTransportInfo;
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription; import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
public class RtpContentMap { public class RtpContentMap {
@ -32,13 +34,32 @@ public class RtpContentMap {
public final Group group; public final Group group;
public final Map<String, DescriptionTransport> contents; public final Map<String, DescriptionTransport> contents;
private RtpContentMap(Group group, Map<String, DescriptionTransport> contents) { public RtpContentMap(Group group, Map<String, DescriptionTransport> contents) {
this.group = group; this.group = group;
this.contents = contents; this.contents = contents;
} }
public static RtpContentMap of(final JinglePacket jinglePacket) { public static RtpContentMap of(final JinglePacket jinglePacket) {
return new RtpContentMap(jinglePacket.getGroup(), DescriptionTransport.of(jinglePacket.getJingleContents())); final Map<String, DescriptionTransport> contents = DescriptionTransport.of(jinglePacket.getJingleContents());
if (isOmemoVerified(contents)) {
return new OmemoVerifiedRtpContentMap(jinglePacket.getGroup(), contents);
} else {
return new RtpContentMap(jinglePacket.getGroup(), contents);
}
}
private static boolean isOmemoVerified(Map<String, DescriptionTransport> contents) {
final Collection<DescriptionTransport> values = contents.values();
if (values.size() == 0) {
return false;
}
for(final DescriptionTransport descriptionTransport : values) {
if (descriptionTransport.transport instanceof OmemoVerifiedIceUdpTransportInfo) {
continue;
}
return false;
}
return true;
} }
public static RtpContentMap of(final SessionDescription sessionDescription) { public static RtpContentMap of(final SessionDescription sessionDescription) {
@ -123,7 +144,7 @@ public class RtpContentMap {
public final RtpDescription description; public final RtpDescription description;
public final IceUdpTransportInfo transport; public final IceUdpTransportInfo transport;
DescriptionTransport(final RtpDescription description, final IceUdpTransportInfo transport) { public DescriptionTransport(final RtpDescription description, final IceUdpTransportInfo transport) {
this.description = description; this.description = description;
this.transport = transport; this.transport = transport;
} }
@ -146,7 +167,10 @@ public class RtpContentMap {
} else { } else {
throw new UnsupportedTransportException("Content does not contain ICE-UDP transport"); throw new UnsupportedTransportException("Content does not contain ICE-UDP transport");
} }
return new DescriptionTransport(rtpDescription, iceUdpTransportInfo); return new DescriptionTransport(
rtpDescription,
OmemoVerifiedIceUdpTransportInfo.upgrade(iceUdpTransportInfo)
);
} }
public static DescriptionTransport of(final SessionDescription sessionDescription, final SessionDescription.Media media) { public static DescriptionTransport of(final SessionDescription sessionDescription, final SessionDescription.Media media) {

View File

@ -24,7 +24,7 @@ public class SessionDescription {
public final static String LINE_DIVIDER = "\r\n"; public final static String LINE_DIVIDER = "\r\n";
private final static String HARDCODED_MEDIA_PROTOCOL = "UDP/TLS/RTP/SAVPF"; //probably only true for DTLS-SRTP aka when we have a fingerprint private final static String HARDCODED_MEDIA_PROTOCOL = "UDP/TLS/RTP/SAVPF"; //probably only true for DTLS-SRTP aka when we have a fingerprint
private final static int HARDCODED_MEDIA_PORT = 9; private final static int HARDCODED_MEDIA_PORT = 9;
private final static String HARDCODED_ICE_OPTIONS = "trickle renomination"; private final static String HARDCODED_ICE_OPTIONS = "trickle";
private final static String HARDCODED_CONNECTION = "IN IP4 0.0.0.0"; private final static String HARDCODED_CONNECTION = "IN IP4 0.0.0.0";
public final int version; public final int version;

View File

@ -22,7 +22,7 @@ import eu.siacs.conversations.xmpp.jingle.SessionDescription;
public class IceUdpTransportInfo extends GenericTransportInfo { public class IceUdpTransportInfo extends GenericTransportInfo {
private IceUdpTransportInfo() { public IceUdpTransportInfo() {
super("transport", Namespace.JINGLE_TRANSPORT_ICE_UDP); super("transport", Namespace.JINGLE_TRANSPORT_ICE_UDP);
} }

View File

@ -0,0 +1,27 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import eu.siacs.conversations.xml.Namespace;
public class OmemoVerifiedIceUdpTransportInfo extends IceUdpTransportInfo {
public void ensureNoPlaintextFingerprint() {
if (this.findChild("fingerprint", Namespace.JINGLE_APPS_DTLS) != null) {
throw new IllegalStateException("OmemoVerifiedIceUdpTransportInfo contains plaintext fingerprint");
}
}
public static IceUdpTransportInfo upgrade(final IceUdpTransportInfo transportInfo) {
if (transportInfo.hasChild("fingerprint", Namespace.JINGLE_APPS_DTLS)) {
return transportInfo;
}
if (transportInfo.hasChild("fingerprint", Namespace.OMEMO_DTLS_SRTP_VERIFICATION)) {
final OmemoVerifiedIceUdpTransportInfo omemoVerifiedIceUdpTransportInfo = new OmemoVerifiedIceUdpTransportInfo();
omemoVerifiedIceUdpTransportInfo.setAttributes(transportInfo.getAttributes());
omemoVerifiedIceUdpTransportInfo.setChildren(transportInfo.getChildren());
return omemoVerifiedIceUdpTransportInfo;
}
return transportInfo;
}
}

View File

@ -0,0 +1,34 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import java.util.List;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
public class Proceed extends Element {
private Proceed() {
super("propose", Namespace.JINGLE_MESSAGE);
}
public static Proceed upgrade(final Element element) {
Preconditions.checkArgument("proceed".equals(element.getName()));
Preconditions.checkArgument(Namespace.JINGLE_MESSAGE.equals(element.getNamespace()));
final Proceed propose = new Proceed();
propose.setAttributes(element.getAttributes());
propose.setChildren(element.getChildren());
return propose;
}
public Integer getDeviceId() {
final Element device = this.findChild("device");
final String id = device == null ? null : device.getAttribute("id");
if (id == null) {
return null;
}
return Ints.tryParse(id);
}
}

View File

@ -73,7 +73,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_margin="24dp" android:layout_marginTop="@dimen/rtp_session_duration_top_margin"
android:textAppearance="@style/TextAppearance.Conversations.Title.Monospace" android:textAppearance="@style/TextAppearance.Conversations.Title.Monospace"
tools:text="01:23" /> tools:text="01:23" />
@ -86,17 +86,14 @@
</RelativeLayout> </RelativeLayout>
<org.webrtc.SurfaceViewRenderer <org.webrtc.SurfaceViewRenderer
android:id="@+id/local_video" android:id="@+id/local_video"
android:layout_width="@dimen/local_video_preview_width" android:layout_width="@dimen/local_video_preview_width"
android:layout_height="@dimen/local_video_preview_height" android:layout_height="@dimen/local_video_preview_height"
android:layout_below="@+id/app_bar_layout" android:layout_below="@+id/app_bar_layout"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:visibility="gone" android:visibility="gone"
app:elevation="4dp" /> app:elevation="4dp" />
@ -106,22 +103,31 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_below="@+id/app_bar_layout" android:layout_below="@+id/app_bar_layout"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:visibility="gone" /> android:visibility="gone" />
<ImageView
android:id="@+id/verified"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/app_bar_layout"
android:layout_alignParentStart="true"
android:layout_marginStart="16dp"
android:layout_marginTop="@dimen/rtp_session_duration_top_margin"
android:alpha="0.7"
android:src="@drawable/ic_verified_fingerprint" />
<ImageView <ImageView
android:id="@+id/pip_local_mic_off_indicator" android:id="@+id/pip_local_mic_off_indicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_margin="8dp" android:layout_margin="8dp"
android:alpha="0.7" android:alpha="0.7"
android:src="@drawable/ic_mic_off_black_24dp" android:src="@drawable/ic_mic_off_black_24dp"
android:tint="@color/white" android:visibility="gone"
android:visibility="gone" /> app:tint="@color/white" />
<RelativeLayout <RelativeLayout
android:id="@+id/button_row" android:id="@+id/button_row"
@ -141,7 +147,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_margin="16dp" android:layout_margin="16dp"
android:src="@drawable/ic_call_end_white_48dp" android:src="@drawable/ic_call_end_white_48dp"
android:visibility="gone" android:visibility="gone"
@ -156,7 +161,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_margin="16dp" android:layout_margin="16dp"
android:src="@drawable/ic_call_white_48dp" android:src="@drawable/ic_call_white_48dp"
@ -176,7 +180,6 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_margin="@dimen/in_call_fab_margin" android:layout_margin="@dimen/in_call_fab_margin"
android:layout_toStartOf="@+id/end_call" android:layout_toStartOf="@+id/end_call"
android:layout_toLeftOf="@+id/end_call"
android:visibility="gone" android:visibility="gone"
app:backgroundTint="?color_background_primary" app:backgroundTint="?color_background_primary"
app:elevation="4dp" app:elevation="4dp"
@ -203,7 +206,6 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_margin="@dimen/in_call_fab_margin" android:layout_margin="@dimen/in_call_fab_margin"
android:layout_toEndOf="@+id/end_call" android:layout_toEndOf="@+id/end_call"
android:layout_toRightOf="@+id/end_call"
android:visibility="gone" android:visibility="gone"
app:backgroundTint="?color_background_primary" app:backgroundTint="?color_background_primary"
app:elevation="4dp" app:elevation="4dp"
@ -217,7 +219,6 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_margin="@dimen/in_call_fab_margin" android:layout_margin="@dimen/in_call_fab_margin"
android:layout_toEndOf="@+id/in_call_action_right" android:layout_toEndOf="@+id/in_call_action_right"
android:layout_toRightOf="@+id/in_call_action_right"
android:visibility="gone" android:visibility="gone"
app:backgroundTint="?color_background_primary" app:backgroundTint="?color_background_primary"
app:elevation="4dp" app:elevation="4dp"

View File

@ -13,11 +13,11 @@
<string name="action_add_phone_book">Tilføj til adressebog</string> <string name="action_add_phone_book">Tilføj til adressebog</string>
<string name="action_delete_contact">Slet fra liste</string> <string name="action_delete_contact">Slet fra liste</string>
<string name="action_block_contact">Blokerer kontakt</string> <string name="action_block_contact">Blokerer kontakt</string>
<string name="action_unblock_contact">Fjern blokering af kontakt</string> <string name="action_unblock_contact">Frigiv kontakt</string>
<string name="action_block_domain">Blokerer domæne</string> <string name="action_block_domain">Blokerer domæne</string>
<string name="action_unblock_domain">Fjern blokering af domæne</string> <string name="action_unblock_domain">Frigiv domæne</string>
<string name="action_block_participant">Bloker deltager</string> <string name="action_block_participant">Bloker deltager</string>
<string name="action_unblock_participant">Fjern blokering af deltager</string> <string name="action_unblock_participant">Frigiv deltager</string>
<string name="title_activity_manage_accounts">Håndter konti</string> <string name="title_activity_manage_accounts">Håndter konti</string>
<string name="title_activity_settings">Indstillinger</string> <string name="title_activity_settings">Indstillinger</string>
<string name="title_activity_sharewith">Del med Conversation</string> <string name="title_activity_sharewith">Del med Conversation</string>
@ -25,7 +25,7 @@
<string name="title_activity_choose_contact">Vælg kontakt</string> <string name="title_activity_choose_contact">Vælg kontakt</string>
<string name="title_activity_choose_contacts">Vælg kontakter</string> <string name="title_activity_choose_contacts">Vælg kontakter</string>
<string name="title_activity_share_via_account">Del via konto</string> <string name="title_activity_share_via_account">Del via konto</string>
<string name="title_activity_block_list">Bloker liste</string> <string name="title_activity_block_list">Blokeringsliste</string>
<string name="just_now">lige nu</string> <string name="just_now">lige nu</string>
<string name="minute_ago">1 minut siden</string> <string name="minute_ago">1 minut siden</string>
<string name="minutes_ago">%d minutter siden</string> <string name="minutes_ago">%d minutter siden</string>
@ -36,8 +36,8 @@
<item quantity="other">%d ulæst samtaler</item> <item quantity="other">%d ulæst samtaler</item>
</plurals> </plurals>
<string name="sending">sender...</string> <string name="sending">sender</string>
<string name="message_decrypting">Dekrypter besked... Vent venligst...</string> <string name="message_decrypting">Dekrypter besked. Vent venligst…</string>
<string name="pgp_message">OpenPGP krypteret besked</string> <string name="pgp_message">OpenPGP krypteret besked</string>
<string name="nick_in_use">Kaldenavn er allerede i brug</string> <string name="nick_in_use">Kaldenavn er allerede i brug</string>
<string name="invalid_muc_nick">Ugyldig kaldenavn</string> <string name="invalid_muc_nick">Ugyldig kaldenavn</string>
@ -47,16 +47,16 @@
<string name="participant">Deltager</string> <string name="participant">Deltager</string>
<string name="visitor">Gæst</string> <string name="visitor">Gæst</string>
<string name="remove_contact_text">Vil du gerne fjerne %s fra din kontaktliste? Samtaler med denne kontakt vil ikke blive slettet.</string> <string name="remove_contact_text">Vil du gerne fjerne %s fra din kontaktliste? Samtaler med denne kontakt vil ikke blive slettet.</string>
<string name="block_contact_text">Vil du gerne bloker %s i at sende dig beskeder?</string> <string name="block_contact_text">Vil du bloker %s i at sende dig beskeder?</string>
<string name="unblock_contact_text">Vil du gerne frigør%s og tillade dem at sende dig beskeder?</string> <string name="unblock_contact_text">Vil du frigive %s og tillade dem at sende dig beskeder?</string>
<string name="block_domain_text">Bloker alle kontakter fra %s?</string> <string name="block_domain_text">Bloker alle kontakter fra %s?</string>
<string name="unblock_domain_text">Fjern blokering af alle kontakter fra %s? </string> <string name="unblock_domain_text">Frigiv alle kontakter fra %s? </string>
<string name="contact_blocked">Kontakt blokeret</string> <string name="contact_blocked">Kontakt blokeret</string>
<string name="blocked">Blokeret</string> <string name="blocked">Blokeret</string>
<string name="remove_bookmark_text">Vil du gerne slette %s som et bogmærke? Samtaler med dette bogmærke vil ikke blive slettet.</string> <string name="remove_bookmark_text">Vil du gerne slette %s som et bogmærke? Samtaler med dette bogmærke vil ikke blive slettet.</string>
<string name="register_account">Register ny konto på server</string> <string name="register_account">Register ny konto på server</string>
<string name="change_password_on_server">Ændr kodeord på server</string> <string name="change_password_on_server">Ændr adgangskode på server</string>
<string name="share_with">Del med...</string> <string name="share_with">Del med</string>
<string name="start_conversation">Start samtale</string> <string name="start_conversation">Start samtale</string>
<string name="invite_contact">Inviter kontakt</string> <string name="invite_contact">Inviter kontakt</string>
<string name="invite">Inviter</string> <string name="invite">Inviter</string>
@ -68,23 +68,23 @@
<string name="edit">Rediger</string> <string name="edit">Rediger</string>
<string name="delete">Slet</string> <string name="delete">Slet</string>
<string name="block">Bloker</string> <string name="block">Bloker</string>
<string name="unblock">Fjern blokering</string> <string name="unblock">Frigiv</string>
<string name="save">Gem</string> <string name="save">Gem</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="crash_report_title">%1$s er kørt fast</string> <string name="crash_report_title">%1$s er kørt fast</string>
<string name="crash_report_message">Brugen af din XMPP konto til at sende \"stack traces\" hjælper den løbende udvikling af %1$s.</string> <string name="crash_report_message">Brugen af din XMPP-konto til at sende \"stack traces\" hjælper den løbende udvikling af %1$s.</string>
<string name="send_now">Send nu</string> <string name="send_now">Send nu</string>
<string name="send_never">Spørg ikke igen</string> <string name="send_never">Spørg ikke igen</string>
<string name="problem_connecting_to_account">Kunne ikke forbinde til konto</string> <string name="problem_connecting_to_account">Kunne ikke forbinde til konto</string>
<string name="problem_connecting_to_accounts">Kunne ikke forbinde til flere konti</string> <string name="problem_connecting_to_accounts">Kunne ikke forbinde til flere konti</string>
<string name="touch_to_fix">Tryk for at håndter dine konti</string> <string name="touch_to_fix">Tryk for at håndter dine konti</string>
<string name="attach_file">Vedhæft fil</string> <string name="attach_file">Vedhæft fil</string>
<string name="not_in_roster">Tilføj den manglede kontakt til din kontaktliste</string> <string name="not_in_roster">Tilføj den manglede kontakt til din kontaktliste?</string>
<string name="add_contact">Tilføj kontakt</string> <string name="add_contact">Tilføj kontakt</string>
<string name="send_failed">levering mislykkedes</string> <string name="send_failed">levering mislykkedes</string>
<string name="preparing_image">Gør klar til at sende billede</string> <string name="preparing_image">Gør klar til at sende billede</string>
<string name="preparing_images">Gør klar til at sende billeder</string> <string name="preparing_images">Gør klar til at sende billeder</string>
<string name="sharing_files_please_wait">Deler filer. Vent venligst...</string> <string name="sharing_files_please_wait">Deler filer. Vent venligst</string>
<string name="action_clear_history">Ryd historik</string> <string name="action_clear_history">Ryd historik</string>
<string name="clear_conversation_history">Ryd samtalehistorik</string> <string name="clear_conversation_history">Ryd samtalehistorik</string>
<string name="clear_histor_msg">Vil du slette alle beskeder i denne samtale?\n\n<b>Advarsel:</b> Dette vil ikke påvirke beskeder gemt på andre enheder eller servere.</string> <string name="clear_histor_msg">Vil du slette alle beskeder i denne samtale?\n\n<b>Advarsel:</b> Dette vil ikke påvirke beskeder gemt på andre enheder eller servere.</string>
@ -94,9 +94,9 @@
<string name="choose_presence">Vælg enhed</string> <string name="choose_presence">Vælg enhed</string>
<string name="send_unencrypted_message">Send ukrypteret besked</string> <string name="send_unencrypted_message">Send ukrypteret besked</string>
<string name="send_message">Send besked</string> <string name="send_message">Send besked</string>
<string name="send_message_to_x">Send besked to %s</string> <string name="send_message_to_x">Send besked til %s</string>
<string name="send_omemo_message">Send OMEMO krypteret besked</string> <string name="send_omemo_message">Send OMEMO-krypteret besked</string>
<string name="send_omemo_x509_message">Send v\\OMEMO krypteret besked</string> <string name="send_omemo_x509_message">Send v\\OMEMO-krypteret besked</string>
<string name="send_pgp_message">Send OpenPGP krypteret besked</string> <string name="send_pgp_message">Send OpenPGP krypteret besked</string>
<string name="your_nick_has_been_changed">Nyt kaldenavn i brug</string> <string name="your_nick_has_been_changed">Nyt kaldenavn i brug</string>
<string name="send_unencrypted">Send ukrypteret</string> <string name="send_unencrypted">Send ukrypteret</string>
@ -106,15 +106,15 @@
<string name="restart">Genstart</string> <string name="restart">Genstart</string>
<string name="install">Installer</string> <string name="install">Installer</string>
<string name="openkeychain_not_installed">Installer venligst OpenKeychain</string> <string name="openkeychain_not_installed">Installer venligst OpenKeychain</string>
<string name="offering">tilbyder...</string> <string name="offering">tilbyder</string>
<string name="waiting">Venter...</string> <string name="waiting">venter…</string>
<string name="no_pgp_key">Ingen Open PGP nøgler fundet</string> <string name="no_pgp_key">Ingen Open PGP nøgler fundet</string>
<string name="contact_has_no_pgp_key">Kunne ikke kryptere din besked, fordi din kontakt ikke annoncerer deres offentlige nøgle.\n\n<small>Bed din kontakt om at konfigurere OpenPGP.</small></string> <string name="contact_has_no_pgp_key">Kunne ikke kryptere din besked, fordi din kontakt ikke annoncerer deres offentlige nøgle.\n\n<small>Bed din kontakt om at konfigurere OpenPGP.</small></string>
<string name="no_pgp_keys">Ingen OpenPGP nøgler fundet</string> <string name="no_pgp_keys">Ingen OpenPGP nøgler fundet</string>
<string name="contacts_have_no_pgp_keys">Kunne ikke kryptere din besked, fordi din kontakt ikke annoncerer deres offentlige nøgle.\n\n1<small>Bed dem om at oprette OpenPGP.</small></string> <string name="contacts_have_no_pgp_keys">Kunne ikke kryptere din besked, fordi din kontakt ikke annoncerer deres offentlige nøgle.\n\n1<small>Bed dem om at optte OpenPGP.</small></string>
<string name="pref_general">Generel</string> <string name="pref_general">Generel</string>
<string name="pref_accept_files">Accepter filer</string> <string name="pref_accept_files">Accepter filer</string>
<string name="pref_accept_files_summary">Accepter automatisk filer mindre end...</string> <string name="pref_accept_files_summary">Accepter automatisk filer mindre end</string>
<string name="pref_attachments">Vedhæftninger</string> <string name="pref_attachments">Vedhæftninger</string>
<string name="pref_notification_settings">Notifikation</string> <string name="pref_notification_settings">Notifikation</string>
<string name="pref_vibrate">Vibrer</string> <string name="pref_vibrate">Vibrer</string>
@ -125,7 +125,7 @@
<string name="pref_notification_sound">Notifikationslyd</string> <string name="pref_notification_sound">Notifikationslyd</string>
<string name="pref_notification_sound_summary">Lydnotifikation for nye beskeder</string> <string name="pref_notification_sound_summary">Lydnotifikation for nye beskeder</string>
<string name="pref_call_ringtone_summary">Ringetone for indkommende opkald</string> <string name="pref_call_ringtone_summary">Ringetone for indkommende opkald</string>
<string name="pref_notification_grace_period">Grace periode</string> <string name="pref_notification_grace_period">Fredningsperiode</string>
<string name="pref_notification_grace_period_summary">Tidsintervallet hvor notifikationer er lydløs efter at have registreret aktivitet på en af dine andre enheder.</string> <string name="pref_notification_grace_period_summary">Tidsintervallet hvor notifikationer er lydløs efter at have registreret aktivitet på en af dine andre enheder.</string>
<string name="pref_advanced_options">Advanceret</string> <string name="pref_advanced_options">Advanceret</string>
<string name="pref_never_send_crash">Send aldrig fejlrapporter</string> <string name="pref_never_send_crash">Send aldrig fejlrapporter</string>
@ -139,7 +139,7 @@
<string name="error">Der er sket en fejl</string> <string name="error">Der er sket en fejl</string>
<string name="recording_error">Fejl</string> <string name="recording_error">Fejl</string>
<string name="your_account">Din konto</string> <string name="your_account">Din konto</string>
<string name="send_presence_updates">Send nærværelse opdateringer</string> <string name="send_presence_updates">Send nærværsopdateringer</string>
<string name="receive_presence_updates">Modtag nærværsopdateringer</string> <string name="receive_presence_updates">Modtag nærværsopdateringer</string>
<string name="ask_for_presence_updates">Bed om nærværsopdateringer</string> <string name="ask_for_presence_updates">Bed om nærværsopdateringer</string>
<string name="attach_choose_picture">Vælg billede</string> <string name="attach_choose_picture">Vælg billede</string>
@ -148,7 +148,7 @@
<string name="error_not_an_image_file">Den valgte fil er ikke et billede</string> <string name="error_not_an_image_file">Den valgte fil er ikke et billede</string>
<string name="error_compressing_image">Kunne ikke konverter billedefil</string> <string name="error_compressing_image">Kunne ikke konverter billedefil</string>
<string name="error_file_not_found">Fil ikke fundet</string> <string name="error_file_not_found">Fil ikke fundet</string>
<string name="error_io_exception">General I/O fejl. Måske er kørt tør for lagerplads?</string> <string name="error_io_exception">General I/O fejl. Måske er du kørt tør for lagerplads?</string>
<string name="error_security_exception_during_image_copy">Appen du brugte til at vælge dette billede havde ikke tilstrækkelig tilladelse til at læse filen.\n\n<small>Brug en anden filmanager til at vælge et billede</small>.</string> <string name="error_security_exception_during_image_copy">Appen du brugte til at vælge dette billede havde ikke tilstrækkelig tilladelse til at læse filen.\n\n<small>Brug en anden filmanager til at vælge et billede</small>.</string>
<string name="account_status_unknown">Ukendt</string> <string name="account_status_unknown">Ukendt</string>
<string name="account_status_disabled">Midlertidigt deaktiveret</string> <string name="account_status_disabled">Midlertidigt deaktiveret</string>
@ -183,11 +183,11 @@
<string name="mgmt_account_are_you_sure">Er du sikker?</string> <string name="mgmt_account_are_you_sure">Er du sikker?</string>
<string name="mgmt_account_delete_confirm_text">Sletning af din konto sletter hele din samtalehistorik</string> <string name="mgmt_account_delete_confirm_text">Sletning af din konto sletter hele din samtalehistorik</string>
<string name="attach_record_voice">Optag lyd</string> <string name="attach_record_voice">Optag lyd</string>
<string name="account_settings_jabber_id">XMPP adresse</string> <string name="account_settings_jabber_id">XMPP-adresse</string>
<string name="block_jabber_id">Bloker XMPP adresse</string> <string name="block_jabber_id">Bloker XMPP-adresse</string>
<string name="account_settings_example_jabber_id">brugernavn@domæne.dk</string> <string name="account_settings_example_jabber_id">brugernavn@domæne.dk</string>
<string name="password">Adgangskode</string> <string name="password">Adgangskode</string>
<string name="invalid_jid">Dette er ikke en gyldig XMPP adresse</string> <string name="invalid_jid">Dette er ikke en gyldig XMPP-adresse</string>
<string name="error_out_of_memory">Kørt tør for hukommelse. Billedet for stort</string> <string name="error_out_of_memory">Kørt tør for hukommelse. Billedet for stort</string>
<string name="add_phone_book_text">Vil du tilføje %s til din adressebog?</string> <string name="add_phone_book_text">Vil du tilføje %s til din adressebog?</string>
<string name="server_info_show_more">Server info</string> <string name="server_info_show_more">Server info</string>
@ -201,8 +201,8 @@
<string name="server_info_pep">XEP-0163: PEP (Avatars / OMEMO)</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_http_upload">XEP-0363: HTTP File Upload</string>
<string name="server_info_push">XEP-0357: Push</string> <string name="server_info_push">XEP-0357: Push</string>
<string name="server_info_available">Ledig</string> <string name="server_info_available">understøttet</string>
<string name="server_info_unavailable">Utilgængelig</string> <string name="server_info_unavailable">utilgængelig</string>
<string name="missing_public_keys">Ingen meddelelser om offentlige nøgler</string> <string name="missing_public_keys">Ingen meddelelser om offentlige nøgler</string>
<string name="last_seen_now">sidst set lige nu</string> <string name="last_seen_now">sidst set lige nu</string>
<string name="last_seen_min">sidst set for et minut siden</string> <string name="last_seen_min">sidst set for et minut siden</string>
@ -211,16 +211,16 @@
<string name="last_seen_hours">sidst set %d time siden</string> <string name="last_seen_hours">sidst set %d time siden</string>
<string name="last_seen_day">sidst set for en dag siden</string> <string name="last_seen_day">sidst set for en dag siden</string>
<string name="last_seen_days">sidst set %d dage siden</string> <string name="last_seen_days">sidst set %d dage siden</string>
<string name="install_openkeychain">Krypteret besked. Installer venligst OpenKeychain for dekryptere den?</string> <string name="install_openkeychain">Krypteret besked. Installer venligst OpenKeychain for dekryptere den.</string>
<string name="openpgp_messages_found">Ny OpenPGP krypteret beskeder fundet</string> <string name="openpgp_messages_found">Ny OpenPGP krypteret beskeder fundet</string>
<string name="openpgp_key_id">OpenPGP nøgle ID</string> <string name="openpgp_key_id">OpenPGP nøgle ID</string>
<string name="omemo_fingerprint">OMEMO fingeraftryk</string> <string name="omemo_fingerprint">OMEMO-fingeraftryk</string>
<string name="omemo_fingerprint_x509">v\\OMEMO fingeraftryk</string> <string name="omemo_fingerprint_x509">v\\OMEMO-fingeraftryk</string>
<string name="omemo_fingerprint_selected_message">OMEMO fingeraftryk (beskedoprindelse)</string> <string name="omemo_fingerprint_selected_message">OMEMO-fingeraftryk (beskedoprindelse)</string>
<string name="omemo_fingerprint_x509_selected_message">v\\OMEMO fingeraftryk (beskedoprindelse)</string> <string name="omemo_fingerprint_x509_selected_message">v\\OMEMO-fingeraftryk (beskedoprindelse)</string>
<string name="other_devices">Andre enheder</string> <string name="other_devices">Andre enheder</string>
<string name="trust_omemo_fingerprints">Stol på OMEMO fingeraftryk</string> <string name="trust_omemo_fingerprints">Stol på OMEMO-fingeraftryk</string>
<string name="fetching_keys">Henter nøgler...</string> <string name="fetching_keys">Henter nøgler</string>
<string name="done">Færdig</string> <string name="done">Færdig</string>
<string name="decrypt">Dekrypter</string> <string name="decrypt">Dekrypter</string>
<string name="bookmarks">Bogmærker</string> <string name="bookmarks">Bogmærker</string>
@ -228,8 +228,8 @@
<string name="enter_contact">Indtast kontakt</string> <string name="enter_contact">Indtast kontakt</string>
<string name="delete_contact">Slet kontakt</string> <string name="delete_contact">Slet kontakt</string>
<string name="view_contact_details">Vis kontaktdetaljer</string> <string name="view_contact_details">Vis kontaktdetaljer</string>
<string name="block_contact">Blokér kontakt</string> <string name="block_contact">Bloker kontakt</string>
<string name="unblock_contact">Løslad kontakt</string> <string name="unblock_contact">Frigiv kontakt</string>
<string name="create">Opret</string> <string name="create">Opret</string>
<string name="select">Vælg</string> <string name="select">Vælg</string>
<string name="contact_already_exists">Denne kontakt findes allerede</string> <string name="contact_already_exists">Denne kontakt findes allerede</string>
@ -241,22 +241,22 @@
<string name="destroy_room">Slet gruppechat</string> <string name="destroy_room">Slet gruppechat</string>
<string name="destroy_channel">Slet kanal</string> <string name="destroy_channel">Slet kanal</string>
<string name="destroy_room_dialog">Er du sikker på du vil slette denne gruppechat?\n\n <b> Advarsel:</b> Gruppechatten fjernes fuldstændigt på serveren.</string> <string name="destroy_room_dialog">Er du sikker på du vil slette denne gruppechat?\n\n <b> Advarsel:</b> Gruppechatten fjernes fuldstændigt på serveren.</string>
<string name="destroy_channel_dialog">Er du sikker på, at du vil ødelægge denne offentlige kanal?\n\n<b>Advarsel:</b> Kanalen fjernes fuldstændigt på serveren.</string> <string name="destroy_channel_dialog">Er du sikker på, at du vil slette denne offentlige kanal?\n\n<b>Advarsel:</b> Kanalen fjernes fuldstændigt på serveren.</string>
<string name="could_not_destroy_room">Kunne ikke slette gruppechat</string> <string name="could_not_destroy_room">Kunne ikke slette gruppechat</string>
<string name="could_not_destroy_channel">Kunne ikke slette kanal</string> <string name="could_not_destroy_channel">Kunne ikke slette kanal</string>
<string name="action_edit_subject">Rediger titel på gruppechat</string> <string name="action_edit_subject">Rediger titel på gruppechat</string>
<string name="topic">Emne</string> <string name="topic">Emne</string>
<string name="joining_conference">Deltager i gruppechat...</string> <string name="joining_conference">Deltager i gruppechat</string>
<string name="leave">Forlad</string> <string name="leave">Forlad</string>
<string name="contact_added_you">Kontakt tilføjede dig til kontaktliste</string> <string name="contact_added_you">Kontakt tilføjede dig til kontaktliste</string>
<string name="add_back">Tilføj tilbage</string> <string name="add_back">Tilføj tilbage</string>
<string name="contact_has_read_up_to_this_point">%s har læst op til dette punkt</string> <string name="contact_has_read_up_to_this_point">%s har læst hertil</string>
<string name="contacts_have_read_up_to_this_point">%s har læst hertil</string> <string name="contacts_have_read_up_to_this_point">%s har læst hertil</string>
<string name="contacts_and_n_more_have_read_up_to_this_point">%1$s +%2$d andre har læst hertil</string> <string name="contacts_and_n_more_have_read_up_to_this_point">%1$s +%2$d andre har læst hertil</string>
<string name="everyone_has_read_up_to_this_point">Alle har læst op til dette punkt</string> <string name="everyone_has_read_up_to_this_point">Alle har læst hertil</string>
<string name="publish">Offentliggør </string> <string name="publish">Offentliggør </string>
<string name="touch_to_choose_picture">Tryk på avatar for at vælge billede fra galleri</string> <string name="touch_to_choose_picture">Tryk på avatar for at vælge billede fra galleri</string>
<string name="publishing">Offentliggørelse...</string> <string name="publishing">Offentliggørelse</string>
<string name="error_publish_avatar_server_reject">Serveren afviste din offentliggørelse</string> <string name="error_publish_avatar_server_reject">Serveren afviste din offentliggørelse</string>
<string name="error_publish_avatar_converting">Kunne ikke konverter dit billede</string> <string name="error_publish_avatar_converting">Kunne ikke konverter dit billede</string>
<string name="error_saving_avatar">Kunne ikke gemme avatar til disk</string> <string name="error_saving_avatar">Kunne ikke gemme avatar til disk</string>
@ -272,47 +272,47 @@
<string name="skip">Skip</string> <string name="skip">Skip</string>
<string name="disable_notifications">Deaktiver notifikationer</string> <string name="disable_notifications">Deaktiver notifikationer</string>
<string name="enable">Aktiver</string> <string name="enable">Aktiver</string>
<string name="conference_requires_password">Gruppechat kræver kodeord</string> <string name="conference_requires_password">Gruppechat kræver adgangskode</string>
<string name="enter_password">Indtast kodeord</string> <string name="enter_password">Indtast adgangskode</string>
<string name="request_presence_updates">Bed først om tilstedeværelsesopdateringer fra din kontakt.\n\n<small>Dette bruges til at bestemme, hvilken chat-app din kontakt bruger.</small></string> <string name="request_presence_updates">Bed først om nærværsopdateringer fra din kontakt.\n\n<small>Dette bruges til at bestemme, hvilken chat-app din kontakt bruger.</small></string>
<string name="request_now">Anmod nu</string> <string name="request_now">Anmod nu</string>
<string name="ignore">Ignore</string> <string name="ignore">Ignore</string>
<string name="without_mutual_presence_updates"><b>Advarsel:</b> Afsendelse af dette uden gensidig nærværsopdatering kan forårsage uventede problemer.\n\n <small>Gå til \"Kontaktoplysninger\" for at bekræfte dine nærværsabonnementer.</small></string> <string name="without_mutual_presence_updates"><b>Advarsel:</b> Afsendelse af dette uden gensidig nærværsopdatering kan forårsage uventede problemer.\n\n<small>Gå til \"Kontaktdetaljer\" for at bekræfte dine nærværsabonnementer.</small></string>
<string name="pref_security_settings">Sikkerhed</string> <string name="pref_security_settings">Sikkerhed</string>
<string name="pref_allow_message_correction">Tillad rettelse af beskeder</string> <string name="pref_allow_message_correction">Tillad rettelse af beskeder</string>
<string name="pref_allow_message_correction_summary">Tillad dine kontakter at redigere deres beskder med tilbagevirkende kraft</string> <string name="pref_allow_message_correction_summary">Tillad dine kontakter at redigere deres beskeder med tilbagevirkende kraft</string>
<string name="pref_expert_options">Ekspert indstillinger</string> <string name="pref_expert_options">Ekspert indstillinger</string>
<string name="pref_expert_options_summary">Være venligst forsigtig med disse</string> <string name="pref_expert_options_summary">Være forsigtig med at ændre i disse</string>
<string name="title_activity_about_x">Om %s</string> <string name="title_activity_about_x">Om %s</string>
<string name="title_pref_quiet_hours">Stilletid</string> <string name="title_pref_quiet_hours">Stilletid</string>
<string name="title_pref_quiet_hours_start_time">Starttidspunkt</string> <string name="title_pref_quiet_hours_start_time">Starttidspunkt</string>
<string name="title_pref_quiet_hours_end_time">Sluttidspunkt</string> <string name="title_pref_quiet_hours_end_time">Sluttidspunkt</string>
<string name="title_pref_enable_quiet_hours">Aktiver lydløs timer</string> <string name="title_pref_enable_quiet_hours">Aktiver stilletid</string>
<string name="pref_quiet_hours_summary">Notifikationer vil være lydløs under stilletid</string> <string name="pref_quiet_hours_summary">Notifikationer vil være lydløs under stilletid</string>
<string name="pref_expert_options_other">Andre</string> <string name="pref_expert_options_other">Andre</string>
<string name="pref_autojoin">Synkroniser med bogmærker</string> <string name="pref_autojoin">Synkroniser med bogmærker</string>
<string name="pref_autojoin_summary">Deltag automatisk i gruppechat hvis bogmærket tillader det</string> <string name="pref_autojoin_summary">Deltag automatisk i gruppechat hvis bogmærket tillader det</string>
<string name="toast_message_omemo_fingerprint">OMEMO fingeraftryk kopieret til udklipsholder</string> <string name="toast_message_omemo_fingerprint">OMEMO-fingeraftryk kopieret til udklipsholder</string>
<string name="conference_banned">Du er udelukket fra denne gruppechat</string> <string name="conference_banned">Du er udelukket fra denne gruppechat</string>
<string name="conference_members_only">Denne gruppechat er kun for medlemmer</string> <string name="conference_members_only">Denne gruppechat er kun for medlemmer</string>
<string name="conference_resource_constraint">Ressourcebegrænsning</string> <string name="conference_resource_constraint">Ressourcebegrænsning</string>
<string name="conference_kicked">Du er blevet smidt ud af denne gruppechat</string> <string name="conference_kicked">Du er blevet smidt ud af denne gruppechat</string>
<string name="conference_shutdown">Gruppechat er lukket ned</string> <string name="conference_shutdown">Gruppechatten er lukket ned</string>
<string name="conference_unknown_error">Du er ikke længere i denne gruppechat</string> <string name="conference_unknown_error">Du er ikke længere i denne gruppechat</string>
<string name="using_account">bruger konto %s</string> <string name="using_account">anvender konto %s</string>
<string name="hosted_on">Hostet på %s</string> <string name="hosted_on">hostet på %s</string>
<string name="checking_x">Tjekker %s på HTTP vært</string> <string name="checking_x">Tjekker %s på HTTP vært</string>
<string name="not_connected_try_again">Du er ikke forbundet. Prøv igen senere</string> <string name="not_connected_try_again">Du er ikke forbundet. Prøv igen senere</string>
<string name="check_x_filesize">Tjek %s størrelse</string> <string name="check_x_filesize">Tjek %s størrelse</string>
<string name="check_x_filesize_on_host">Tjek %1$s størrelse pp %2$s</string> <string name="check_x_filesize_on_host">Tjek %1$s størrelse på %2$s</string>
<string name="message_options">Beskedindstillinger</string> <string name="message_options">Beskedvalg</string>
<string name="quote">Citat</string> <string name="quote">Citat</string>
<string name="paste_as_quote">Indsæt som citat</string> <string name="paste_as_quote">Indsæt som citat</string>
<string name="copy_original_url">Kopier original URL</string> <string name="copy_original_url">Kopier original URL</string>
<string name="send_again">Send igen</string> <string name="send_again">Send igen</string>
<string name="file_url">Fil URL</string> <string name="file_url">Fil URL</string>
<string name="url_copied_to_clipboard">Kopieret URL til udklipsholder</string> <string name="url_copied_to_clipboard">Kopieret URL til udklipsholder</string>
<string name="jabber_id_copied_to_clipboard">Kopieret XMPP adresse til udklipsholder</string> <string name="jabber_id_copied_to_clipboard">Kopieret XMPP-adresse til udklipsholder</string>
<string name="error_message_copied_to_clipboard">Kopieret fejlmeddelelse til udklipsholder</string> <string name="error_message_copied_to_clipboard">Kopieret fejlmeddelelse til udklipsholder</string>
<string name="web_address">webadresse</string> <string name="web_address">webadresse</string>
<string name="scan_qr_code">Skan 2D stregkode</string> <string name="scan_qr_code">Skan 2D stregkode</string>
@ -328,8 +328,8 @@
<string name="notification_create_backup_title">Opretter backup filer</string> <string name="notification_create_backup_title">Opretter backup filer</string>
<string name="notification_backup_created_title">Din backup er oprettet</string> <string name="notification_backup_created_title">Din backup er oprettet</string>
<string name="notification_backup_created_subtitle">Backup filerne er blevet gem i %s</string> <string name="notification_backup_created_subtitle">Backup filerne er blevet gem i %s</string>
<string name="restoring_backup">Gendan sikkerhedskopi</string> <string name="restoring_backup">Gendan backup</string>
<string name="notification_restored_backup_title">Din sikkerhedskopi er blevet gendannet</string> <string name="notification_restored_backup_title">Din backup er blevet gendannet</string>
<string name="notification_restored_backup_subtitle">Glem ikke at aktivere kontoen.</string> <string name="notification_restored_backup_subtitle">Glem ikke at aktivere kontoen.</string>
<string name="choose_file">Vælg fil</string> <string name="choose_file">Vælg fil</string>
<string name="receiving_x_file">Modtager %1$s (%2$d%% fuldført)</string> <string name="receiving_x_file">Modtager %1$s (%2$d%% fuldført)</string>
@ -337,9 +337,9 @@
<string name="delete_x_file">Slet %s</string> <string name="delete_x_file">Slet %s</string>
<string name="file">fil</string> <string name="file">fil</string>
<string name="open_x_file">Åben %s</string> <string name="open_x_file">Åben %s</string>
<string name="sending_file">Sender (%1$d%% fuldført)</string> <string name="sending_file">sender (%1$d%% fuldført)</string>
<string name="preparing_file">Gør klar til at dele fil</string> <string name="preparing_file">Gør klar til at dele fil</string>
<string name="x_file_offered_for_download">%s kan hentes ned</string> <string name="x_file_offered_for_download">%s kan downloades</string>
<string name="cancel_transmission">Annuller overførsel</string> <string name="cancel_transmission">Annuller overførsel</string>
<string name="file_transmission_failed">kunne ikke dele fil</string> <string name="file_transmission_failed">kunne ikke dele fil</string>
<string name="file_transmission_cancelled">fil overførsel annulleret </string> <string name="file_transmission_cancelled">fil overførsel annulleret </string>
@ -347,28 +347,28 @@
<string name="no_application_found_to_open_file">Ingen app fundet der kan åbne filen</string> <string name="no_application_found_to_open_file">Ingen app fundet der kan åbne filen</string>
<string name="no_application_found_to_open_link">Ingen app fundet der kan åbne link</string> <string name="no_application_found_to_open_link">Ingen app fundet der kan åbne link</string>
<string name="no_application_found_to_view_contact">Ingen app fundet der kan vise kontakt</string> <string name="no_application_found_to_view_contact">Ingen app fundet der kan vise kontakt</string>
<string name="pref_show_dynamic_tags">Dynamisk Mærker</string> <string name="pref_show_dynamic_tags">Dynamiske Mærker</string>
<string name="pref_show_dynamic_tags_summary">Vis skrivebeskyttede mærker under kontakter</string> <string name="pref_show_dynamic_tags_summary">Vis skrivebeskyttet mærker under kontakter</string>
<string name="enable_notifications">Aktiver notifikationer</string> <string name="enable_notifications">Aktiver notifikationer</string>
<string name="no_conference_server_found">Ingen gruppechat server fundet</string> <string name="no_conference_server_found">Ingen gruppechat server fundet</string>
<string name="conference_creation_failed">Kunne ikke oprette gruppechat</string> <string name="conference_creation_failed">Kunne ikke oprette gruppechat</string>
<string name="account_image_description">Konto avatar</string> <string name="account_image_description">Konto avatar</string>
<string name="copy_omemo_clipboard_description">Kopier OMEMO fingeraftryk til udklipsholder</string> <string name="copy_omemo_clipboard_description">Kopier OMEMO-fingeraftryk til udklipsholder</string>
<string name="regenerate_omemo_key">Gendan OMEMO nøgle</string> <string name="regenerate_omemo_key">Gendan OMEMO-nøgle</string>
<string name="clear_other_devices">Ryd enheder</string> <string name="clear_other_devices">Ryd enheder</string>
<string name="clear_other_devices_desc">Er du sikker på, at du vil rydde alle andre enheder fra OMEMO-meddelelsen? Næste gang dine enheder opretter forbindelse, annoncerer de sig selv, men de modtager muligvis ikke beskeder sendt i mellemtiden.</string> <string name="clear_other_devices_desc">Er du sikker på, at du vil rydde alle andre enheder fra OMEMO-meddelelsen? Næste gang dine enheder opretter forbindelse, annoncerer de sig selv, men de modtager muligvis ikke beskeder sendt i mellemtiden.</string>
<string name="error_no_keys_to_trust_server_error">Der er ingen brugbare nøgler til rådighed for denne kontakt.\nKunne ikke hente nye nøgler fra serveren. Måske er der noget galt med din kontakts server?</string> <string name="error_no_keys_to_trust_server_error">Der er ingen brugbare nøgler til rådighed for denne kontakt.\nKunne ikke hente nye nøgler fra serveren. Måske er der noget galt med din kontakts server?</string>
<string name="error_no_keys_to_trust_presence">Der er ingen tilgængelige nøgler til denne kontakt.\nSørg for, at I begge har nærværsabonnement.</string> <string name="error_no_keys_to_trust_presence">Der er ingen tilgængelige nøgler til denne kontakt.\nSørg for, at I begge har nærværsabonnement.</string>
<string name="error_trustkeys_title">Noget gik galt</string> <string name="error_trustkeys_title">Noget gik galt</string>
<string name="fetching_history_from_server">Henter historik fra server</string> <string name="fetching_history_from_server">Henter historik fra server</string>
<string name="no_more_history_on_server">Ikke mere historik på server</string> <string name="no_more_history_on_server">Der er ikke mere historik på server</string>
<string name="updating">Opdater...</string> <string name="updating">Opdater</string>
<string name="password_changed">Kodeord ændret!</string> <string name="password_changed">Adgangskode ændret!</string>
<string name="could_not_change_password">Kunne ikke ændre kodeord</string> <string name="could_not_change_password">Kunne ikke ændre adgangskode</string>
<string name="change_password">Ændr kodeord</string> <string name="change_password">Ændr adgangskode</string>
<string name="current_password">Nuværende kodeord</string> <string name="current_password">Nuværende adgangskode</string>
<string name="new_password">Nyt kodeord</string> <string name="new_password">Ny adgangskode</string>
<string name="password_should_not_be_empty">Kodeordet kan ikke være tomt</string> <string name="password_should_not_be_empty">Adgangskode kan ikke være tomt</string>
<string name="enable_all_accounts">Aktiver alle konti</string> <string name="enable_all_accounts">Aktiver alle konti</string>
<string name="disable_all_accounts">Deaktiver alle konti</string> <string name="disable_all_accounts">Deaktiver alle konti</string>
<string name="perform_action_with">Udfør handling med</string> <string name="perform_action_with">Udfør handling med</string>
@ -394,7 +394,7 @@
<string name="conference_options">Privat gruppechatkonfiguration</string> <string name="conference_options">Privat gruppechatkonfiguration</string>
<string name="channel_options">Konfiguration af offentlig kanal</string> <string name="channel_options">Konfiguration af offentlig kanal</string>
<string name="members_only">Privat, kun medlemmer</string> <string name="members_only">Privat, kun medlemmer</string>
<string name="non_anonymous">Gør XMPP adresser synlig for alle</string> <string name="non_anonymous">Gør XMPP-adresser synlig for alle</string>
<string name="moderated">Moderere kanal</string> <string name="moderated">Moderere kanal</string>
<string name="you_are_not_participating">Du deltager ikke</string> <string name="you_are_not_participating">Du deltager ikke</string>
<string name="modified_conference_options">Ændrede gruppechat valg!</string> <string name="modified_conference_options">Ændrede gruppechat valg!</string>
@ -419,9 +419,9 @@
<string name="sending_x_file">Sender %s</string> <string name="sending_x_file">Sender %s</string>
<string name="offering_x_file">Tilbyder %s</string> <string name="offering_x_file">Tilbyder %s</string>
<string name="hide_offline">Skjul offline</string> <string name="hide_offline">Skjul offline</string>
<string name="contact_is_typing">%s skriver...</string> <string name="contact_is_typing">%s skriver</string>
<string name="contact_has_stopped_typing">%s er holdt op med at skrive</string> <string name="contact_has_stopped_typing">%s skriver ikke mere</string>
<string name="contacts_are_typing">%s skriver...</string> <string name="contacts_are_typing">%s skriver</string>
<string name="contacts_have_stopped_typing">%s har stoppet skrivning</string> <string name="contacts_have_stopped_typing">%s har stoppet skrivning</string>
<string name="pref_chat_states">Indtastningsnotifikation</string> <string name="pref_chat_states">Indtastningsnotifikation</string>
<string name="pref_chat_states_summary">Lad dine kontakter vide når du skriver beskeder til dem</string> <string name="pref_chat_states_summary">Lad dine kontakter vide når du skriver beskeder til dem</string>
@ -465,8 +465,8 @@
<string name="account_status_host_unknown">Serveren er ikke ansvarlig for dette domæne</string> <string name="account_status_host_unknown">Serveren er ikke ansvarlig for dette domæne</string>
<string name="server_info_broken">Brudt</string> <string name="server_info_broken">Brudt</string>
<string name="pref_presence_settings">Tilgængelighed</string> <string name="pref_presence_settings">Tilgængelighed</string>
<string name="pref_away_when_screen_off">Ej tilstede når enhed er låst</string> <string name="pref_away_when_screen_off">Væk når enhed er låst</string>
<string name="pref_away_when_screen_off_summary">Ej tilstede når enheden er låst</string> <string name="pref_away_when_screen_off_summary">Vis som Væk når enheden er låst</string>
<string name="pref_dnd_on_silent_mode">Optaget i lydløs tilstand</string> <string name="pref_dnd_on_silent_mode">Optaget i lydløs tilstand</string>
<string name="pref_dnd_on_silent_mode_summary">Vis som Optaget når enhed er i lydløs tilstand</string> <string name="pref_dnd_on_silent_mode_summary">Vis som Optaget når enhed er i lydløs tilstand</string>
<string name="pref_treat_vibrate_as_silent">Behandl vibration som lydløs tilstand</string> <string name="pref_treat_vibrate_as_silent">Behandl vibration som lydløs tilstand</string>
@ -493,7 +493,7 @@
<string name="pref_use_tor_summary">Send alle forbindelser gennem Tor-netværket. Kræver Orbot</string> <string name="pref_use_tor_summary">Send alle forbindelser gennem Tor-netværket. Kræver Orbot</string>
<string name="account_settings_hostname">Værtsnavn</string> <string name="account_settings_hostname">Værtsnavn</string>
<string name="account_settings_port">Port</string> <string name="account_settings_port">Port</string>
<string name="hostname_or_onion">server- eller onion-adresse</string> <string name="hostname_or_onion">Server- eller onion-adresse</string>
<string name="not_a_valid_port">Dette er ikke en gyldigt port-nummer</string> <string name="not_a_valid_port">Dette er ikke en gyldigt port-nummer</string>
<string name="not_valid_hostname">Dette er ikke et gyldigt værtsnavn</string> <string name="not_valid_hostname">Dette er ikke et gyldigt værtsnavn</string>
<string name="connected_accounts">%1$d af %2$d konti forbundet</string> <string name="connected_accounts">%1$d af %2$d konti forbundet</string>
@ -509,7 +509,8 @@
<string name="no_storage_permission">Giv %1$s adgang til ekstern lagerplads</string> <string name="no_storage_permission">Giv %1$s adgang til ekstern lagerplads</string>
<string name="no_camera_permission">Giv %1$s adgang til kameraet</string> <string name="no_camera_permission">Giv %1$s adgang til kameraet</string>
<string name="sync_with_contacts">Synkroniser med kontakter</string> <string name="sync_with_contacts">Synkroniser med kontakter</string>
<string name="sync_with_contacts_quicksy"><![CDATA[Quicksy har brug for adgang til dine kontaktpersons telefonnumre for at komme med forslag til mulige kontakter, der allerede er på Quicksy.<br><br>Vi gemmer ikke en kopi af disse telefonnumre.\n\nFor mere information, læs vores <a href=\"https://quicksy.im/#privacy\">privatlivspolitik</a>.<br><br>Du vil bedes nu om at give tilladelse til at få adgang til dine kontakter.]]></string> <string name="sync_with_contacts_long">%1$s ønsker tilladelse til at få adgang til din adressebog for at matche den med din XMPP kontaktliste.\nDette vil vise dine kontakters fulde navne og avatarer.\n\n%1$s læser kun din adressebog og matcher den lokalt uden at uploade noget til din server.</string>
<string name="sync_with_contacts_quicksy"><![CDATA[Quicksy har brug for adgang til dine kontaktpersons telefonnumre for at komme med forslag til mulige kontakter, der allerede er på Quicksy.<br><br>Vi gemmer ikke en kopi af disse telefonnumre.\n\nFor mere information, læs vores <a href="https://quicksy.im/#privacy">privatlivspolitik</a>.<br><br>Du vil bedes nu om at give tilladelse til at få adgang til dine kontakter.]]></string>
<string name="notify_on_all_messages">Underret ved alle beskeder</string> <string name="notify_on_all_messages">Underret ved alle beskeder</string>
<string name="notify_only_when_highlighted">Underret kun når nævnt</string> <string name="notify_only_when_highlighted">Underret kun når nævnt</string>
<string name="notify_never">Notifikationer deaktiveret</string> <string name="notify_never">Notifikationer deaktiveret</string>
@ -519,9 +520,8 @@
<string name="always">Altid</string> <string name="always">Altid</string>
<string name="large_images_only">Kun store billeder</string> <string name="large_images_only">Kun store billeder</string>
<string name="battery_optimizations_enabled">Batterioptimering aktiveret</string> <string name="battery_optimizations_enabled">Batterioptimering aktiveret</string>
<string name="battery_optimizations_enabled_explained">  <string name="battery_optimizations_enabled_explained">Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsinkede notifikationer eller tab af beskeder.\nDet er anbefalet at slå dem fra.</string>
Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsinkede notifikationer eller endda tab af beskeder.\nDet er anbefalet at slå dem fra.</string> <string name="battery_optimizations_enabled_dialog">Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsinkede notifikationer eller tab af beskeder.\n\nDu bliver nu bedt om at deaktivere dem.</string>
<string name="battery_optimizations_enabled_dialog">Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsinkede notifikationer eller endda tab af beskeder.\n\ nDu bliver nu bedt om at deaktivere dem.</string>
<string name="disable">Deaktiver</string> <string name="disable">Deaktiver</string>
<string name="selection_too_large">Det valgte område er for stort</string> <string name="selection_too_large">Det valgte område er for stort</string>
<string name="no_accounts">(Ingen aktiverede konti)</string> <string name="no_accounts">(Ingen aktiverede konti)</string>
@ -532,19 +532,20 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="this_account_is_disabled">Du har deaktiveret denne konto</string> <string name="this_account_is_disabled">Du har deaktiveret denne konto</string>
<string name="security_error_invalid_file_access">Sikkerhedsfejl: Ugyldig filadgang!</string> <string name="security_error_invalid_file_access">Sikkerhedsfejl: Ugyldig filadgang!</string>
<string name="no_application_to_share_uri">Ingen app fundet der kan dele URL</string> <string name="no_application_to_share_uri">Ingen app fundet der kan dele URL</string>
<string name="share_uri_with">Del URL med...</string> <string name="share_uri_with">Del URL med…</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy er en klone af den populære XMPP klient samtaler med automatisk kontaktopdagelse.<br><br>Du tilmelder dig med dit telefonnummer, og Quicksy vil automatisk - baseret på telefonnumre i din adressebog - foreslå mulige kontakter til dig.<br><br>Når du tilmelder dig, accepterer du vores <a href="https://quicksy.im/#privacy">privatlispolitik</a>.]]></string>
<string name="agree_and_continue">Accepter og fortsætte</string> <string name="agree_and_continue">Accepter og fortsætte</string>
<string name="magic_create_text">En guide er oprettet til kontooprettelse på conversations.im.¹\nNår du vælger conversations.im som udbyder, kan du kommunikere med brugere fra andre udbydere ved at give dem din fulde XMPP-adresse.</string> <string name="magic_create_text">En guide er oprettet til kontooprettelse på conversations.im.¹\nNår du vælger conversations.im som udbyder, kan du kommunikere med brugere fra andre udbydere ved at give dem din fulde XMPP-adresse.</string>
<string name="your_full_jid_will_be">Din fulde XMPP adresse vil blive: %s</string> <string name="your_full_jid_will_be">Din fulde XMPP-adresse vil blive: %s</string>
<string name="create_account">Opret konto</string> <string name="create_account">Opret konto</string>
<string name="use_own_provider">Brug min egen udbyder</string> <string name="use_own_provider">Brug min egen udbyder</string>
<string name="pick_your_username">Vælg dit brugernavn</string> <string name="pick_your_username">Vælg dit brugernavn</string>
<string name="pref_manually_change_presence">Håndter tilgængelighed menuelt</string> <string name="pref_manually_change_presence">Håndter tilgængelighed manuelt</string>
<string name="pref_manually_change_presence_summary">Indstil din tilgængelighed, når du redigerer din statusmeddelelse.</string> <string name="pref_manually_change_presence_summary">Indstil din tilgængelighed, når du redigerer din statusbesked.</string>
<string name="status_message">Statusmeddelelse</string> <string name="status_message">Statusbesked</string>
<string name="presence_chat">Gratis for Chat</string> <string name="presence_chat">Gratis for Chat</string>
<string name="presence_online">Online</string> <string name="presence_online">Online</string>
<string name="presence_away">Ej tilstede</string> <string name="presence_away">Væk</string>
<string name="presence_xa">Ikke tilgængelig</string> <string name="presence_xa">Ikke tilgængelig</string>
<string name="presence_dnd">Optaget</string> <string name="presence_dnd">Optaget</string>
<string name="secure_password_generated">Der er genereret en sikker adgangskode</string> <string name="secure_password_generated">Der er genereret en sikker adgangskode</string>
@ -552,13 +553,13 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="registration_please_wait">Registrering mislykkes: Prøv igen senere</string> <string name="registration_please_wait">Registrering mislykkes: Prøv igen senere</string>
<string name="registration_password_too_weak">Registring mislykkes: Adgangskode for svag</string> <string name="registration_password_too_weak">Registring mislykkes: Adgangskode for svag</string>
<string name="choose_participants">Vælg deltager</string> <string name="choose_participants">Vælg deltager</string>
<string name="creating_conference">Opretter gruppechat...</string> <string name="creating_conference">Opretter gruppechat</string>
<string name="invite_again">Inviter igen</string> <string name="invite_again">Inviter igen</string>
<string name="gp_disable">Deaktiver</string> <string name="gp_disable">Deaktiver</string>
<string name="gp_short">Kort</string> <string name="gp_short">Kort</string>
<string name="gp_medium">Mellem</string> <string name="gp_medium">Mellem</string>
<string name="gp_long">Lang</string> <string name="gp_long">Lang</string>
<string name="pref_broadcast_last_activity">Broadcast brug</string> <string name="pref_broadcast_last_activity">Udsend brug af app</string>
<string name="pref_broadcast_last_activity_summary">Lad dine kontakter vide når du bruger Conversations</string> <string name="pref_broadcast_last_activity_summary">Lad dine kontakter vide når du bruger Conversations</string>
<string name="pref_privacy">Privatliv</string> <string name="pref_privacy">Privatliv</string>
<string name="pref_theme_options">Tema</string> <string name="pref_theme_options">Tema</string>
@ -584,22 +585,23 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="remote_server_not_found">Fjernserver ikke fundet</string> <string name="remote_server_not_found">Fjernserver ikke fundet</string>
<string name="remote_server_timeout">Fjernserver timeout</string> <string name="remote_server_timeout">Fjernserver timeout</string>
<string name="unable_to_update_account">Kunne ikke opdatere konto</string> <string name="unable_to_update_account">Kunne ikke opdatere konto</string>
<string name="report_jid_as_spammer">Reporter denne XMPP adresse for spamming.</string> <string name="report_jid_as_spammer">Reporter denne XMPP-adresse for spamming.</string>
<string name="pref_delete_omemo_identities">Slet OMEMO identiteter</string> <string name="pref_delete_omemo_identities">Slet OMEMO-identiteter</string>
<string name="pref_delete_omemo_identities_summary">Gendan dine OMEMO nøgler. Alle dine kontakter skal bekræfte dig igen. Brug kun dette som en sidste udvej.</string> <string name="pref_delete_omemo_identities_summary">Gendan dine OMEMO-nøgler. Alle dine kontakter skal bekræfte dig igen. Brug kun dette som en sidste udvej.</string>
<string name="delete_selected_keys">Slet valgte nøgler</string> <string name="delete_selected_keys">Slet valgte nøgler</string>
<string name="error_publish_avatar_offline">Du skal være forbundet for at offentliggøre dit avatar.</string> <string name="error_publish_avatar_offline">Du skal være forbundet for at offentliggøre dit avatar.</string>
<string name="show_error_message">Vis fejlbesked</string> <string name="show_error_message">Vis fejlbesked</string>
<string name="error_message">Fejlbesked</string> <string name="error_message">Fejlbesked</string>
<string name="data_saver_enabled">Datasparer aktiveret</string> <string name="data_saver_enabled">Datasparer aktiveret</string>
<string name="data_saver_enabled_explained">Dit operativsystem begrænser %1$s adgangen til Internettet i baggrunden. For at modtage notifikationer om nye beskeder, skal du tillade %1$s ubegrænset adgang, når \"Datasparer\" er aktiveret. \n%1$s vil stadig gøre en indsats for at gemme data, når det er muligt.</string>
<string name="device_does_not_support_data_saver">Din enhed understøtter ikke deaktivering af Databesparelse for %1$s.</string> <string name="device_does_not_support_data_saver">Din enhed understøtter ikke deaktivering af Databesparelse for %1$s.</string>
<string name="error_unable_to_create_temporary_file">Kunne ikke oprette midlertidig fil </string> <string name="error_unable_to_create_temporary_file">Kunne ikke oprette midlertidig fil </string>
<string name="this_device_has_been_verified">Den enhed er blevet bekræftet</string> <string name="this_device_has_been_verified">Den enhed er blevet bekræftet</string>
<string name="copy_fingerprint">Kopier fingeraftryk</string> <string name="copy_fingerprint">Kopier fingeraftryk</string>
<string name="all_omemo_keys_have_been_verified">Du har bekræftet alle OMEMO nøglerne i din besiddelse</string> <string name="all_omemo_keys_have_been_verified">Du har bekræftet alle OMEMO-nøglerne i din besiddelse</string>
<string name="barcode_does_not_contain_fingerprints_for_this_conversation">Stregkoden indeholder ingen fingeraftryk for denne samtale.</string> <string name="barcode_does_not_contain_fingerprints_for_this_conversation">Stregkoden indeholder ingen fingeraftryk for denne samtale.</string>
<string name="verified_fingerprints">Bekræft fingeraftryk</string> <string name="verified_fingerprints">Bekræft fingeraftryk</string>
<string name="use_camera_icon_to_scan_barcode">Brug kameraet til at skanne en kontakt\'s stregkode</string> <string name="use_camera_icon_to_scan_barcode">Brug kameraet til at skanne en kontakts stregkode</string>
<string name="please_wait_for_keys_to_be_fetched">Vent til nøglerne hentes</string> <string name="please_wait_for_keys_to_be_fetched">Vent til nøglerne hentes</string>
<string name="share_as_barcode">Del som stregkode</string> <string name="share_as_barcode">Del som stregkode</string>
<string name="share_as_uri">Del som XMPP URL</string> <string name="share_as_uri">Del som XMPP URL</string>
@ -614,12 +616,12 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="pref_clean_private_storage">Tøm privat lagerplads</string> <string name="pref_clean_private_storage">Tøm privat lagerplads</string>
<string name="pref_clean_private_storage_summary">Tøm privat lagerplads, hvor filer opbevares (De kan downloades igen fra serveren)</string> <string name="pref_clean_private_storage_summary">Tøm privat lagerplads, hvor filer opbevares (De kan downloades igen fra serveren)</string>
<string name="i_followed_this_link_from_a_trusted_source">Jeg fulgte dette link fra en pålidelig kilde</string> <string name="i_followed_this_link_from_a_trusted_source">Jeg fulgte dette link fra en pålidelig kilde</string>
<string name="verifying_omemo_keys_trusted_source">Du er ved bekræfte OMEMO nøgler af %1$s efter du har klikket på et link. Dette er kun sikkert, hvis du fulgte linket fra troværdig kilde hvor kun %2$s kunne have offentliggjort dette link.</string> <string name="verifying_omemo_keys_trusted_source">Du er ved bekræfte OMEMO-nøgler af %1$s efter du har klikket på et link. Dette er kun sikkert, hvis du fulgte linket fra troværdig kilde hvor kun %2$s kunne have offentliggjort dette link.</string>
<string name="verify_omemo_keys">Beskræft OMEMO nøgler</string> <string name="verify_omemo_keys">Beskræft OMEMO-nøgler</string>
<string name="show_inactive_devices">Vis inaktive</string> <string name="show_inactive_devices">Vis inaktive</string>
<string name="hide_inactive_devices">Skjul inaktive</string> <string name="hide_inactive_devices">Skjul inaktive</string>
<string name="distrust_omemo_key">Stol ikke på enhed</string> <string name="distrust_omemo_key">Stol ikke på enhed</string>
<string name="distrust_omemo_key_text">Er du sikker på, at du vil fjerne bekræftelsen på denne enhed? \ Denne enhed og meddelelser fra den vil blive markeret som \"upålidelig\".</string> <string name="distrust_omemo_key_text">Er du sikker på, at du vil fjerne bekræftelsen på denne enhed?\nDenne enhed og meddelelser fra den vil blive markeret som \"upålidelig\".</string>
<plurals name="seconds"> <plurals name="seconds">
<item quantity="one">%d sekund</item> <item quantity="one">%d sekund</item>
<item quantity="other">%d sekunder</item> <item quantity="other">%d sekunder</item>
@ -669,7 +671,7 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="yesterday">I går</string> <string name="yesterday">I går</string>
<string name="pref_validate_hostname">Bekræft værtsnavn med DNSSEC</string> <string name="pref_validate_hostname">Bekræft værtsnavn med DNSSEC</string>
<string name="pref_validate_hostname_summary">Servercertifikater, der indeholder det validerede værtsnavn, betragtes som bekræftede</string> <string name="pref_validate_hostname_summary">Servercertifikater, der indeholder det validerede værtsnavn, betragtes som bekræftede</string>
<string name="certificate_does_not_contain_jid">Certifikat indeholder ikke en XMPP adresse</string> <string name="certificate_does_not_contain_jid">Certifikat indeholder ikke en XMPP-adresse</string>
<string name="server_info_partial">delvis</string> <string name="server_info_partial">delvis</string>
<string name="attach_record_video">Optag video</string> <string name="attach_record_video">Optag video</string>
<string name="copy_to_clipboard">Kopier til udklipsholder</string> <string name="copy_to_clipboard">Kopier til udklipsholder</string>
@ -695,11 +697,12 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="error_trustkey_device_list">Kunne ikke hente enhedsliste</string> <string name="error_trustkey_device_list">Kunne ikke hente enhedsliste</string>
<string name="error_trustkey_bundle">Kunne ikke hente krypteringsnøgler</string> <string name="error_trustkey_bundle">Kunne ikke hente krypteringsnøgler</string>
<string name="error_trustkey_hint_mutual">Tip: I nogle tilfælde kan dette løses ved at tilføje hinanden dine kontaktlister.</string> <string name="error_trustkey_hint_mutual">Tip: I nogle tilfælde kan dette løses ved at tilføje hinanden dine kontaktlister.</string>
<string name="disable_encryption_message">Er du sikker på, at du vil deaktivere OMEMO-kryptering til denne samtale?\nDette giver din serveradministrator mulighed for at læse dine meddelelser, men det er muligvis den eneste måde at kommunikere med folk, der bruger forældede klienter.</string>
<string name="disable_now">Deaktiver nu</string> <string name="disable_now">Deaktiver nu</string>
<string name="draft">Udkast:</string> <string name="draft">Udkast:</string>
<string name="pref_omemo_setting">OMEMO kryptering</string> <string name="pref_omemo_setting">OMEMO-kryptering</string>
<string name="pref_omemo_setting_summary_always">OMEMO vil altid blive brugt for en-til-en og private gruppechats</string> <string name="pref_omemo_setting_summary_always">OMEMO vil altid blive brugt for en-til-en og private gruppechats</string>
<string name="pref_omemo_setting_summary_default_on">OMEMO vil blive brugt som standard for nye samlater.</string> <string name="pref_omemo_setting_summary_default_on">OMEMO vil blive brugt som standard for nye samtaler.</string>
<string name="pref_omemo_setting_summary_default_off">OMEMO skal være tændt udtrykkeligt for nye samtaler.</string> <string name="pref_omemo_setting_summary_default_off">OMEMO skal være tændt udtrykkeligt for nye samtaler.</string>
<string name="create_shortcut">Opret genvej</string> <string name="create_shortcut">Opret genvej</string>
<string name="pref_font_size">Skriftstørrelse</string> <string name="pref_font_size">Skriftstørrelse</string>
@ -709,9 +712,9 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="small">Lille</string> <string name="small">Lille</string>
<string name="medium">Mellem</string> <string name="medium">Mellem</string>
<string name="large">Stor</string> <string name="large">Stor</string>
<string name="not_encrypted_for_this_device">Besked var ikke krypteret på denne enhed</string> <string name="not_encrypted_for_this_device">Besked var ikke krypteret på denne enhed.</string>
<string name="omemo_decryption_failed">Dekryptering af OMEMO besked mislykkes.</string> <string name="omemo_decryption_failed">Dekryptering af OMEMO-besked mislykkes.</string>
<string name="undo">Fortryd</string> <string name="undo">fortryd</string>
<string name="location_disabled">Deling af placering er deaktiveret</string> <string name="location_disabled">Deling af placering er deaktiveret</string>
<string name="action_fix_to_location">Fastgør position</string> <string name="action_fix_to_location">Fastgør position</string>
<string name="action_unfix_from_location">Frigør position</string> <string name="action_unfix_from_location">Frigør position</string>
@ -722,7 +725,7 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="title_activity_show_location">Vis placering</string> <string name="title_activity_show_location">Vis placering</string>
<string name="share">Del</string> <string name="share">Del</string>
<string name="unable_to_start_recording">Kunne ikke starte optagelse</string> <string name="unable_to_start_recording">Kunne ikke starte optagelse</string>
<string name="please_wait">Vent venligst...</string> <string name="please_wait">Vent venligst</string>
<string name="no_microphone_permission">Giv %1$s adgang til mikrofonen</string> <string name="no_microphone_permission">Giv %1$s adgang til mikrofonen</string>
<string name="search_messages">Find beskeder</string> <string name="search_messages">Find beskeder</string>
<string name="gif">GIF</string> <string name="gif">GIF</string>
@ -730,13 +733,13 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="pref_use_share_location_plugin">Del placeringsplugin</string> <string name="pref_use_share_location_plugin">Del placeringsplugin</string>
<string name="pref_use_share_location_plugin_summary">Brug Plugin for delingsplacering i stedet for det indbyggede kort</string> <string name="pref_use_share_location_plugin_summary">Brug Plugin for delingsplacering i stedet for det indbyggede kort</string>
<string name="copy_link">Kopier webadresse</string> <string name="copy_link">Kopier webadresse</string>
<string name="copy_jabber_id">Kopier XMPP adresse</string> <string name="copy_jabber_id">Kopier XMPP-adresse</string>
<string name="p1_s3_filetransfer">HTTP fildeling for S3</string> <string name="p1_s3_filetransfer">HTTP fildeling for S3</string>
<string name="pref_start_search">Direkte søgning</string> <string name="pref_start_search">Direkte søgning</string>
<string name="pref_start_search_summary">Skærmbilledet \'Start samtale\' skal du åbne tastaturet og placere markøren i søgefeltet</string> <string name="pref_start_search_summary">skærmbillede \'Start samtale\' skal du åbne tastaturet og placere markøren i søgefeltet</string>
<string name="group_chat_avatar">Gruppechat avatar</string> <string name="group_chat_avatar">Gruppechat avatar</string>
<string name="host_does_not_support_group_chat_avatars">Vært understøtter ikke gruppechat avatarer</string> <string name="host_does_not_support_group_chat_avatars">Vært understøtter ikke gruppechat avatarer</string>
<string name="only_the_owner_can_change_group_chat_avatar">Kin ejeren kan ændre gruppechat avatar</string> <string name="only_the_owner_can_change_group_chat_avatar">Kun ejeren kan ændre gruppechat avatar</string>
<string name="contact_name">Kontaktnavn</string> <string name="contact_name">Kontaktnavn</string>
<string name="nickname">Kaldenavn</string> <string name="nickname">Kaldenavn</string>
<string name="group_chat_name">Navn</string> <string name="group_chat_name">Navn</string>
@ -755,7 +758,7 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="incoming_calls_channel_name">Indkommende opkald</string> <string name="incoming_calls_channel_name">Indkommende opkald</string>
<string name="ongoing_calls_channel_name">Udgående opkald</string> <string name="ongoing_calls_channel_name">Udgående opkald</string>
<string name="silent_messages_channel_name">Lydløse beskeder</string> <string name="silent_messages_channel_name">Lydløse beskeder</string>
<string name="silent_messages_channel_description">Denne notifikationsgruppe bruges til at vise notifikationer, der ikke bør udløse nogen lyd. For eksempel når du er aktiv på en anden enhed (Grace Periode).</string> <string name="silent_messages_channel_description">Denne notifikationsgruppe bruges til at vise notifikationer, der ikke bør udløse nogen lyd. For eksempel når du er aktiv på en anden enhed (Fredningsperiode).</string>
<string name="delivery_failed_channel_name">Mislykkede leverancer</string> <string name="delivery_failed_channel_name">Mislykkede leverancer</string>
<string name="pref_message_notification_settings">Notifikationsindstilling for besked</string> <string name="pref_message_notification_settings">Notifikationsindstilling for besked</string>
<string name="pref_incoming_call_notification_settings">Notifikationsindstilling for indgående opkald</string> <string name="pref_incoming_call_notification_settings">Notifikationsindstilling for indgående opkald</string>
@ -769,7 +772,7 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="pref_video_compression_summary">Lavere kvalitet betyder mindre filer</string> <string name="pref_video_compression_summary">Lavere kvalitet betyder mindre filer</string>
<string name="video_360p">Mellem (360p)</string> <string name="video_360p">Mellem (360p)</string>
<string name="video_720p">Høj (720p)</string> <string name="video_720p">Høj (720p)</string>
<string name="cancelled">Annulleret</string> <string name="cancelled">annulleret</string>
<string name="already_drafting_message">Du er allerede ved at udarbejde en besked.</string> <string name="already_drafting_message">Du er allerede ved at udarbejde en besked.</string>
<string name="feature_not_implemented">Funktionen ikke implementeret</string> <string name="feature_not_implemented">Funktionen ikke implementeret</string>
<string name="invalid_country_code">Ugyldig landekode</string> <string name="invalid_country_code">Ugyldig landekode</string>
@ -784,18 +787,18 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="verify_x">Bekræft %s</string> <string name="verify_x">Bekræft %s</string>
<string name="we_have_sent_you_an_sms_to_x"><![CDATA[Vi har sendt dig en SMS til <b>%s</b>.]]></string> <string name="we_have_sent_you_an_sms_to_x"><![CDATA[Vi har sendt dig en SMS til <b>%s</b>.]]></string>
<string name="we_have_sent_you_another_sms">Vi har sendt dig en ny SMS med en 6 cifret pinkode.</string> <string name="we_have_sent_you_another_sms">Vi har sendt dig en ny SMS med en 6 cifret pinkode.</string>
<string name="please_enter_pin_below">Indtast venligst den 6 cifret pinkode herunder.</string> <string name="please_enter_pin_below">Indtast den 6 cifret pinkode herunder.</string>
<string name="resend_sms">Send SMS igen</string> <string name="resend_sms">Send SMS igen</string>
<string name="resend_sms_in">Send SMS igen (%s)</string> <string name="resend_sms_in">Send SMS igen (%s)</string>
<string name="wait_x">Vent venligst (%s)</string> <string name="wait_x">Vent venligst (%s)</string>
<string name="back">Tilbage</string> <string name="back">Tilbage</string>
<string name="possible_pin">Indsat automatisk mulig pinkode fra udklipsholderen.</string> <string name="possible_pin">Indsat automatisk mulig pinkode fra udklipsholder.</string>
<string name="please_enter_pin">Indtast venligst din 6 cifret pinkode. </string> <string name="please_enter_pin">Indtast venligst din 6 cifret pinkode. </string>
<string name="abort_registration_procedure">Er du sikker på at du afbryde registreringsproceduren?</string> <string name="abort_registration_procedure">Er du sikker på at du afbryde registreringsproceduren?</string>
<string name="yes">Ja</string> <string name="yes">Ja</string>
<string name="no">Nej</string> <string name="no">Nej</string>
<string name="verifying">Bekræfter...</string> <string name="verifying">Bekræfter</string>
<string name="requesting_sms">Anmoder SMS...</string> <string name="requesting_sms">Anmoder SMS</string>
<string name="incorrect_pin">Den indtastet pinkode er forkert.</string> <string name="incorrect_pin">Den indtastet pinkode er forkert.</string>
<string name="pin_expired">Den sendte pinkode er udløbet.</string> <string name="pin_expired">Den sendte pinkode er udløbet.</string>
<string name="unknown_api_error_network">Ukendt netværksfejl.</string> <string name="unknown_api_error_network">Ukendt netværksfejl.</string>
@ -813,7 +816,7 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="the_app_is_out_of_date">Du bruger en ældre version af denne app.</string> <string name="the_app_is_out_of_date">Du bruger en ældre version af denne app.</string>
<string name="update">Opdater</string> <string name="update">Opdater</string>
<string name="logged_in_with_another_device">Dette telefonnummer er i øjeblikket logget ind med en anden enhed.</string> <string name="logged_in_with_another_device">Dette telefonnummer er i øjeblikket logget ind med en anden enhed.</string>
<string name="enter_your_name_instructions">Indtast dit navn for at lade folk, der ikke har dig i deres telefonbøger, vide, hvem du er.</string> <string name="enter_your_name_instructions">Indtast dit navn for at lade folk, der ikke har dig i deres adressebog, vide, hvem du er.</string>
<string name="your_name">Dit navn</string> <string name="your_name">Dit navn</string>
<string name="enter_your_name">Indtast dit navn</string> <string name="enter_your_name">Indtast dit navn</string>
<string name="no_name_set_instructions">Brug redigeringsknappen for at instille dit navn</string> <string name="no_name_set_instructions">Brug redigeringsknappen for at instille dit navn</string>
@ -821,29 +824,30 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="install_orbot">Installer Orbot</string> <string name="install_orbot">Installer Orbot</string>
<string name="start_orbot">Start Orbot</string> <string name="start_orbot">Start Orbot</string>
<string name="no_market_app_installed">Ingen markedsapp installeret.</string> <string name="no_market_app_installed">Ingen markedsapp installeret.</string>
<string name="group_chat_will_make_your_jabber_id_public">Denne kanal vil offentliggør din XMPP adresse</string> <string name="group_chat_will_make_your_jabber_id_public">Denne kanal vil offentliggør din XMPP-adresse</string>
<string name="ebook">e-bog</string> <string name="ebook">e-bog</string>
<string name="video_original">Original (ukomprimeret)</string> <string name="video_original">Original (ukomprimeret)</string>
<string name="open_with">Åbn med...</string> <string name="open_with">Åbn med</string>
<string name="set_profile_picture">Conversations profilbillede</string> <string name="set_profile_picture">Conversations profilbillede</string>
<string name="choose_account">Vælg konto</string> <string name="choose_account">Vælg konto</string>
<string name="restore_backup">Gendan backup</string> <string name="restore_backup">Gendan backup</string>
<string name="restore">Gendan</string> <string name="restore">Gendan</string>
<string name="enter_password_to_restore">Indtast din adgangskode til kontoen %s for at gendanne sikkerhedskopien.</string> <string name="enter_password_to_restore">Indtast din adgangskode til kontoen %s for at gendanne backuppen.</string>
<string name="restore_warning">Brug ikke gendannelsessikkerhedsfunktionen i et forsøg på at klone (køre samtidigt) en installation. Gendannelse af en backup er kun beregnet til migreringer, eller hvis du har mistet den originale enhed.</string>
<string name="unable_to_restore_backup">Kunne ikke gendan backup</string> <string name="unable_to_restore_backup">Kunne ikke gendan backup</string>
<string name="unable_to_decrypt_backup">Kunne ikke dekryptere backup. Er adgangskoden korrekt?</string> <string name="unable_to_decrypt_backup">Kunne ikke dekryptere backup. Er adgangskoden korrekt?</string>
<string name="backup_channel_name">Backup &amp; Gendan</string> <string name="backup_channel_name">Backup &amp; Gendan</string>
<string name="enter_jabber_id">Indtast XMPP adresse</string> <string name="enter_jabber_id">Indtast XMPP-adresse</string>
<string name="create_group_chat">Opret gruppechat</string> <string name="create_group_chat">Opret gruppechat</string>
<string name="join_public_channel">Deltag i offentlig kanal</string> <string name="join_public_channel">Deltag i offentlig kanal</string>
<string name="create_private_group_chat">Opret privat gruppechat</string> <string name="create_private_group_chat">Opret privat gruppechat</string>
<string name="create_public_channel">Opret offentlig kanal</string> <string name="create_public_channel">Opret offentlig kanal</string>
<string name="create_dialog_channel_name">Kanalnavn</string> <string name="create_dialog_channel_name">Kanalnavn</string>
<string name="xmpp_address">XMPP adresse</string> <string name="xmpp_address">XMPP-adresse</string>
<string name="please_enter_name">Angiv venligst et navn til kanalen</string> <string name="please_enter_name">Angiv venligst et navn til kanalen</string>
<string name="please_enter_xmpp_address">Angiv venligst en XMPP adresse</string> <string name="please_enter_xmpp_address">Angiv venligst en XMPP-adresse</string>
<string name="this_is_an_xmpp_address">Dette er en XMPP adresse. Angiv venligst et navn</string> <string name="this_is_an_xmpp_address">Dette er en XMPP-adresse. Angiv venligst et navn</string>
<string name="creating_channel">Opret offentlig kanal...</string> <string name="creating_channel">Opret offentlig kanal</string>
<string name="channel_already_exists">Denne kanal eksister allerede</string> <string name="channel_already_exists">Denne kanal eksister allerede</string>
<string name="joined_an_existing_channel">Du sluttede dig til en eksisterende kanal</string> <string name="joined_an_existing_channel">Du sluttede dig til en eksisterende kanal</string>
<string name="unable_to_set_channel_configuration">Kunne ikke gemme kanalkonfiguration</string> <string name="unable_to_set_channel_configuration">Kunne ikke gemme kanalkonfiguration</string>
@ -854,9 +858,9 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="admins_can_edit_subject">Administrator kan redigere emnet.</string> <string name="admins_can_edit_subject">Administrator kan redigere emnet.</string>
<string name="owners_can_invite_others">Ejere kan invitere andre.</string> <string name="owners_can_invite_others">Ejere kan invitere andre.</string>
<string name="anyone_can_invite_others">Alle kan invitere andre.</string> <string name="anyone_can_invite_others">Alle kan invitere andre.</string>
<string name="jabber_ids_are_visible_to_admins">XMPP adresser er synlig for administratorerne.</string> <string name="jabber_ids_are_visible_to_admins">XMPP-adresser er synlig for administratorerne.</string>
<string name="jabber_ids_are_visible_to_anyone">XMPP adresser er synlige for alle.</string> <string name="jabber_ids_are_visible_to_anyone">XMPP-adresser er synlige for alle.</string>
<string name="no_users_hint_channel">Denne offentlige kanal har ingen deltagere. Inviter dine kontakter eller brug deleknappen til distribuere dens XMPP adresse</string> <string name="no_users_hint_channel">Denne offentlige kanal har ingen deltagere. Inviter dine kontakter eller brug deleknappen til distribuere dens XMPP-adresse</string>
<string name="no_users_hint_group_chat">Denne private gruppechat har ingen medlemmer.</string> <string name="no_users_hint_group_chat">Denne private gruppechat har ingen medlemmer.</string>
<string name="manage_permission">Administrer rettigheder</string> <string name="manage_permission">Administrer rettigheder</string>
<string name="search_participants">Find deltagere</string> <string name="search_participants">Find deltagere</string>
@ -864,7 +868,8 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="attach">Vedhæft</string> <string name="attach">Vedhæft</string>
<string name="discover_channels">Find kanaler</string> <string name="discover_channels">Find kanaler</string>
<string name="search_channels">Find kanaler</string> <string name="search_channels">Find kanaler</string>
<string name="channel_discovery_opt_in_title">Mulig krænkelse af privatlivet!</string> <string name="channel_discovery_opt_in_title">Risiko for krænkelse af privatlivet!</string>
<string name="channel_discover_opt_in_message"><![CDATA[Kanalsøgning bruger en tredjepartstjeneste kaldet <a href="https://search.jabber.network">søg.jabber.netværk</a>.<br><br>Brug af denne funktion sender din IP-adresse og søgetermer til denne service. Se deres <a href="https://search.jabber.network/privacy">Privatlivspolitik</a> for mere information.]]></string>
<string name="i_already_have_an_account">Jeg har allerede en konto</string> <string name="i_already_have_an_account">Jeg har allerede en konto</string>
<string name="add_existing_account">Tilføj eksisterende konto</string> <string name="add_existing_account">Tilføj eksisterende konto</string>
<string name="register_new_account">Registrer ny konto</string> <string name="register_new_account">Registrer ny konto</string>
@ -879,13 +884,13 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<string name="account_already_setup">Denne konto er allerede oprettet</string> <string name="account_already_setup">Denne konto er allerede oprettet</string>
<string name="please_enter_password">Indtast adgangskoden til denne konto</string> <string name="please_enter_password">Indtast adgangskoden til denne konto</string>
<string name="unable_to_perform_this_action">Kunne ikke udføre denne handling</string> <string name="unable_to_perform_this_action">Kunne ikke udføre denne handling</string>
<string name="open_join_dialog">Deltag i offentlig kanal...</string> <string name="open_join_dialog">Deltag i offentlig kanal</string>
<string name="sharing_application_not_grant_permission">Dele-appen gav ikke tilladelse til at få adgang til denne fil.</string> <string name="sharing_application_not_grant_permission">Dele-appen gav ikke tilladelse til at få adgang til denne fil.</string>
<string name="group_chats_and_channels"><![CDATA[Gruppechats & Kanaler]]></string> <string name="group_chats_and_channels"><![CDATA[Gruppechats & Kanaler]]></string>
<string name="jabber_network">jabber.netværk</string> <string name="jabber_network">jabber.netværk</string>
<string name="local_server">Lokal server</string> <string name="local_server">Lokal server</string>
<string name="pref_channel_discovery_summary">De fleste brugere bør vælge \'jabber.network\' for bedst mulige forslag fra hele det offentlige XMPP-økosystem.</string> <string name="pref_channel_discovery_summary">De fleste brugere bør vælge \'jabber.netrk\' for bedst mulige forslag fra hele det offentlige XMPP-økosystem.</string>
<string name="pref_channel_discovery">Kanal søgningsmetode</string> <string name="pref_channel_discovery">Metode for kanalsøgning</string>
<string name="backup">Backup</string> <string name="backup">Backup</string>
<string name="category_about">Om</string> <string name="category_about">Om</string>
<string name="please_enable_an_account">Aktiver venligst en konto</string> <string name="please_enable_an_account">Aktiver venligst en konto</string>
@ -948,7 +953,7 @@ Din enhed anvender kraftig batterioptimeringer for %1$s som kan føre til forsin
<item quantity="other">Nogle beskeder kunne ikke leveres</item> <item quantity="other">Nogle beskeder kunne ikke leveres</item>
</plurals> </plurals>
<string name="failed_deliveries">Mislykkede leverancer</string> <string name="failed_deliveries">Mislykkede leverancer</string>
<string name="more_options">Flere muligheder</string> <string name="more_options">Flere valg</string>
<string name="no_application_found">Intet program fundet</string> <string name="no_application_found">Intet program fundet</string>
<string name="invite_to_app">Inviter til Conversations</string> <string name="invite_to_app">Inviter til Conversations</string>
<string name="unable_to_parse_invite">Kunne ikke analysere invitation</string> <string name="unable_to_parse_invite">Kunne ikke analysere invitation</string>

View File

@ -7,7 +7,7 @@
<string name="action_end_conversation">Pechar conversa</string> <string name="action_end_conversation">Pechar conversa</string>
<string name="action_contact_details">Detalles do contacto</string> <string name="action_contact_details">Detalles do contacto</string>
<string name="action_muc_details">Detalles da conversa de grupo</string> <string name="action_muc_details">Detalles da conversa de grupo</string>
<string name="channel_details">Detalles do canal</string> <string name="channel_details">Detalles da canle</string>
<string name="action_add_account">Engadir conta</string> <string name="action_add_account">Engadir conta</string>
<string name="action_edit_contact">Editar contacto</string> <string name="action_edit_contact">Editar contacto</string>
<string name="action_add_phone_book">Engadir a libreta de enderezos</string> <string name="action_add_phone_book">Engadir a libreta de enderezos</string>
@ -234,16 +234,16 @@
<string name="select">Selecionar</string> <string name="select">Selecionar</string>
<string name="contact_already_exists">Xa existe o contacto</string> <string name="contact_already_exists">Xa existe o contacto</string>
<string name="join">Unirse</string> <string name="join">Unirse</string>
<string name="channel_full_jid_example">canal@sala.exemplo.com/alcume</string> <string name="channel_full_jid_example">canle@sala.exemplo.com/alcume</string>
<string name="channel_bare_jid_example">canal@sala.exemplo.com</string> <string name="channel_bare_jid_example">canle@sala.exemplo.com</string>
<string name="save_as_bookmark">Gardar como marcador</string> <string name="save_as_bookmark">Gardar como marcador</string>
<string name="delete_bookmark">Eliminar marcador</string> <string name="delete_bookmark">Eliminar marcador</string>
<string name="destroy_room">Destruír a conversa en grupo</string> <string name="destroy_room">Destruír a conversa en grupo</string>
<string name="destroy_channel">Eliminar canal</string> <string name="destroy_channel">Eliminar canle</string>
<string name="destroy_room_dialog">Está segura de querer destruír esta conversa en grupo?\n\n<b>Aviso:</b> A conversa en grupo será totalmente eliminada do servidor.</string> <string name="destroy_room_dialog">Está segura de querer destruír esta conversa en grupo?\n\n<b>Aviso:</b> A conversa en grupo será totalmente eliminada do servidor.</string>
<string name="destroy_channel_dialog">Está segura de que quere eliminar o canal?\n\n<b>Aviso:</b> O canal eliminarase completamente do servidor.</string> <string name="destroy_channel_dialog">Tes a certeza de querer eliminar a canle?\n\n<b>Aviso:</b> A canle será eliminada completamente do servidor.</string>
<string name="could_not_destroy_room">Non se desfixo a conversa en grupo</string> <string name="could_not_destroy_room">Non se desfixo a conversa en grupo</string>
<string name="could_not_destroy_channel">Non se puido eliminar o canal</string> <string name="could_not_destroy_channel">Non se puido eliminar a canle</string>
<string name="action_edit_subject">Editar o tema da conversa en grupo</string> <string name="action_edit_subject">Editar o tema da conversa en grupo</string>
<string name="topic">Asunto</string> <string name="topic">Asunto</string>
<string name="joining_conference">Entrando na conversa en grupo</string> <string name="joining_conference">Entrando na conversa en grupo</string>
@ -384,18 +384,18 @@
<string name="grant_owner_privileges">Conceder privilexios de propiedade</string> <string name="grant_owner_privileges">Conceder privilexios de propiedade</string>
<string name="remove_owner_privileges">Retirar privilexios de propiedade</string> <string name="remove_owner_privileges">Retirar privilexios de propiedade</string>
<string name="remove_from_room">Eliminar da conversa en grupo</string> <string name="remove_from_room">Eliminar da conversa en grupo</string>
<string name="remove_from_channel">Retirar do canal</string> <string name="remove_from_channel">Retirar da canle</string>
<string name="could_not_change_affiliation">Non se puido mudar a afiliación de %s</string> <string name="could_not_change_affiliation">Non se puido mudar a afiliación de %s</string>
<string name="ban_from_conference">Prohibición da conversa en grupo</string> <string name="ban_from_conference">Prohibición da conversa en grupo</string>
<string name="ban_from_channel">Prohibir no canal</string> <string name="ban_from_channel">Vetar na canle</string>
<string name="removing_from_public_conference">Estás a intentar eliminar %s dun canal público. O único xeito de facelo é prohibíndolle o acceso para sempre.</string> <string name="removing_from_public_conference">Estás a intentar eliminar a %s dunha canle pública. O único xeito de facelo é prohibíndolle o acceso para sempre.</string>
<string name="ban_now">Rexeitar agora</string> <string name="ban_now">Rexeitar agora</string>
<string name="could_not_change_role">Non se puido mudar o rol de %s</string> <string name="could_not_change_role">Non se puido mudar o rol de %s</string>
<string name="conference_options">Configuración do grupo privado de conversa</string> <string name="conference_options">Configuración do grupo privado de conversa</string>
<string name="channel_options">Configuración do canal público</string> <string name="channel_options">Configuración da canle pública</string>
<string name="members_only">Privada, só para membros</string> <string name="members_only">Privada, só para membros</string>
<string name="non_anonymous">Facer os enderezos XMPP visibles para calquera</string> <string name="non_anonymous">Facer os enderezos XMPP visibles para calquera</string>
<string name="moderated">Establecer canal como moderado</string> <string name="moderated">Establecer canle como moderada</string>
<string name="you_are_not_participating">Non estás a participar</string> <string name="you_are_not_participating">Non estás a participar</string>
<string name="modified_conference_options">¡Opcións da conversa en grupo modificadas!</string> <string name="modified_conference_options">¡Opcións da conversa en grupo modificadas!</string>
<string name="could_not_modify_conference_options">Non se puideron modificar as opcións da conversa en grupo</string> <string name="could_not_modify_conference_options">Non se puideron modificar as opcións da conversa en grupo</string>
@ -431,7 +431,7 @@
<string name="location">Localización</string> <string name="location">Localización</string>
<string name="title_undo_swipe_out_conversation">Pechouse a conversa</string> <string name="title_undo_swipe_out_conversation">Pechouse a conversa</string>
<string name="title_undo_swipe_out_group_chat">Deixar o grupo de conversa privada</string> <string name="title_undo_swipe_out_group_chat">Deixar o grupo de conversa privada</string>
<string name="title_undo_swipe_out_channel">Deixar o canal público</string> <string name="title_undo_swipe_out_channel">Deixar a canle pública</string>
<string name="pref_dont_trust_system_cas_title">Non confiar nas CAs do sistema</string> <string name="pref_dont_trust_system_cas_title">Non confiar nas CAs do sistema</string>
<string name="pref_dont_trust_system_cas_summary">Todos os certificados deberán ser aprobados manualmente</string> <string name="pref_dont_trust_system_cas_summary">Todos os certificados deberán ser aprobados manualmente</string>
<string name="pref_remove_trusted_certificates_title">Eliminar certificados</string> <string name="pref_remove_trusted_certificates_title">Eliminar certificados</string>
@ -510,7 +510,7 @@
<string name="no_camera_permission">Permitir que %1$s acceda á cámara</string> <string name="no_camera_permission">Permitir que %1$s acceda á cámara</string>
<string name="sync_with_contacts">Sincronice con todos os contactos</string> <string name="sync_with_contacts">Sincronice con todos os contactos</string>
<string name="sync_with_contacts_long">%1$s quere ter permiso para acceder á túa libreta de enderezos para comparala coa lista de contactos XMPP.\nDeste xeito poderá mostrar o nome completo e avatares dos teus contactos.\n\n%1$s só utilizará de xeito local a túa lista de contactos, sen subila a ningún servidor.</string> <string name="sync_with_contacts_long">%1$s quere ter permiso para acceder á túa libreta de enderezos para comparala coa lista de contactos XMPP.\nDeste xeito poderá mostrar o nome completo e avatares dos teus contactos.\n\n%1$s só utilizará de xeito local a túa lista de contactos, sen subila a ningún servidor.</string>
<string name="sync_with_contacts_quicksy"><![CDATA[Quicksy precisa acceder ao número de teléfono dos contactos para facer suxestións sobre posibles contactos que xa están en Quicksy.<br><br>Non gardaremos unha copia de esos números de teléfono.\n\nPara máis información lea a nosa <a href="https://quicksy.im/#privacy">política de intimidade</a>.<br><br>A continuación pediráselle permiso para acceder aos contactos.]]></string> <string name="sync_with_contacts_quicksy"><![CDATA[Quicksy precisa acceder ao número de teléfono dos contactos para facer suxestións sobre posibles contactos que xa están en Quicksy.<br><br>Non gardaremos unha copia desos números de teléfono.\n\nPara máis información le a nosa <a href="https://quicksy.im/#privacy">política de privacidade</a>.<br><br>A continuación pediremos permiso para acceder aos contactos.]]></string>
<string name="notify_on_all_messages">Notificar todas as mensaxes</string> <string name="notify_on_all_messages">Notificar todas as mensaxes</string>
<string name="notify_only_when_highlighted">Notificar só cando é mencionada</string> <string name="notify_only_when_highlighted">Notificar só cando é mencionada</string>
<string name="notify_never">Notificacións desactivadas</string> <string name="notify_never">Notificacións desactivadas</string>
@ -533,7 +533,7 @@
<string name="security_error_invalid_file_access">Fallo de seguridade: Acceso non válido ao ficheiro!</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> <string name="no_application_to_share_uri">Non se atopou unha app para compartir URI</string>
<string name="share_uri_with">Compartir URI con...</string> <string name="share_uri_with">Compartir URI con...</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy é un derivado do popular cliente XMPP Conversations con descubrimento automático de contactos.<br><br>Podes rexistrarte co teu número de teléfono e Quicksy suxerillache automáticamente —tomando os números da túa libreta de enderezos como referencia— posibles contactos para ti.<br><br>Ao rexistrarte aceptas a nosa <a href="https://quicksy.im/#privacy">política de intimidade</a>.]]></string> <string name="welcome_text_quicksy"><![CDATA[Quicksy é un derivado do popular cliente XMPP Conversations con descubrimento automático de contactos.<br><br>Podes rexistrarte co teu número de teléfono e Quicksy suxerirache automáticamente —tomando os números da túa libreta de enderezos como referencia— posibles contactos para ti.<br><br>Ao rexistrarte aceptas a nosa <a href="https://quicksy.im/#privacy">política de privacidade</a>.]]></string>
<string name="agree_and_continue">Aceptar e continuar</string> <string name="agree_and_continue">Aceptar e continuar</string>
<string name="magic_create_text">Tes unha guía para crear unha conta en chat.sum7.eu\nAo escoller chat.sum7.eu como provedor poderás comunicarte con outras usuarias de outros provedores con só darlles o teu enderezo XMPP completo.</string> <string name="magic_create_text">Tes unha guía para crear unha conta en chat.sum7.eu\nAo escoller chat.sum7.eu como provedor poderás comunicarte con outras usuarias de outros provedores con só darlles o teu enderezo XMPP completo.</string>
<string name="your_full_jid_will_be">O seu enderezo XMPP completo será: %s</string> <string name="your_full_jid_will_be">O seu enderezo XMPP completo será: %s</string>
@ -561,7 +561,7 @@
<string name="gp_long">Longo</string> <string name="gp_long">Longo</string>
<string name="pref_broadcast_last_activity">Publicar utilización</string> <string name="pref_broadcast_last_activity">Publicar utilización</string>
<string name="pref_broadcast_last_activity_summary">Permitelle aos teus contactos saber cando estás a usar Conversations</string> <string name="pref_broadcast_last_activity_summary">Permitelle aos teus contactos saber cando estás a usar Conversations</string>
<string name="pref_privacy">Intimidade</string> <string name="pref_privacy">Privacidade</string>
<string name="pref_theme_options">Decorado</string> <string name="pref_theme_options">Decorado</string>
<string name="pref_theme_options_summary">Escolle a gama de cores</string> <string name="pref_theme_options_summary">Escolle a gama de cores</string>
<string name="pref_theme_automatic">Automático</string> <string name="pref_theme_automatic">Automático</string>
@ -824,7 +824,7 @@
<string name="install_orbot">Instalar Orbot</string> <string name="install_orbot">Instalar Orbot</string>
<string name="start_orbot">Iniciar Orbot</string> <string name="start_orbot">Iniciar Orbot</string>
<string name="no_market_app_installed">Non ten loxa de aplicacións instalada.</string> <string name="no_market_app_installed">Non ten loxa de aplicacións instalada.</string>
<string name="group_chat_will_make_your_jabber_id_public">Este canal fará público o seu enderezo XMPP</string> <string name="group_chat_will_make_your_jabber_id_public">Esta canle fará público o teu enderezo XMPP</string>
<string name="ebook">e-book</string> <string name="ebook">e-book</string>
<string name="video_original">Orixinal (non comprimido)</string> <string name="video_original">Orixinal (non comprimido)</string>
<string name="open_with">Abrir con...</string> <string name="open_with">Abrir con...</string>
@ -839,18 +839,18 @@
<string name="backup_channel_name">Respaldar &amp; Restaurar</string> <string name="backup_channel_name">Respaldar &amp; Restaurar</string>
<string name="enter_jabber_id">Introducir enderezo XMPP</string> <string name="enter_jabber_id">Introducir enderezo XMPP</string>
<string name="create_group_chat">Crear grupo de conversa</string> <string name="create_group_chat">Crear grupo de conversa</string>
<string name="join_public_channel">Unirse a canal público</string> <string name="join_public_channel">Unirse a canle pública</string>
<string name="create_private_group_chat">Crear grupo privado de conversa</string> <string name="create_private_group_chat">Crear grupo privado de conversa</string>
<string name="create_public_channel">Crear canal público</string> <string name="create_public_channel">Crear canle pública</string>
<string name="create_dialog_channel_name">Nome do canal</string> <string name="create_dialog_channel_name">Nome da canle</string>
<string name="xmpp_address">Enderezo XMPP</string> <string name="xmpp_address">Enderezo XMPP</string>
<string name="please_enter_name">Por favor, proporcione un nome para o canal</string> <string name="please_enter_name">Por favor, escribe un nome para a canle</string>
<string name="please_enter_xmpp_address">Por favor, proporcione un enderezo XMPP</string> <string name="please_enter_xmpp_address">Por favor, proporcione un enderezo XMPP</string>
<string name="this_is_an_xmpp_address">Esto é un enderezo XMPP. Por favor, proporcione un nome.</string> <string name="this_is_an_xmpp_address">Esto é un enderezo XMPP. Por favor, proporcione un nome.</string>
<string name="creating_channel">Creando canal público...</string> <string name="creating_channel">Creando canle pública...</string>
<string name="channel_already_exists">Este canal xa existe</string> <string name="channel_already_exists">Esta canle xa existe</string>
<string name="joined_an_existing_channel">Entrou nun canal existente</string> <string name="joined_an_existing_channel">Entraches nunha canle existente</string>
<string name="unable_to_set_channel_configuration">Non se gardaron os axustes do canal</string> <string name="unable_to_set_channel_configuration">Non se gardaron os axustes da canle</string>
<string name="allow_participants_to_edit_subject">Permitir que calquera cambie o asunto</string> <string name="allow_participants_to_edit_subject">Permitir que calquera cambie o asunto</string>
<string name="allow_participants_to_invite_others">Permitir que calquera poida convidar</string> <string name="allow_participants_to_invite_others">Permitir que calquera poida convidar</string>
<string name="anyone_can_edit_subject">Calquera pode editar o asunto.</string> <string name="anyone_can_edit_subject">Calquera pode editar o asunto.</string>
@ -860,22 +860,22 @@
<string name="anyone_can_invite_others">Calquera pode convidar a outras.</string> <string name="anyone_can_invite_others">Calquera pode convidar a outras.</string>
<string name="jabber_ids_are_visible_to_admins">Os enderezos XMPP son visibles para a administración.</string> <string name="jabber_ids_are_visible_to_admins">Os enderezos XMPP son visibles para a administración.</string>
<string name="jabber_ids_are_visible_to_anyone">Os enderezos XMPP son visibles para calquera.</string> <string name="jabber_ids_are_visible_to_anyone">Os enderezos XMPP son visibles para calquera.</string>
<string name="no_users_hint_channel">Este canal público non ten participantes. Convida aos teus contactos ou utiliza o botón compartir para distribuír o teu enderezo XMPP.</string> <string name="no_users_hint_channel">Esta canle pública non ten participantes. Convida aos teus contactos ou utiliza o botón compartir para distribuír o teu enderezo XMPP.</string>
<string name="no_users_hint_group_chat">Este grupo privado non ten participantes.</string> <string name="no_users_hint_group_chat">Este grupo privado non ten participantes.</string>
<string name="manage_permission">Xestionar privilexios</string> <string name="manage_permission">Xestionar privilexios</string>
<string name="search_participants">Buscar participantes</string> <string name="search_participants">Buscar participantes</string>
<string name="file_too_large">Ficheiro demasiado grande</string> <string name="file_too_large">Ficheiro demasiado grande</string>
<string name="attach">Anexar</string> <string name="attach">Anexar</string>
<string name="discover_channels">Descubrir canales</string> <string name="discover_channels">Descubrir canles</string>
<string name="search_channels">Buscar canales</string> <string name="search_channels">Buscar canles</string>
<string name="channel_discovery_opt_in_title">Posible intrusión na intimidade!</string> <string name="channel_discovery_opt_in_title">Posible intrusión na privacidade!</string>
<string name="channel_discover_opt_in_message"><![CDATA[O descubrimento de canais utiliza un servizo de terceiros chamado <a href="https://search.jabber.network">search.jabber.network</a>.<br><br>Ao utilizar esta función transmitirá o teu enderezo IP e termos de busca a ese servizo. Le a súa <a href="https://search.jabber.network/privacy">Política de Privacidade</a> para máis información.]]></string> <string name="channel_discover_opt_in_message"><![CDATA[O descubrimento de canles utiliza un servizo de terceiros chamado <a href="https://search.jabber.network">search.jabber.network</a>.<br><br>Ao utilizar esta función transmitirás o teu enderezo IP e termos de busca a ese servizo. Le a súa <a href="https://search.jabber.network/privacy">Política de Privacidade</a> para máis información.]]></string>
<string name="i_already_have_an_account">Xa teño unha conta</string> <string name="i_already_have_an_account">Xa teño unha conta</string>
<string name="add_existing_account">Engadir conta existente</string> <string name="add_existing_account">Engadir conta existente</string>
<string name="register_new_account">Rexistrar unha nova conta</string> <string name="register_new_account">Rexistrar unha nova conta</string>
<string name="this_looks_like_a_domain">Esto semella un enderezo de dominio</string> <string name="this_looks_like_a_domain">Esto semella un enderezo de dominio</string>
<string name="add_anway">Engadir igualmente</string> <string name="add_anway">Engadir igualmente</string>
<string name="this_looks_like_channel">Esto semella o enderezo de un canal</string> <string name="this_looks_like_channel">Esto semella o enderezo dunha canle</string>
<string name="share_backup_files">Compartir ficheiros de apoio</string> <string name="share_backup_files">Compartir ficheiros de apoio</string>
<string name="conversations_backup">Respaldar Conversations</string> <string name="conversations_backup">Respaldar Conversations</string>
<string name="event">Evento</string> <string name="event">Evento</string>
@ -886,7 +886,7 @@
<string name="unable_to_perform_this_action">Non se puido completar a acción</string> <string name="unable_to_perform_this_action">Non se puido completar a acción</string>
<string name="open_join_dialog">Unirse a canle pública...</string> <string name="open_join_dialog">Unirse a canle pública...</string>
<string name="sharing_application_not_grant_permission">A aplicación que comparte non proporciona permiso para acceder ao ficheiro.</string> <string name="sharing_application_not_grant_permission">A aplicación que comparte non proporciona permiso para acceder ao ficheiro.</string>
<string name="group_chats_and_channels"><![CDATA[Conversas grupo & Canles]]></string> <string name="group_chats_and_channels"><![CDATA[Grupos de conversa & Canles]]></string>
<string name="jabber_network">jabber.network</string> <string name="jabber_network">jabber.network</string>
<string name="local_server">Servidor local</string> <string name="local_server">Servidor local</string>
<string name="pref_channel_discovery_summary">A maioría das usuarias debería escoller \'jabber.network\' para obter mellores suxestións desde o ecosistema público XMPP.</string> <string name="pref_channel_discovery_summary">A maioría das usuarias debería escoller \'jabber.network\' para obter mellores suxestións desde o ecosistema público XMPP.</string>

View File

@ -1,4 +1,5 @@
<resources> <resources>
<dimen name="local_video_preview_height">96dp</dimen> <dimen name="local_video_preview_height">96dp</dimen>
<dimen name="local_video_preview_width">128dp</dimen> <dimen name="local_video_preview_width">128dp</dimen>
<dimen name="rtp_session_duration_top_margin">16dp</dimen>
</resources> </resources>

View File

@ -411,7 +411,7 @@
<string name="reply">Ответить</string> <string name="reply">Ответить</string>
<string name="mark_as_read">Прочитано</string> <string name="mark_as_read">Прочитано</string>
<string name="pref_input_options">Ввод</string> <string name="pref_input_options">Ввод</string>
<string name="pref_enter_is_send">Отправка по \\"Enter\\"</string> <string name="pref_enter_is_send">Отправка по \"Enter\"</string>
<string name="pref_enter_is_send_summary">Отправлять сообщения клавишей Enter. Даже если эта опция отключена, сообщение можно отправить, нажав Ctrl+Enter.</string> <string name="pref_enter_is_send_summary">Отправлять сообщения клавишей Enter. Даже если эта опция отключена, сообщение можно отправить, нажав Ctrl+Enter.</string>
<string name="pref_display_enter_key">Показывать клавишу ввода</string> <string name="pref_display_enter_key">Показывать клавишу ввода</string>
<string name="pref_display_enter_key_summary">Поменять кнопку смайликов на кнопку ввода</string> <string name="pref_display_enter_key_summary">Поменять кнопку смайликов на кнопку ввода</string>

View File

@ -43,4 +43,5 @@
<dimen name="local_video_preview_height">128dp</dimen> <dimen name="local_video_preview_height">128dp</dimen>
<dimen name="local_video_preview_width">96dp</dimen> <dimen name="local_video_preview_width">96dp</dimen>
<dimen name="rtp_session_duration_top_margin">24dp</dimen>
</resources> </resources>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="pref_notification_grace_period_summary">Hvor lang tid Quicksy holder stille efter at have set aktivitet på en anden enhed</string> <string name="pref_notification_grace_period_summary">Hvor lang tid Quicksy er stille efter at have set aktivitet på en anden enhed</string>
<string name="pref_never_send_crash_summary">Ved at indsende \"stack traces\" hjælper du udviklingen af Quicksy</string> <string name="pref_never_send_crash_summary">Ved at indsende \"stack traces\" hjælper du udviklingen af Quicksy</string>
<string name="pref_broadcast_last_activity_summary">Lad alle dine kontakter vide når du bruger Quicksy </string> <string name="pref_broadcast_last_activity_summary">Lad alle dine kontakter vide når du bruger Quicksy </string>
<string name="huawei_protected_apps_summary">For at modtage underretninger, selv når skærmen er slukket, skal du tilføje Quicksy til listen over beskyttede apps.</string> <string name="huawei_protected_apps_summary">For at modtage underretninger, selv når skærmen er slukket, skal du tilføje Quicksy til listen over beskyttede apps.</string>