Merge tag '2.7.1' into develop

This commit is contained in:
genofire 2020-03-10 13:05:41 +01:00
commit 35b9745fc9
No known key found for this signature in database
GPG Key ID: 9D7D3C6BFF600C6A
21 changed files with 163 additions and 76 deletions

View File

@ -1,5 +1,10 @@
# Changelog
### Version 2.7.1
* Fix avatar selection on some Android 10 devices
* Fix file transfer for larger files
### Version 2.7.0
* Provide PDF preview on Android 5+

View File

@ -175,7 +175,7 @@ FCM (Google Push) allows an app to wake up from *Doze* which is (as the name sug
#### Conversations doesnt work for me. Where can I get help?
You can join our conference room on `support@conference.chat.sum7.eu`.
You can join our conference room on [`support@conference.chat.sum7.eu`](https://conversations.im/j/support@conference.chat.sum7.eu).
A lot of people in there are able to answer basic questions about the usage of
Conversations or can provide you with tips on running your own XMPP server. If
you found a bug or your app crashes please read the Developer / Report Bugs
@ -265,7 +265,7 @@ and introduce yourself to `iNPUTmice` so he can approve your join request.
#### How do I backup / move Conversations to a new device?
On the one hand Conversations supports Message Archive Management to keep a server side history of your messages so when migrating to a new device that device can display your entire history. However that does not work if you enable OMEMO due to its forward secrecy. (Read [The State of Mobile XMPP in 2016](https://gultsch.de/xmpp_2016.html) especially the section on encryption.)
As of version 2.4.0 an integrated Backup & Restore function will help with this, go to Settings → Expert settings → Create backup. A notification will pop-up during the creation process that will announce you when it's ready. After the files, one for each account, are created, you can move the **Conversations** folder *(if you want your old media files too)* or only the **Conversations/Backup** folder *(for OMEMO keys and history only)* to your new device (or to a storage place) where a freshly installed Conversations can restore each account. Don't forget to enable the accounts after a succesful restore.
As of version 2.4.0 an integrated Backup & Restore function will help with this, go to Settings and youll find a setting called Create backup. A notification will pop-up during the creation process that will announce you when it's ready. After the files, one for each account, are created, you can move the **Conversations** folder *(if you want your old media files too)* or only the **Conversations/Backup** folder *(for OMEMO keys and history only)* to your new device (or to a storage place) where a freshly installed Conversations can restore each account. Don't forget to enable the accounts after a succesful restore.
This backup method will include your OMEMO keys. Due to forward secrecy you will not be able to recover messages sent and received between creating the backup and restoring it. If you have a server side archive (MAM) those messages will be retrieved but displayed as *unable to decrypt*. For technical reasons you might also lose the first message you either sent or receive after the restore; for each conversation you have. This message will then also show up as *unable to decrypt*, but this will automatically recover itself as long as both participants are on Conversations 2.3.11+. Note that this doesnt happen if you just transfer to a new phone and no messages have been exchanged between backup and restore.

View File

@ -54,7 +54,7 @@ dependencies {
compatImplementation "com.android.support:support-emoji-appcompat:$supportLibVersion"
conversationsFreeCompatImplementation "com.android.support:support-emoji-bundled:$supportLibVersion"
quicksyFreeCompatImplementation "com.android.support:support-emoji-bundled:$supportLibVersion"
implementation 'org.bouncycastle:bcmail-jdk15on:1.58'
implementation 'org.bouncycastle:bcmail-jdk15on:1.64'
//zxing stopped supporting Java 7 so we have to stick with 3.3.3
//https://github.com/zxing/zxing/issues/1170
implementation 'com.google.zxing:core:3.3.3'
@ -90,8 +90,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 364
versionName "2.7.0"
versionCode 367
versionName "2.7.1"
archivesBaseName += "-$versionName"
applicationId "eu.sum7.conversations"
resValue "string", "applicationId", applicationId

View File

@ -0,0 +1,2 @@
• Fix avatar selection on some Android 10 devices
• Fix file transfer for larger files

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Escolla o seu provedor XMPP</string>
<string name="pick_a_server">Escolle o teu provedor XMPP</string>
<string name="use_chat.sum7.eu">Utilizar chat.sum7.eu</string>
<string name="create_new_account">Crear nova conta</string>
<string name="do_you_have_an_account">Xa posúe unha conta XMPP? Este pode ser o caso se xa está a utilizar outro cliente XMPP ou utilizou Conversations previamente. Se non é así pode crear unha nova conta agora mesmo.\nTruco: Algúns provedores de correo tamén proporcionan contas XMPP.</string>
<string name="server_select_text">XMPP é unha rede de mensaxería independente do provedor. Pode utilizar este cliente con calquer provedor XMPP da súa elección.\nMais para a súa conveniencia fixemos que fose doado crear unha conta en chat.sum7.eu; un provedor especialmente axeitado para utilizar con Conversations.</string>
<string name="do_you_have_an_account">Xa posúes unha conta XMPP? Este pode ser o caso se xa estás a utilizar outro cliente XMPP ou utilizaches Conversations previamente. Se non é así podes crear unha nova conta agora mesmo.\nTruco: Algúns provedores de correo tamén proporcionan contas XMPP.</string>
<string name="server_select_text">XMPP é unha rede de mensaxería independente do provedor. Podes utilizar este cliente con calquera provedor XMPP da túa elección.\nMais para a tua conveniencia fixemos que fose doado crear unha conta en chat.sum7.eu; un provedor especialmente axeitado para utilizar con Conversations.</string>
<string name="magic_create_text_on_x">Convidáronte a %1$s. Guiarémoste no proceso para crear unha conta.\nAo escoller %1$s como provedor poderás comunicarte con usuarias de outros provedores cando lles deas o teu enderezo XMPP completo.</string>
<string name="magic_create_text_fixed">Convidáronte a %1$s. Escollemos un nome de usuaria por ti. Guiarémoste no proceso de crear unha conta.\nPoderás comunicarte con usuarias de outros provedores cando lles digas o teu enderezo XMPP completo.</string>
<string name="your_server_invitation">O convite do teu servidor</string>

View File

@ -100,7 +100,6 @@ public final class Config {
public static final boolean REMOVE_BROKEN_DEVICES = false;
public static final boolean OMEMO_PADDING = false;
public static final boolean PUT_AUTH_TAG_INTO_KEY = true;
public static final boolean TWELVE_BYTE_IV = true;
public static final boolean USE_BOOKMARKS2 = false;

View File

@ -651,7 +651,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
final boolean wipe,
final boolean firstAttempt) {
final Bundle publishOptions = account.getXmppConnection().getFeatures().pepPublishOptions() ? PublishOptions.openAccess() : null;
IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles(
final IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles(
signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(),
preKeyRecords, getOwnDeviceId(), publishOptions);
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing...");

View File

@ -115,14 +115,13 @@ public class XmppAxolotlMessage {
generator.init(128);
return generator.generateKey().getEncoded();
} catch (NoSuchAlgorithmException e) {
Log.e(Config.LOGTAG, e.getMessage());
return null;
throw new IllegalStateException(e);
}
}
private static byte[] generateIv() {
final SecureRandom random = new SecureRandom();
byte[] iv = new byte[Config.TWELVE_BYTE_IV ? 12 : 16];
final byte[] iv = new byte[12];
random.nextBytes(iv);
return iv;
}

View File

@ -1,7 +1,10 @@
package eu.siacs.conversations.entities;
import android.util.Log;
import java.io.File;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.MimeUtils;
public class DownloadableFile extends File {
@ -66,6 +69,7 @@ public class DownloadableFile extends File {
this.iv = new byte[]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf };
System.arraycopy(keyIvCombo, 0, aeskey, 0, 32);
}
Log.d(Config.LOGTAG,"using "+this.iv.length+"-byte IV for file transmission");
}
public void setKey(byte[] key) {

View File

@ -613,6 +613,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
this.getBody().length() + message.getBody().length() <= Config.MAX_DISPLAY_MESSAGE_CHARS &&
!message.isGeoUri() &&
!this.isGeoUri() &&
!message.isOOb() &&
!this.isOOb() &&
!message.treatAsDownloadable() &&
!this.treatAsDownloadable() &&
!message.getBody().startsWith(ME_COMMAND) &&

View File

@ -105,11 +105,12 @@ public class HttpUploadConnection implements Transferable {
} else {
this.mime = this.file.getMimeType();
}
final long originalFileSize = file.getSize();
this.delayed = delay;
if (Config.ENCRYPT_ON_HTTP_UPLOADED
|| message.getEncryption() == Message.ENCRYPTION_AXOLOTL
|| message.getEncryption() == Message.ENCRYPTION_OTR) {
this.key = new byte[Config.TWELVE_BYTE_IV ? 44 : 48];
this.key = new byte[44];
mXmppConnectionService.getRNG().nextBytes(this.key);
this.file.setKeyAndIv(this.key);
}
@ -128,7 +129,7 @@ public class HttpUploadConnection implements Transferable {
md5 = null;
}
this.file.setExpectedSize(file.getSize() + (file.getKey() != null ? 16 : 0));
this.file.setExpectedSize(originalFileSize + (file.getKey() != null ? 16 : 0));
message.resetFileParams();
this.mSlotRequester.request(method, account, file, mime, md5, new SlotRequester.OnSlotRequested() {
@Override

View File

@ -5,6 +5,14 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.io.CipherInputStream;
import org.bouncycastle.crypto.io.CipherOutputStream;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
@ -15,24 +23,15 @@ import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.concurrent.atomic.AtomicLong;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.CryptoHelper;
public class AbstractConnectionManager {
private static final String KEYTYPE = "AES";
private static final String CIPHERMODE = "AES/GCM/NoPadding";
private static final String PROVIDER = "BC";
private static final int UI_REFRESH_THRESHOLD = 250;
private static final AtomicLong LAST_UI_UPDATE_CALL = new AtomicLong(0);
protected XmppConnectionService mXmppConnectionService;
@ -43,10 +42,8 @@ public class AbstractConnectionManager {
public static InputStream upgrade(DownloadableFile file, InputStream is) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, NoSuchProviderException {
if (file.getKey() != null && file.getIv() != null) {
final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
SecretKeySpec keySpec = new SecretKeySpec(file.getKey(), KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(file.getIv());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
return new CipherInputStream(is, cipher);
} else {
return is;
@ -61,17 +58,15 @@ public class AbstractConnectionManager {
return os;
}
} catch (FileNotFoundException e) {
Log.d(Config.LOGTAG,"unable to create output stream", e);
Log.d(Config.LOGTAG, "unable to create output stream", e);
return null;
}
try {
final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
SecretKeySpec keySpec = new SecretKeySpec(file.getKey(), KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(file.getIv());
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
cipher.init(false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
return new CipherOutputStream(os, cipher);
} catch (Exception e) {
Log.d(Config.LOGTAG,"unable to create cipher output stream", e);
Log.d(Config.LOGTAG, "unable to create cipher output stream", e);
return null;
}
}

View File

@ -1838,7 +1838,7 @@ public class XmppConnectionService extends Service {
private void markFileDeleted(final String path) {
synchronized (FILENAMES_TO_IGNORE_DELETION) {
if (FILENAMES_TO_IGNORE_DELETION.remove(path)) {
Log.d(Config.LOGTAG,"ignored deletion of "+path);
Log.d(Config.LOGTAG, "ignored deletion of " + path);
return;
}
}
@ -1854,7 +1854,7 @@ public class XmppConnectionService extends Service {
for (Conversation conversation : getConversations()) {
deleted |= conversation.markAsDeleted(uuids);
}
for(final String uuid : uuids) {
for (final String uuid : uuids) {
evictPreview(uuid);
}
if (deleted) {
@ -3172,16 +3172,20 @@ public class XmppConnectionService extends Service {
conversation.setAttribute("accept_non_anonymous", true);
updateConversation(conversation);
}
IqPacket request = new IqPacket(IqPacket.TYPE.GET);
if (options.containsKey("muc#roomconfig_moderatedroom")) {
final boolean moderated = "1".equals(options.getString("muc#roomconfig_moderatedroom"));
options.putString("members_by_default", moderated ? "0" : "1");
}
final IqPacket request = new IqPacket(IqPacket.TYPE.GET);
request.setTo(conversation.getJid().asBareJid());
request.query("http://jabber.org/protocol/muc#owner");
sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
Data data = Data.parse(packet.query().findChild("x", Namespace.DATA));
final Data data = Data.parse(packet.query().findChild("x", Namespace.DATA));
data.submit(options);
IqPacket set = new IqPacket(IqPacket.TYPE.SET);
final IqPacket set = new IqPacket(IqPacket.TYPE.SET);
set.setTo(conversation.getJid().asBareJid());
set.query("http://jabber.org/protocol/muc#owner").addChild(data);
sendIqPacket(account, set, new OnIqPacketReceived() {
@ -4580,7 +4584,7 @@ public class XmppConnectionService extends Service {
public void evictPreview(String uuid) {
if (mBitmapCache.remove(uuid) != null) {
Log.d(Config.LOGTAG,"deleted cached preview");
Log.d(Config.LOGTAG, "deleted cached preview");
}
}

View File

@ -49,6 +49,8 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.ui.interfaces.OnAvatarPublication;
import eu.siacs.conversations.ui.util.PendingItem;
import static eu.siacs.conversations.ui.PublishProfilePictureActivity.REQUEST_CHOOSE_PICTURE;
public class PublishGroupChatProfilePictureActivity extends XmppActivity implements OnAvatarPublication {
private final PendingItem<String> pendingConversationUuid = new PendingItem<>();
private ActivityPublishProfilePictureBinding binding;
@ -93,7 +95,7 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
configureActionBar(getSupportActionBar());
this.binding.cancelButton.setOnClickListener((v) -> this.finish());
this.binding.secondaryHint.setVisibility(View.GONE);
this.binding.accountImage.setOnClickListener((v) -> this.chooseAvatar());
this.binding.accountImage.setOnClickListener((v) -> PublishProfilePictureActivity.chooseAvatar(this));
Intent intent = getIntent();
String uuid = intent == null ? null : intent.getStringExtra("uuid");
if (uuid != null) {
@ -113,7 +115,7 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
CropImage.ActivityResult result = CropImage.getActivityResult(data);
final CropImage.ActivityResult result = CropImage.getActivityResult(data);
if (resultCode == RESULT_OK) {
this.uri = result.getUri();
if (xmppConnectionServiceBound) {
@ -125,17 +127,13 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity impleme
Toast.makeText(this, error.getMessage(), Toast.LENGTH_SHORT).show();
}
}
} else if (requestCode == REQUEST_CHOOSE_PICTURE) {
if (resultCode == RESULT_OK) {
PublishProfilePictureActivity.cropUri(this, data.getData());
}
}
}
private void chooseAvatar() {
CropImage.activity()
.setOutputCompressFormat(Bitmap.CompressFormat.PNG)
.setAspectRatio(1, 1)
.setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
.start(this);
}
@Override
public void onAvatarPublicationSucceeded() {
runOnUiThread(() -> {

View File

@ -1,8 +1,10 @@
package eu.siacs.conversations.ui;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.StringRes;
import android.util.Log;
@ -26,6 +28,8 @@ import eu.siacs.conversations.utils.PhoneHelper;
public class PublishProfilePictureActivity extends XmppActivity implements XmppConnectionService.OnAccountUpdate, OnAvatarPublication {
public static final int REQUEST_CHOOSE_PICTURE = 0x1337;
private ImageView avatar;
private TextView hintOrWarning;
private TextView secondaryHint;
@ -106,7 +110,7 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
}
finish();
});
this.avatar.setOnClickListener(v -> chooseAvatar());
this.avatar.setOnClickListener(v -> chooseAvatar(this));
this.defaultUri = PhoneHelper.getProfilePictureUri(getApplicationContext());
if (savedInstanceState != null) {
this.avatarUri = savedInstanceState.getParcelable("uri");
@ -139,15 +143,28 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
Toast.makeText(this, error.getMessage(), Toast.LENGTH_SHORT).show();
}
}
} else if (requestCode == REQUEST_CHOOSE_PICTURE) {
if (resultCode == RESULT_OK) {
cropUri(this, data.getData());
}
}
}
private void chooseAvatar() {
CropImage.activity()
.setOutputCompressFormat(Bitmap.CompressFormat.PNG)
.setAspectRatio(1, 1)
.setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
.start(this);
public static void chooseAvatar(final Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
activity.startActivityForResult(
Intent.createChooser(intent, activity.getString(R.string.attach_choose_picture)),
REQUEST_CHOOSE_PICTURE
);
} else {
CropImage.activity()
.setOutputCompressFormat(Bitmap.CompressFormat.PNG)
.setAspectRatio(1, 1)
.setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
.start(activity);
}
}
@Override
@ -181,10 +198,7 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
final Uri uri = intent != null ? intent.getData() : null;
if (uri != null && handledExternalUri.compareAndSet(false,true)) {
CropImage.activity(uri).setOutputCompressFormat(Bitmap.CompressFormat.PNG)
.setAspectRatio(1, 1)
.setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
.start(this);
cropUri(this, uri);
return;
}
@ -194,6 +208,13 @@ public class PublishProfilePictureActivity extends XmppActivity implements XmppC
configureActionBar(getSupportActionBar(), !this.mInitialAccountSetup && !handledExternalUri.get());
}
public static void cropUri(final Activity activity, final Uri uri) {
CropImage.activity(uri).setOutputCompressFormat(Bitmap.CompressFormat.PNG)
.setAspectRatio(1, 1)
.setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
.start(activity);
}
protected void loadImageIntoPreview(Uri uri) {
Bitmap bm = null;

View File

@ -251,7 +251,7 @@ public class Resolver {
ResolverResult<D> results = resolveWithFallback(hostname, type, authenticated);
for (D record : results.getAnswersOrEmptySet()) {
Result resolverResult = Result.fromRecord(srv, directTls);
resolverResult.authenticated = results.isAuthenticData() && authenticated;
resolverResult.authenticated = results.isAuthenticData() && authenticated; //TODO technically it doesnt matter if the IP was authenticated
resolverResult.ip = record.getInetAddress();
list.add(resolverResult);
}

View File

@ -292,6 +292,7 @@
<string name="conference_shutdown">A csoportos csevegés le lett állítva</string>
<string name="conference_unknown_error">Többé már nincs ebben a csoportos csevegésben</string>
<string name="using_account">használt fiók: %s</string>
<string name="hosted_on">kiszolgálva itt: %s</string>
<string name="checking_x"> %s ellenőrzése a HTTP gépen</string>
<string name="not_connected_try_again">Nincs kapcsolódva. Próbálja meg később újra</string>
<string name="check_x_filesize"> %s méretének ellenőrzése</string>
@ -556,6 +557,7 @@
<string name="pref_privacy">Adatvédelem</string>
<string name="pref_theme_options">Téma</string>
<string name="pref_theme_options_summary">Színpaletta kiválasztása</string>
<string name="pref_theme_automatic">Automatikus</string>
<string name="pref_theme_light">Világos téma</string>
<string name="pref_theme_dark">Sötét téma</string>
<string name="unable_to_connect_to_keychain">Nem sikerült kapcsolódni a OpenKeychain alkalmazáshoz</string>

View File

@ -292,6 +292,7 @@
<string name="conference_shutdown">La chat di gruppo è stata chiusa</string>
<string name="conference_unknown_error">Non sei più in questa chat di gruppo</string>
<string name="using_account">usando laccount %s</string>
<string name="hosted_on">ospitato su %s</string>
<string name="checking_x">Controllo %s su host HTTP</string>
<string name="not_connected_try_again">Non sei connesso. Riprova più tardi</string>
<string name="check_x_filesize">Controlla dimensione %s</string>

View File

@ -292,6 +292,7 @@
<string name="conference_shutdown">Konferencja została zamknięta</string>
<string name="conference_unknown_error">Nie uczestniczysz już w tej konferencji</string>
<string name="using_account">używając konta %s</string>
<string name="hosted_on">udostępnione na %s</string>
<string name="checking_x">Sprawdzanie %s na hoście HTTP</string>
<string name="not_connected_try_again">Brak połączenia. Spróbuj ponownie później</string>
<string name="check_x_filesize">Sprawdź rozmiar %s</string>

View File

@ -151,6 +151,7 @@
<string name="account_status_regis_conflict">Användarnamn används redan</string>
<string name="account_status_regis_success">Registrering klar</string>
<string name="account_status_regis_not_sup">Servern stödjer inte registrering</string>
<string name="account_status_tls_error">TLS-förhandling misslyckades</string>
<string name="account_status_policy_violation">Kränkning av policy</string>
<string name="account_status_incompatible_server">Inkompatibel server</string>
<string name="account_status_stream_error">Strömningsfel</string>
@ -322,6 +323,7 @@
<string name="regenerate_omemo_key">Regenerera OMEMO-nyckel</string>
<string name="clear_other_devices">Rensa enheter</string>
<string name="clear_other_devices_desc">Är du säker på att du vill rensa alla andra enheter från OMEMO-annonsering? Nästa gång dina enheter ansluter kommer de att återannonsera sig, men de kanske inte tar emot enheter under tiden.</string>
<string name="error_trustkeys_title">Något gick fel</string>
<string name="fetching_history_from_server">Hämtar historik från server</string>
<string name="no_more_history_on_server">Ingen mer historik på server</string>
<string name="updating">Uppdaterar…</string>
@ -350,6 +352,7 @@
<string name="you_are_not_participating">Du deltar ej</string>
<string name="never">Aldrig</string>
<string name="until_further_notice">Tills vidare</string>
<string name="mark_as_read">Läsmarkera</string>
<string name="pref_input_options">Input</string>
<string name="pref_enter_is_send">Skicka med enter</string>
<string name="pref_enter_is_send_summary">Använd enter-knappen för att skicka meddelande</string>
@ -493,6 +496,7 @@
<string name="pref_privacy">Privatliv</string>
<string name="pref_theme_options">Tema</string>
<string name="pref_theme_options_summary">Välj färgschema</string>
<string name="pref_theme_automatic">Automatisk</string>
<string name="pref_theme_light">Ljust tema</string>
<string name="pref_theme_dark">Mörkt tema</string>
<string name="unable_to_connect_to_keychain">Kan inte ansluta till OpenKeychain</string>
@ -543,6 +547,8 @@
<string name="i_followed_this_link_from_a_trusted_source">Jag följde denna länk från en trovärdig källa</string>
<string name="verifying_omemo_keys_trusted_source">Du håller på att verifiera OMEMO-nyckeln för %1$s efter att du följt en länk. Detta är endast säkert om du följde länken från en trovärdig källa där endast %2$s kan ha publiserat denna länk.</string>
<string name="verify_omemo_keys">Verifiera OMEMO-nycklar</string>
<string name="show_inactive_devices">Visa inaktiva</string>
<string name="hide_inactive_devices">Dölj inaktiva</string>
<string name="distrust_omemo_key">Lita ej på enhet</string>
<string name="distrust_omemo_key_text">Är du säker på att du vill ta bort verifieringen av denna enhet?\nDenna enhet och meddelanden som kommer från enheten kommer att markeras som ej pålitliga.</string>
<plurals name="seconds">
@ -586,27 +592,74 @@
<string name="session_failure">Sessionsfel</string>
<string name="today">Idag</string>
<string name="yesterday">Igår</string>
<string name="pref_validate_hostname">Bekräfta värdnamn med DNSSEC</string>
<string name="certificate_does_not_contain_jid">Certifikatet innehåller ej en XMPP-adress</string>
<string name="attach_record_video">Spela in video</string>
<string name="message">Meddelande</string>
<string name="mtm_accept_cert">Godkänn okänt certifikat?</string>
<string name="mtm_trust_anchor">Servercertifikatet är inte signerat av en känd certifikatutfärdare.</string>
<string name="mtm_connect_anyway">Vill du ansluta ändå?</string>
<string name="mtm_cert_details">Certifikatdetaljer:</string>
<string name="draft">Utkast:</string>
<string name="create_shortcut">Skapa genväg</string>
<string name="small">Liten</string>
<string name="medium">Mellan</string>
<string name="large">Stor</string>
<string name="action_copy_location">Kopiera plats</string>
<string name="action_share_location">Dela plats</string>
<string name="title_activity_share_location">Dela plats</string>
<string name="title_activity_show_location">Visa plats</string>
<string name="share">Dela</string>
<string name="please_wait">Var god dröj...</string>
<string name="no_microphone_permission">Conversations behöver tillgång till mikrofonen</string>
<string name="gif">GIF</string>
<string name="copy_jabber_id">Kopiera XMPP-adress</string>
<string name="nickname">Smeknamn</string>
<string name="group_chat_name">Namn</string>
<string name="providing_a_name_is_optional">Att ange ett namn är valfritt</string>
<string name="create_dialog_group_chat_name">Gruppchattens namn</string>
<string name="unable_to_save_recording">Kan inte spara inspelning</string>
<string name="group_chat_members">Deltagare</string>
<string name="video_360p">Mellan (360p)</string>
<string name="video_720p">Hög (720p)</string>
<string name="choose_a_country">Välj ett land</string>
<string name="phone_number">telefonnummer</string>
<string name="verify_your_phone_number">Bekräfta ditt telefonnummer</string>
<string name="yes">Ja</string>
<string name="no">Nej</string>
<string name="verifying">Bekräftar...</string>
<string name="unknown_api_error_network">Okänt nätverksfel.</string>
<string name="too_many_attempts">För många försök</string>
<string name="the_app_is_out_of_date">Du använder en föråldrad version av denna app.</string>
<string name="your_name">Ditt namn</string>
<string name="enter_your_name">Skriv in ditt namn</string>
<string name="reject_request">Avslå begäran</string>
<string name="install_orbot">Installera Orbot</string>
<string name="start_orbot">Starta Orbot</string>
<string name="ebook">e-bok</string>
<string name="open_with">Öppna med...</string>
<string name="choose_account">Välj konto</string>
<string name="create_group_chat">Skapa gruppchatt</string>
<string name="create_private_group_chat">Skapa sluten gruppchatt</string>
<string name="create_dialog_channel_name">Kanalnamn</string>
<string name="xmpp_address">XMPP-adress</string>
<string name="please_enter_name">Vänligen ange ett namn på kanalen</string>
<string name="please_enter_xmpp_address">Ange en XMPP-adress</string>
<string name="this_is_an_xmpp_address">Detta är en XMPP-adress. Ange ett namn.</string>
<string name="channel_already_exists">Denna kanal finns redan</string>
<string name="joined_an_existing_channel">Du har gått med i en befintlig kanal</string>
<string name="jabber_ids_are_visible_to_admins">XMPP-adresser är synliga för administratörer.</string>
<string name="jabber_ids_are_visible_to_anyone">XMPP-adresser är synliga för alla.</string>
<string name="no_users_hint_group_chat">Denna slutna gruppchatt har inga deltagare.</string>
<string name="manage_permission">Hantera rättigheter</string>
<string name="file_too_large">För stor fil</string>
<string name="attach">Bifoga</string>
<string name="discover_channels">Upptäck kanaler</string>
<string name="i_already_have_an_account">Jag har redan ett konto</string>
<string name="add_existing_account">Lägg till befintligt konto</string>
<string name="register_new_account">Skapa nytt konto</string>
<string name="this_looks_like_a_domain">Detta verkar vara ett domännamn</string>
<string name="add_anway">Lägg till ändå</string>
<string name="this_looks_like_channel">Detta ser ut som en kanaladress</string>
<string name="category_about">Om</string>
</resources>

View File

@ -1,26 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="crash_report_title">Quicksy fallou</string>
<string name="crash_report_message">Ao enviar lotes de rexistro está a axudar no desenvolvemento de Quicksy\n<b>Aviso:</b> vai utilizar a súa conta XMPP para enviar o rexistro ao equipo de desenvolvemento.</string>
<string name="openkeychain_required_long">Quicksy utiliza unha app de terceiros chamada <b>OpenKeychain</b> para cifrar e descifrar as mensaxes e xestionar a súas chaves públicas.\n\nOpenKeychain en licenza GPLv3 e está dispoñible en F-Droid e Google Play.\n\n<small>(Por favor, reinicie Quicksy ao rematar).</small></string>
<string name="contact_has_no_pgp_key">Quicksy non pode cifrar as súas mensaxes porque os seu contacto non publicou as sua chave pública.\n\n<small>Por favor, solicite ao contacto configure OpenPGP.</small></string>
<string name="contacts_have_no_pgp_keys">Quicksy non pode cifrar as súas mensaxes porque os seus contactos non están a publicar a súa chave pública.\n\n<small>Por favor, pídalle aos seus contactos que configuren OpenPGP.</small></string>
<string name="crash_report_message">Ao enviar lotes de rexistro estás a axudar no desenvolvemento de Quicksy\n<b>Aviso:</b> vas utilizar a tua conta XMPP para enviar o rexistro ao equipo de desenvolvemento.</string>
<string name="openkeychain_required_long">Quicksy utiliza unha app de terceiros chamada <b>OpenKeychain</b> para cifrar e descifrar as mensaxes e xestionar a tuas chaves públicas.\n\nOpenKeychain ten licenza GPLv3 e está dispoñible en F-Droid e Google Play.\n\n<small>(Por favor, reinicia Quicksy ao rematar).</small></string>
<string name="contact_has_no_pgp_key">Quicksy non pode cifrar as tuas mensaxes porque o teu contacto non publicou as sua chave pública.\n\n<small>Por favor, solicita ao contacto que configure OpenPGP.</small></string>
<string name="contacts_have_no_pgp_keys">Quicksy non pode cifrar as tuas mensaxes porque os teus contactos non están a publicar a súa chave pública.\n\n<small>Por favor, pídelle aos teus contactos que configuren OpenPGP.</small></string>
<string name="pref_notification_grace_period_summary">O período de tempo que Quicksy permanece acalado tras ver actividade en outro dispositivo</string>
<string name="pref_never_send_crash_summary">Enviando trazas de rexistro está axudando ao desenvolvemento de Quicksy</string>
<string name="pref_never_send_crash_summary">Enviando trazas de rexistro estás axudando ao desenvolvemento de Quicksy</string>
<string name="no_storage_permission">Quicksy precisa acceso ao almacenamento externo</string>
<string name="no_camera_permission">Quicksy precisa acceso a cámara</string>
<string name="battery_optimizations_enabled_explained">O seu dispositivo está a realizar optimizacións de batería intensivas con Quicksy que poderían levar a que as notificacións tarden en chegar ou que as mensaxes se perdan.\nRecomendamos desactivalas.</string>
<string name="battery_optimizations_enabled_dialog">O seu dispositivo está a realizar optimizacións de batería moi fortes con Quicksy que poderían levar a que se retrasen as notificacións ou que se perdan mensaxes.\n\nPediráselle que as desactive.</string>
<string name="pref_broadcast_last_activity_summary">Permitir a todos os seus contactos saber cando está a utilizar Quicksy</string>
<string name="data_saver_enabled_explained">O seu sistema operativo está restrinxindo o acceso a internet en segundo plano para Quicksy. Para recibir notificacións ou novas mensaxes debería permitir a Quicksy acceso non limitado cando se activa o aforro de datos.\nQuicksy esforzarase en aforrar datos cando sexa posible.</string>
<string name="device_does_not_support_data_saver">O seu dispositivo non soporta a desactivación do aforro de datos para Quicksy.</string>
<string name="huawei_protected_apps_summary">Para seguir recibindo notificacións, mesmo coa pantalla apagada, precisa engadir a Quicksy na lista de apps protexidas.</string>
<string name="error_trustkey_general">Quicksy non pode enviar mensaxes cifradas a %1$s. Esto podería deberse a que o seu contacto utiliza un servidor antigo ou un cliente sen soporte OMEMO.</string>
<string name="battery_optimizations_enabled_explained">O teu dispositivo está a realizar optimizacións de batería intensivas con Quicksy que poderían levar a que as notificacións tarden en chegar ou que as mensaxes se perdan.\nRecomendamos desactivalas.</string>
<string name="battery_optimizations_enabled_dialog">O teu dispositivo está a realizar optimizacións de batería moi fortes con Quicksy que poderían levar a que se retrasen as notificacións ou que se perdan mensaxes.\n\nVaiche solicitar que as desactives.</string>
<string name="pref_broadcast_last_activity_summary">Permitir a todos os teus contactos saber cando estás a utilizar Quicksy</string>
<string name="data_saver_enabled_explained">O teu sistema operativo está restrinxindo o acceso a internet en segundo plano para Quicksy. Para recibir notificacións ou novas mensaxes deberías permitir a Quicksy acceso non limitado cando se activa o aforro de datos.\nQuicksy esforzarase en aforrar datos cando sexa posible.</string>
<string name="device_does_not_support_data_saver">O teu dispositivo non soporta a desactivación do aforro de datos para Quicksy.</string>
<string name="huawei_protected_apps_summary">Para seguir recibindo notificacións, mesmo coa pantalla apagada, precisas engadir a Quicksy na lista de apps protexidas.</string>
<string name="error_trustkey_general">Quicksy non pode enviar mensaxes cifradas a %1$s. Esto podería deberse a que o teu contacto utiliza un servidor antigo ou un cliente sen soporte OMEMO.</string>
<string name="no_microphone_permission">Quicksy precisa acceder ao micrófono</string>
<string name="foreground_service_channel_description">Esta categoría de notificacións utilízase para mostrar unha notificación permanente que indica que Quicksy está funcionando.</string>
<string name="set_profile_picture">Imaxe de perfil Quicksy</string>
<string name="not_available_in_your_country">Quicksy non está dispoñible no seu país.</string>
<string name="unable_to_verify_server_identity">Non se puido validar a identidade do servidor.</string>
<string name="not_available_in_your_country">Quicksy non está dispoñible no teu país.</string>
<string name="unable_to_verify_server_identity">Non se puido verificar a identidade do servidor.</string>
<string name="unknown_security_error">Fallo de seguridade descoñecido.</string>
<string name="timeout_while_connecting_to_server">Caducou a conexión mentras conectaba co servidor.</string>
</resources>