Merge tag '2.10.0' into develop

This commit is contained in:
Geno 2021-09-12 23:58:59 +02:00
commit abe3718353
102 changed files with 2377 additions and 869 deletions

38
.github/workflows/android.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: Android CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
- name: Download WebRTC
run: mkdir libs && wget -O libs/libwebrtc-m92.aar https://gultsch.de/files/libwebrtc-m92.aar
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build Quicksy (Compat)
run: ./gradlew assembleQuicksyFreeCompatDebug
- name: Build Quicksy (System)
run: ./gradlew assembleQuicksyFreeSystemDebug
- name: Build Conversations (Compat)
run: ./gradlew assembleConversationsFreeCompatDebug
- name: Build Conversations (System)
run: ./gradlew assembleConversationsFreeSystemDebug
- uses: actions/upload-artifact@v2
with:
name: Conversations all-flavors (debug)
path: ./build/outputs/apk/**/debug/Conversations-*.apk

View File

@ -1,22 +0,0 @@
language: android
jdk:
- oraclejdk8
android:
components:
- platform-tools
- tools
- build-tools-28.0.3
- extra-google-google_play_services
licenses:
- '.+'
before_script:
- mkdir libs
- wget -O libs/libwebrtc-m90.aar https://gultsch.de/files/libwebrtc-m90.aar
script:
- ./gradlew assembleQuicksyFreeCompatDebug
- ./gradlew assembleQuicksyFreeSystemDebug
- ./gradlew assembleConversationsFreeCompatDebug
- ./gradlew assembleConversationsFreeSystemDebug
before_install:
- yes | sdkmanager "platforms;android-28"

View File

@ -1,5 +1,11 @@
# Changelog
### Version 2.10.0
* Show black bars when remote video does not match aspect ratio of screen
* Improve search performance
* Add setting to prevent screenshots
### Version 2.9.13
* minor A/V improvements

View File

@ -371,7 +371,7 @@ Unfortunately we dont have a recommendation for iPhones right now. There are
**Note:** Starting with version 2.8.0 you will need to compile libwebrtc.
[Instructions](https://webrtc.github.io/webrtc-org/native-code/android/) can be found on the WebRTC
website. Place the resulting libwebrtc.aar in the `libs/` directory. The PlayStore release currently
uses the stable M81 release and renamed the file name to `libwebrtc-m81.aar` put potentially you can
uses the stable M90 release and renamed the file name to `libwebrtc-m90.aar` put potentially you can
reference any file name by modifying `build.gradle`.
Make sure to have ANDROID_HOME point to your Android SDK. Use the Android SDK Manager to install missing dependencies.

View File

@ -5,10 +5,10 @@ import com.android.build.OutputFile
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath 'com.android.tools.build:gradle:7.0.2'
}
}
@ -16,8 +16,8 @@ apply plugin: 'com.android.application'
repositories {
google()
jcenter()
mavenCentral()
jcenter()
}
configurations {
@ -35,19 +35,19 @@ configurations {
dependencies {
implementation 'androidx.viewpager:viewpager:1.0.0'
playstoreImplementation('com.google.firebase:firebase-messaging:21.0.1') {
playstoreImplementation('com.google.firebase:firebase-messaging:22.0.0') {
exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
}
conversationsPlaystoreCompatImplementation("com.android.installreferrer:installreferrer:2.2")
conversationsPlaystoreSystemImplementation("com.android.installreferrer:installreferrer:2.2")
quicksyPlaystoreCompatImplementation 'com.google.android.gms:play-services-auth-api-phone:17.5.0'
quicksyPlaystoreSystemImplementation 'com.google.android.gms:play-services-auth-api-phone:17.5.0'
quicksyPlaystoreCompatImplementation 'com.google.android.gms:play-services-auth-api-phone:17.5.1'
quicksyPlaystoreSystemImplementation 'com.google.android.gms:play-services-auth-api-phone:17.5.1'
implementation 'org.sufficientlysecure:openpgp-api:10.0'
implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.exifinterface:exifinterface:1.3.2'
implementation 'androidx.exifinterface:exifinterface:1.3.3'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.emoji:emoji:1.1.0'
@ -65,19 +65,19 @@ dependencies {
implementation 'com.makeramen:roundedimageview:2.3.0'
implementation "com.wefika:flowlayout:0.4.1"
implementation 'net.ypresto.androidtranscoder:android-transcoder:0.3.0'
implementation 'org.jxmpp:jxmpp-jid:0.6.4'
implementation 'org.jxmpp:jxmpp-jid:1.0.1'
implementation 'org.osmdroid:osmdroid-android:6.1.10'
implementation 'org.hsluv:hsluv:0.2'
implementation 'org.conscrypt:conscrypt-android:2.2.1'
implementation 'org.conscrypt:conscrypt-android:2.5.2'
implementation 'me.drakeet.support:toastcompat:1.1.0'
implementation "com.leinardi.android:speed-dial:2.0.1"
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.okhttp3:okhttp:4.9.1"
implementation 'com.google.guava:guava:30.1-android'
implementation 'com.google.guava:guava:30.1.1-android'
quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.12.18'
// implementation fileTree(include: ['libwebrtc-m90.aar'], dir: 'libs')
// implementation fileTree(include: ['libwebrtc-m92.aar'], dir: 'libs')
implementation 'org.webrtc:google-webrtc:1.0.32006'
}
@ -93,8 +93,8 @@ android {
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
versionCode 42015
versionName "2.9.13"
versionCode 42018
versionName "2.10.0"
archivesBaseName += "-$versionName"
applicationId "eu.sum7.conversations"
resValue "string", "applicationId", applicationId
@ -105,19 +105,13 @@ android {
configurations {
compile.exclude group: 'org.jetbrains' , module:'annotations'
implementation.exclude group: 'org.jetbrains' , module:'annotations'
}
dataBinding {
enabled true
}
dexOptions {
// Skip pre-dexing when running on Travis CI or when disabled via -Dpre-dex=false.
preDexLibraries = preDexEnabled && !travisBuild
jumboMode true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8

View File

@ -453,12 +453,19 @@
<xmpp:version>0.2.0</xmpp:version>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0454.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.1.0</xmpp:version>
</xmpp:SupportedXep>
</implements>
<release>
<Version>
<revision>2.5.8</revision>
<created>2019-09-12</created>
<file-release rdf:resource="https://github.com/iNPUTmice/Conversations/archive/2.5.8.tar.gz"/>
<revision>2.9.13</revision>
<created>2021-05-03</created>
<file-release rdf:resource="https://github.com/iNPUTmice/Conversations/archive/2.9.13.tar.gz"/>
</Version>
</release>
</Project>

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip

9
proguard-rules.pro vendored
View File

@ -26,6 +26,15 @@
-dontwarn java.lang.**
-dontwarn javax.lang.**
-dontwarn com.android.org.conscrypt.SSLParametersImpl
-dontwarn org.apache.harmony.xnet.provider.jsse.SSLParametersImpl
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
-keepclassmembers class eu.siacs.conversations.http.services.** {
!transient <fields>;
}

View File

@ -6,7 +6,6 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
@ -30,6 +29,7 @@ import eu.siacs.conversations.databinding.ActivityImportBackupBinding;
import eu.siacs.conversations.databinding.DialogEnterPasswordBinding;
import eu.siacs.conversations.services.ImportBackupService;
import eu.siacs.conversations.ui.adapter.BackupFileAdapter;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
public class ImportBackupActivity extends ActionBarActivity implements ServiceConnection, ImportBackupService.OnBackupFilesLoaded, BackupFileAdapter.OnItemClickedListener, ImportBackupService.OnBackupProcessed {
@ -56,6 +56,12 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
this.backupFileAdapter.setOnItemClickedListener(this);
}
@Override
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.import_backup, menu);
@ -125,7 +131,8 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
try {
final ImportBackupService.BackupFile backupFile = ImportBackupService.BackupFile.read(this, uri);
showEnterPasswordDialog(backupFile, finishOnCancel);
} catch (IOException | IllegalArgumentException e) {
} catch (final IOException | IllegalArgumentException e) {
Log.d(Config.LOGTAG, "unable to open backup file " + uri, e);
Snackbar.make(binding.coordinator, R.string.not_a_backup_file, Snackbar.LENGTH_LONG).show();
}
}
@ -181,6 +188,7 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (resultCode == RESULT_OK) {
if (requestCode == 0xbac) {
openBackupFileFromUri(intent.getData(), false);
@ -225,15 +233,17 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_open_backup_file) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
}
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(intent, getString(R.string.open_backup)), 0xbac);
openBackupFile();
return true;
}
return super.onOptionsItemSelected(item);
}
private void openBackupFile() {
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(intent, getString(R.string.open_backup)), 0xbac);
}
}

View File

@ -3,4 +3,18 @@
<string name="pick_a_server">اختر مزود خدمة XMPP الخاص بك</string>
<string name="use_chat.sum7.eu">استخدِم chat.sum7.eu</string>
<string name="create_new_account">أنشئ حسابًا جديدًا</string>
<string name="do_you_have_an_account">هل تملك حساب XMPP؟؟ قد يكون ذلك ممكنا لو كنت تستعمل خدمة XMPP أخرى أو إستعملت تطبيق كونفرسايشنز سابقا. أو يمكنك صنع حساب XMPP جديد الآن.
ملاحظة: بعض خدمات البريد الإلكتروني تقدم حسابات XMPP.</string>
<string name="server_select_text">XMPP هي خدمة مستقلة للتواصل بشبكة الرسائل المباشرة. يمكنك إستعمال هذه الخدمة مع أي خادم XMPP تختاره.
سعيا لراحتك جعلنا خلق حساب في كونفيرسايشنز سهلا مع مقدم خدمة خاص بالإستعمال مع كونفيرسايشنز.</string>
<string name="magic_create_text_on_x">لقد تمت دعوتك لـ %1$s. سيتم دلّك على طريقة صنع حساب.
عندما تختار %1$sكمقدّم خدمة سيصبح من الممكن لك التواصل مع مستعملين من أي خادم آخر عن طريق إعطائهم عنوانك الكامل على XMPP.</string>
<string name="magic_create_text_fixed">تمّت دعوتك إلى %1$s. تم إختيار إسم مستخدم خاص بك. سيتم قيادتك عبر طريقة صنع حساب.
سيمكنك التواصل مع مستخدمين من مزودين آخرين عبر إعطائهم كامل عنوانك XMPP.</string>
<string name="your_server_invitation">سيرفر دعوتك</string>
<string name="improperly_formatted_provisioning">لم يتم التقاط الكود بطريقة جيّدة</string>
<string name="tap_share_button_send_invite">إضغط على زر مشاركة لترسل إلى المتصل بك دعوة إلى %1$s.</string>
<string name="if_contact_is_nearby_use_qr">إذا كان المتصل بك قريبا منك، يمكنه فحص الكود بالأسفل ليقبل دعوتك.</string>
<string name="easy_invite_share_text">إنظم %1$s وتحدّث معي: %2$s</string>
<string name="share_invite_with">شارك إستدعاء مع...</string>
</resources>

View File

@ -10,7 +10,7 @@
<string name="your_server_invitation">Η πρόσκλησή σας στον διακομιστή</string>
<string name="improperly_formatted_provisioning">Λάθος μορφοποίηση κώδικα παροχής</string>
<string name="tap_share_button_send_invite">Πατήστε το πλήκτρο διαμοιρασμού για να στείλετε στην επαφή σας μια πρόσκληση στο %1$s.</string>
<string name="if_contact_is_nearby_use_qr">Αν η επαφή σας βρίσκεται κοντά σας, μπορεί επίσης να σκανάρει τον κωδικό παρακάτω για να αποδεχτεί την πρόσκλησή σας.</string>
<string name="if_contact_is_nearby_use_qr">Αν η επαφή σας βρίσκεται κοντά σας, μπορεί επίσης να σαρώσει τον κωδικό παρακάτω για να αποδεχτεί την πρόσκλησή σας.</string>
<string name="easy_invite_share_text">Μπείτε στο %1$s και συνομιλήστε μαζί μου: %2$s</string>
<string name="share_invite_with">Διαμοιρασμός πρόσκλησης με...</string>
</resources>

View File

@ -2,11 +2,11 @@
<resources>
<string name="pick_a_server">XMPP プロバイダーを選択してください</string>
<string name="use_chat.sum7.eu">chat.sum7.eu を利用する</string>
<string name="create_new_account">アカウントを作成</string>
<string name="do_you_have_an_account">XMPPアカウントをお持ちですか既にほかのXMPPクライアントを利用しているか、Conversationsを利用したことがある場合はこちら。初めての方は、今すぐ新しいXMPPアカウントを作成できます。\nヒント: eメールのプロバイダーがXMPPアカウントも提供している場合があります。</string>
<string name="server_select_text">XMPPは、プロバイダーに依存しないインスタントメッセージのプロトコルです。XMPPサーバーならどこでも、このクライアントを使用することができます。\nよろしければ、Conversationsに最適化されたプロバイダーchat.sum7.euで簡単にアカウントを作成することもできます。</string>
<string name="create_new_account">新しいアカウントを作成</string>
<string name="do_you_have_an_account">XMPP アカウントをお持ちですか?既にほかの XMPP クライアントを利用しているか、 Conv6sations を利用したことがある場合はこちら。初めての方は、今すぐ新しい XMPP アカウントを作成できます。\nヒント: e メールのプロバイダーが XMPP アカウントも提供している場合があります。</string>
<string name="server_select_text">XMPP は、プロバイダーに依存しないインスタントメッセージのプロトコルです。 XMPP サーバーならどこでも、このクライアントを使用することができます。\nよろしければ、 Conv6sations に最適化されたプロバイダー chat.sum7.eu で簡単にアカウントを作成することもできます。</string>
<string name="magic_create_text_on_x">%1$s へ招待されました。アカウント作成手順をご案内します。 \n%1$s をプロバイダーに選択してほかのプロバイダーのユーザーと会話するには、 XMPP のフルアドレスを相手にお知らせください。</string>
<string name="magic_create_text_fixed">%1$sへ招待されました。ユーザーネームは既に選択されています。アカウント作成手順をご案内します。 \nほかのプロバイダーのユーザーと会話するには、XMPPのフルアドレスを相手にお知らせください。</string>
<string name="magic_create_text_fixed">%1$s へ招待されました。ユーザー名は既に選択されています。アカウント作成手順をご案内します。 \nほかのプロバイダーのユーザーと会話するには、 XMPP のフルアドレスを相手にお知らせください。</string>
<string name="your_server_invitation">サーバーの招待</string>
<string name="improperly_formatted_provisioning">仮コードの書式が不正です</string>
<string name="tap_share_button_send_invite">共有ボタンを叩いて、連絡先の %1$s に招待を送信する。</string>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Vyberte si svojho XMPP poskytovateľa</string>
<string name="use_conversations.im">Použiť conversations.im</string>
<string name="create_new_account">Vytvoriť nové konto</string>
<string name="do_you_have_an_account">Máte už svoje XMPP konto? Môže to tak byť v prípade, že už používate iného klienta XMPP alebo ste predtým používali Conversations. Ak nie, môžete si vytvoriť nové XMPP konto práve teraz.\nHint: Niektorí poskytovatelia emailu zároveň poskytujú aj XMPP kontá.</string>
<string name="server_select_text">XMPP je sieť pre okamžité správy nezávislá od poskytovateľa. Tohto klienta môžete používať s akýmkoľvek XMPP serverom, ktorý si vyberiete..\nAvšak pre vaše pohodlie sme zjednodušili vytvorenie konta na conversations.im¹; poskytovateľ špeciálne vhodný na používanie s Conversations.</string>
<string name="magic_create_text_on_x">Boli ste pozvaný do %1$s. Prevedieme vás procesom vytvorenia konta..\nPo výbere %1$s ako poskytovateľa, budete môcť komunikovať s užívateľmi iných poskytovateľov tak, že im dáte vašu úplnú XMPP adresu.</string>
<string name="magic_create_text_fixed">Boli ste pozvaný do %1$s . Užívateľské meno vám už bolo vopred vybrané. Prevedieme vás procesom vytvorenia konta..\nBudete môcť komunikovať s užívateľmi iných poskytovateľov tak, že im dáte vašu úplnú XMPP adresu.</string>
<string name="tap_share_button_send_invite">Ťuknite na tlačidlo zdieľať na odoslanie pozvánky do %1$s vášmu kontaktu.</string>
<string name="if_contact_is_nearby_use_qr">Ak je váš kontakt blízko, na prijatie vašej pozvánky si môže nasnímať kód nižšie.</string>
<string name="easy_invite_share_text">Pripojte sa k %1$sa rozprávajte sa so mnou: %2$s</string>
<string name="share_invite_with">Zdieľať pozvánku s...</string>
</resources>

View File

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Välj din XMPP leverantör</string>
<string name="use_chat.sum7.eu">Använd chat.sum7.eu</string>
<string name="create_new_account">Skapa nytt konto</string>
<string name="your_server_invitation">Din server inbjudan</string>
<string name="share_invite_with">Dela inbjudan med...</string>
</resources>

View File

@ -118,6 +118,7 @@ public final class Config {
public static final boolean ENCRYPT_ON_HTTP_UPLOADED = false;
public static final boolean X509_VERIFICATION = false; //use x509 certificates to verify OMEMO keys
public static final boolean REQUIRE_RTP_VERIFICATION = false; //require a/v calls to be verified with OMEMO
public static final boolean ONLY_INTERNAL_STORAGE = false; //use internal storage instead of sdcard to save attachments
@ -199,4 +200,9 @@ public final class Config {
public final static float LOCATION_FIX_SPACE_DELTA = 10; // m
public final static int LOCATION_FIX_SIGNIFICANT_TIME_DELTA = 1000 * 60 * 2; // ms
}
// How deep nested quotes should be displayed. '2' means one quote nested in another.
public static final int QUOTE_MAX_DEPTH = 7;
// How deep nested quotes should be created on quoting a message.
public static final int QUOTING_MAX_DEPTH = 1;
}

View File

@ -8,7 +8,12 @@ import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.whispersystems.libsignal.IdentityKey;
@ -733,16 +738,22 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
axolotlStore.setFingerprintStatus(fingerprint, status);
}
private void verifySessionWithPEP(final XmppAxolotlSession session) {
private ListenableFuture<XmppAxolotlSession> verifySessionWithPEP(final XmppAxolotlSession session) {
Log.d(Config.LOGTAG, "trying to verify fresh session (" + session.getRemoteAddress().getName() + ") with pep");
final SignalProtocolAddress address = session.getRemoteAddress();
final IdentityKey identityKey = session.getIdentityKey();
final Jid jid;
try {
IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(Jid.of(address.getName()), address.getDeviceId());
mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
Pair<X509Certificate[], byte[]> verification = mXmppConnectionService.getIqParser().verification(packet);
jid = Jid.of(address.getName());
} catch (final IllegalArgumentException e) {
fetchStatusMap.put(address, FetchStatus.SUCCESS);
finishBuildingSessionsFromPEP(address);
return Futures.immediateFuture(session);
}
final SettableFuture<XmppAxolotlSession> future = SettableFuture.create();
final IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(jid, address.getDeviceId());
mXmppConnectionService.sendIqPacket(account, packet, (account, response) -> {
Pair<X509Certificate[], byte[]> verification = mXmppConnectionService.getIqParser().verification(response);
if (verification != null) {
try {
Signature verifier = Signature.getInstance("sha256WithRSA");
@ -759,13 +770,14 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
Bundle information = CryptoHelper.extractCertificateInformation(verification.first[0]);
try {
final String cn = information.getString("subject_cn");
final Jid jid = Jid.of(address.getName());
Log.d(Config.LOGTAG, "setting common name for " + jid + " to " + cn);
account.getRoster().getContact(jid).setCommonName(cn);
final Jid jid1 = Jid.of(address.getName());
Log.d(Config.LOGTAG, "setting common name for " + jid1 + " to " + cn);
account.getRoster().getContact(jid1).setCommonName(cn);
} catch (final IllegalArgumentException ignored) {
//ignored
}
finishBuildingSessionsFromPEP(address);
future.set(session);
return;
} catch (Exception e) {
Log.d(Config.LOGTAG, "could not verify certificate");
@ -779,12 +791,9 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
fetchStatusMap.put(address, FetchStatus.SUCCESS);
finishBuildingSessionsFromPEP(address);
}
future.set(session);
});
} catch (IllegalArgumentException e) {
fetchStatusMap.put(address, FetchStatus.SUCCESS);
finishBuildingSessionsFromPEP(address);
}
return future;
}
private void finishBuildingSessionsFromPEP(final SignalProtocolAddress address) {
@ -900,22 +909,23 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
}
private void buildSessionFromPEP(final SignalProtocolAddress address) {
buildSessionFromPEP(address, null);
private ListenableFuture<XmppAxolotlSession> buildSessionFromPEP(final SignalProtocolAddress address) {
return buildSessionFromPEP(address, null);
}
private void buildSessionFromPEP(final SignalProtocolAddress address, OnSessionBuildFromPep callback) {
private ListenableFuture<XmppAxolotlSession> buildSessionFromPEP(final SignalProtocolAddress address, OnSessionBuildFromPep callback) {
final SettableFuture<XmppAxolotlSession> sessionSettableFuture = SettableFuture.create();
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new session for " + address.toString());
if (address.equals(getOwnAxolotlAddress())) {
throw new AssertionError("We should NEVER build a session with ourselves. What happened here?!");
}
final Jid jid = Jid.of(address.getName());
final boolean oneOfOurs = jid.asBareJid().equals(account.getJid().asBareJid());
IqPacket bundlesPacket = mXmppConnectionService.getIqGenerator().retrieveBundlesForDevice(jid, address.getDeviceId());
mXmppConnectionService.sendIqPacket(account, bundlesPacket, (account, packet) -> {
if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
fetchStatusMap.put(address, FetchStatus.TIMEOUT);
sessionSettableFuture.setException(new CryptoFailedException("Unable to build session. Timeout"));
} else if (packet.getType() == IqPacket.TYPE.RESULT) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Received preKey IQ packet, processing...");
final IqParser parser = mXmppConnectionService.getIqParser();
@ -928,6 +938,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
if (callback != null) {
callback.onSessionBuildFailed();
}
sessionSettableFuture.setException(new CryptoFailedException("Unable to build session. IQ Packet Invalid"));
return;
}
Random random = new Random();
@ -939,6 +950,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
if (callback != null) {
callback.onSessionBuildFailed();
}
sessionSettableFuture.setException(new CryptoFailedException("Unable to build session. No suitable PreKey found"));
return;
}
@ -953,7 +965,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey());
sessions.put(address, session);
if (Config.X509_VERIFICATION) {
verifySessionWithPEP(session); //TODO; maybe inject callback in here too
sessionSettableFuture.setFuture(verifySessionWithPEP(session)); //TODO; maybe inject callback in here too
} else {
FingerprintStatus status = getFingerprintTrust(CryptoHelper.bytesToHex(bundle.getIdentityKey().getPublicKey().serialize()));
FetchStatus fetchStatus;
@ -969,6 +981,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
if (callback != null) {
callback.onSessionBuildSuccessful();
}
sessionSettableFuture.set(session);
}
} catch (UntrustedIdentityException | InvalidKeyException e) {
Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": "
@ -981,6 +994,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
if (callback != null) {
callback.onSessionBuildFailed();
}
sessionSettableFuture.setException(new CryptoFailedException(e));
}
} else {
fetchStatusMap.put(address, FetchStatus.ERROR);
@ -994,8 +1008,10 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
if (callback != null) {
callback.onSessionBuildFailed();
}
sessionSettableFuture.setException(new CryptoFailedException("Unable to build session. IQ Packet Error"));
}
});
return sessionSettableFuture;
}
private void removeFromDeviceAnnouncement(Integer id) {
@ -1217,7 +1233,7 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(account.getJid().asBareJid(), getOwnDeviceId());
final String content = child.getContent();
axolotlMessage.encrypt(content);
axolotlMessage.addDevice(session);
axolotlMessage.addDevice(session, true);
fingerprint.addChild(axolotlMessage.toElement());
transportInfo.addChild(fingerprint);
} else {
@ -1228,36 +1244,63 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
}
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));
public ListenableFuture<OmemoVerifiedPayload<OmemoVerifiedRtpContentMap>> encrypt(final RtpContentMap rtpContentMap, final Jid jid, final int deviceId) {
return Futures.transformAsync(
getSession(jid, deviceId),
session -> encrypt(rtpContentMap, session),
MoreExecutors.directExecutor()
);
}
private ListenableFuture<OmemoVerifiedPayload<OmemoVerifiedRtpContentMap>> encrypt(final RtpContentMap rtpContentMap, final XmppAxolotlSession session) {
if (Config.REQUIRE_RTP_VERIFICATION) {
requireVerification(session);
}
final ImmutableMap.Builder<String, RtpContentMap.DescriptionTransport> descriptionTransportBuilder = new ImmutableMap.Builder<>();
final OmemoVerification omemoVerification = new OmemoVerification();
omemoVerification.setDeviceId(deviceId);
omemoVerification.setDeviceId(session.getRemoteAddress().getDeviceId());
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);
final OmemoVerifiedIceUdpTransportInfo encryptedTransportInfo;
try {
encryptedTransportInfo = encrypt(descriptionTransport.transport, session);
} catch (final CryptoFailedException e) {
return Futures.immediateFailedFuture(e);
}
descriptionTransportBuilder.put(
content.getKey(),
new RtpContentMap.DescriptionTransport(descriptionTransport.description, encryptedTransportInfo)
);
}
return new OmemoVerifiedPayload<>(
return Futures.immediateFuture(
new OmemoVerifiedPayload<>(
omemoVerification,
new OmemoVerifiedRtpContentMap(rtpContentMap.group, descriptionTransportBuilder.build())
);
));
}
public OmemoVerifiedPayload<RtpContentMap> decrypt(OmemoVerifiedRtpContentMap omemoVerifiedRtpContentMap, final Jid from) throws CryptoFailedException {
private ListenableFuture<XmppAxolotlSession> getSession(final Jid jid, final int deviceId) {
final SignalProtocolAddress address = new SignalProtocolAddress(jid.asBareJid().toString(), deviceId);
final XmppAxolotlSession session = sessions.get(address);
if (session == null) {
return buildSessionFromPEP(address);
}
return Futures.immediateFuture(session);
}
public ListenableFuture<OmemoVerifiedPayload<RtpContentMap>> decrypt(OmemoVerifiedRtpContentMap omemoVerifiedRtpContentMap, final Jid from) {
final ImmutableMap.Builder<String, RtpContentMap.DescriptionTransport> descriptionTransportBuilder = new ImmutableMap.Builder<>();
final OmemoVerification omemoVerification = new OmemoVerification();
final ImmutableList.Builder<ListenableFuture<XmppAxolotlSession>> pepVerificationFutures = new ImmutableList.Builder<>();
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);
final OmemoVerifiedPayload<IceUdpTransportInfo> decryptedTransport;
try {
decryptedTransport = decrypt((OmemoVerifiedIceUdpTransportInfo) descriptionTransport.transport, from, pepVerificationFutures);
} catch (CryptoFailedException e) {
return Futures.immediateFailedFuture(e);
}
omemoVerification.setOrEnsureEqual(decryptedTransport);
descriptionTransportBuilder.put(
content.getKey(),
@ -1265,13 +1308,26 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
);
}
processPostponed();
final ImmutableList<ListenableFuture<XmppAxolotlSession>> sessionFutures = pepVerificationFutures.build();
return Futures.transform(
Futures.allAsList(sessionFutures),
sessions -> {
if (Config.REQUIRE_RTP_VERIFICATION) {
for (XmppAxolotlSession session : sessions) {
requireVerification(session);
}
}
return new OmemoVerifiedPayload<>(
omemoVerification,
new RtpContentMap(omemoVerifiedRtpContentMap.group, descriptionTransportBuilder.build())
);
},
MoreExecutors.directExecutor()
);
}
private OmemoVerifiedPayload<IceUdpTransportInfo> decrypt(final OmemoVerifiedIceUdpTransportInfo verifiedIceUdpTransportInfo, final Jid from) throws CryptoFailedException {
private OmemoVerifiedPayload<IceUdpTransportInfo> decrypt(final OmemoVerifiedIceUdpTransportInfo verifiedIceUdpTransportInfo, final Jid from, ImmutableList.Builder<ListenableFuture<XmppAxolotlSession>> pepVerificationFutures) throws CryptoFailedException {
final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo();
transportInfo.setAttributes(verifiedIceUdpTransportInfo.getAttributes());
final OmemoVerification omemoVerification = new OmemoVerification();
@ -1288,6 +1344,11 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
if (preKeyId != null) {
postponedSessions.add(session);
}
if (session.isFresh()) {
pepVerificationFutures.add(putFreshSession(session));
} else if (Config.REQUIRE_RTP_VERIFICATION) {
pepVerificationFutures.add(Futures.immediateFuture(session));
}
fingerprint.setContent(plaintext.getPlaintext());
omemoVerification.setDeviceId(session.getRemoteAddress().getDeviceId());
omemoVerification.setSessionFingerprint(plaintext.getFingerprint());
@ -1299,6 +1360,16 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return new OmemoVerifiedPayload<>(omemoVerification, transportInfo);
}
private static void requireVerification(final XmppAxolotlSession session) {
if (session.getTrust().isVerified()) {
return;
}
throw new NotVerifiedException(String.format(
"session with %s was not verified",
session.getFingerprint()
));
}
public void prepareKeyTransportMessage(final Conversation conversation, final OnMessageCreatedCallback onMessageCreatedCallback) {
executor.execute(new Runnable() {
@Override
@ -1496,15 +1567,16 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return keyTransportMessage;
}
private void putFreshSession(XmppAxolotlSession session) {
private ListenableFuture<XmppAxolotlSession> putFreshSession(XmppAxolotlSession session) {
sessions.put(session);
if (Config.X509_VERIFICATION) {
if (session.getIdentityKey() != null) {
verifySessionWithPEP(session);
return verifySessionWithPEP(session);
} else {
Log.e(Config.LOGTAG, account.getJid().asBareJid() + ": identity key was empty after reloading for x509 verification");
}
}
return Futures.immediateFuture(session);
}
public enum FetchStatus {
@ -1690,4 +1762,12 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return payload;
}
}
public static class NotVerifiedException extends SecurityException {
public NotVerifiedException(String message) {
super(message);
}
}
}

View File

@ -5,6 +5,8 @@ import android.database.Cursor;
import android.os.SystemClock;
import android.util.Log;
import com.google.common.base.Strings;
import org.json.JSONException;
import org.json.JSONObject;
@ -247,7 +249,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
}
public String getHostname() {
return this.hostname == null ? "" : this.hostname;
return Strings.nullToEmpty(this.hostname);
}
public void setHostname(String hostname) {

View File

@ -8,6 +8,7 @@ import android.util.Log;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Longs;
import org.json.JSONException;
@ -849,10 +850,10 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
fileParams.height = parseInt(parts[3]);
case 2:
fileParams.url = URL.tryParse(parts[0]);
fileParams.size = parseLong(parts[1]);
fileParams.size = Longs.tryParse(parts[1]);
break;
case 3:
fileParams.size = parseLong(parts[0]);
fileParams.size = Longs.tryParse(parts[0]);
fileParams.width = parseInt(parts[1]);
fileParams.height = parseInt(parts[2]);
break;
@ -861,14 +862,6 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
return fileParams;
}
private static long parseLong(String value) {
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
return 0;
}
}
private static int parseInt(String value) {
try {
return Integer.parseInt(value);
@ -905,10 +898,14 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
public static class FileParams {
public String url;
public long size = 0;
public Long size = null;
public int width = 0;
public int height = 0;
public int runtime = 0;
public long getSize() {
return size == null ? 0 : size;
}
}
public void setFingerprint(String fingerprint) {

View File

@ -22,7 +22,7 @@ public interface Transferable {
int getStatus();
long getFileSize();
Long getFileSize();
int getProgress();

View File

@ -18,8 +18,8 @@ public class TransferablePlaceholder implements Transferable {
}
@Override
public long getFileSize() {
return 0;
public Long getFileSize() {
return null;
}
@Override

View File

@ -83,7 +83,7 @@ public class HttpDownloadConnection implements Transferable {
final Message.FileParams fileParams = message.getFileParams();
if (message.hasFileOnRemoteHost()) {
mUrl = AesGcmURL.of(fileParams.url);
} else if (message.isOOb() && fileParams.url != null && fileParams.size > 0) {
} else if (message.isOOb() && fileParams.url != null && fileParams.size != null) {
mUrl = AesGcmURL.of(fileParams.url);
} else {
mUrl = AesGcmURL.of(message.getBody().split("\n")[0]);
@ -106,8 +106,9 @@ public class HttpDownloadConnection implements Transferable {
this.message.setEncryption(Message.ENCRYPTION_NONE);
}
//TODO add auth tag size to knownFileSize
final long knownFileSize = message.getFileParams().size;
if (knownFileSize > 0 && interactive) {
final Long knownFileSize = message.getFileParams().size;
Log.d(Config.LOGTAG,"knownFileSize: "+knownFileSize+", body="+message.getBody());
if (knownFileSize != null && interactive) {
this.file.setExpectedSize(knownFileSize);
download(true);
} else {
@ -130,6 +131,7 @@ public class HttpDownloadConnection implements Transferable {
}
private void download(final boolean interactive) {
Log.d(Config.LOGTAG,"download()",new Exception());
EXECUTOR.execute(new FileDownloader(interactive));
}
@ -234,11 +236,11 @@ public class HttpDownloadConnection implements Transferable {
}
@Override
public long getFileSize() {
public Long getFileSize() {
if (this.file != null) {
return this.file.getExpectedSize();
} else {
return 0;
return null;
}
}
@ -318,6 +320,7 @@ public class HttpDownloadConnection implements Transferable {
mostRecentCall = client.newCall(request);
try {
final Response response = mostRecentCall.execute();
throwOnInvalidCode(response);
final String contentLength = response.header("Content-Length");
final String contentType = response.header("Content-Type");
final AbstractConnectionManager.Extension extension = AbstractConnectionManager.Extension.of(mUrl.encodedPath());
@ -332,7 +335,11 @@ public class HttpDownloadConnection implements Transferable {
if (Strings.isNullOrEmpty(contentLength)) {
throw new IOException("no content-length found in HEAD response");
}
return Long.parseLong(contentLength, 10);
final long size = Long.parseLong(contentLength, 10);
if (size < 0) {
throw new IOException("Server reported negative file size");
}
return size;
} catch (IOException e) {
Log.d(Config.LOGTAG, "io exception during HEAD " + e.getMessage());
throw e;
@ -395,8 +402,7 @@ public class HttpDownloadConnection implements Transferable {
final Request request = requestBuilder.build();
mostRecentCall = client.newCall(request);
final Response response = mostRecentCall.execute();
final int code = response.code();
if (code >= 200 && code <= 299) {
throwOnInvalidCode(response);
final String contentRange = response.header("Content-Range");
final boolean serverResumed = tryResume && contentRange != null && contentRange.startsWith("bytes " + resumeSize + "-");
final InputStream inputStream = response.body().byteStream();
@ -431,9 +437,6 @@ public class HttpDownloadConnection implements Transferable {
updateProgress(Math.round(((double) transmitted / expected) * 100));
}
outputStream.flush();
} else {
throw new IOException(String.format(Locale.ENGLISH, "HTTP Status code was %d", code));
}
}
private void updateImageBounds() {
@ -451,4 +454,11 @@ public class HttpDownloadConnection implements Transferable {
}
}
private static void throwOnInvalidCode(final Response response) throws IOException {
final int code = response.code();
if (code < 200 || code >= 300) {
throw new IOException(String.format(Locale.ENGLISH, "HTTP Status code was %d", code));
}
}
}

View File

@ -69,8 +69,8 @@ public class HttpUploadConnection implements Transferable, AbstractConnectionMan
}
@Override
public long getFileSize() {
return file == null ? 0 : file.getExpectedSize();
public Long getFileSize() {
return file == null ? null : file.getExpectedSize();
}
@Override

View File

@ -11,6 +11,8 @@ import android.os.SystemClock;
import android.util.Base64;
import android.util.Log;
import com.google.common.base.Stopwatch;
import org.json.JSONException;
import org.json.JSONObject;
import org.whispersystems.libsignal.IdentityKey;
@ -62,7 +64,9 @@ import eu.siacs.conversations.xmpp.mam.MamReference;
public class DatabaseBackend extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "history";
private static final int DATABASE_VERSION = 49;
private static final int DATABASE_VERSION = 50;
private static boolean requiresMessageIndexRebuild = false;
private static DatabaseBackend instance = null;
private static final String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@ -165,16 +169,17 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ "UNIQUE(" + Resolver.Result.DOMAIN + ") ON CONFLICT REPLACE"
+ ");";
private static final String CREATE_MESSAGE_TIME_INDEX = "create INDEX message_time_index ON " + Message.TABLENAME + "(" + Message.TIME_SENT + ")";
private static final String CREATE_MESSAGE_CONVERSATION_INDEX = "create INDEX message_conversation_index ON " + Message.TABLENAME + "(" + Message.CONVERSATION + ")";
private static final String CREATE_MESSAGE_DELETED_INDEX = "create index message_deleted_index ON " + Message.TABLENAME + "(" + Message.DELETED + ")";
private static final String CREATE_MESSAGE_RELATIVE_FILE_PATH_INDEX = "create INDEX message_file_path_index ON " + Message.TABLENAME + "(" + Message.RELATIVE_FILE_PATH + ")";
private static final String CREATE_MESSAGE_TYPE_INDEX = "create INDEX message_type_index ON " + Message.TABLENAME + "(" + Message.TYPE + ")";
private static final String CREATE_MESSAGE_TIME_INDEX = "CREATE INDEX message_time_index ON " + Message.TABLENAME + "(" + Message.TIME_SENT + ")";
private static final String CREATE_MESSAGE_CONVERSATION_INDEX = "CREATE INDEX message_conversation_index ON " + Message.TABLENAME + "(" + Message.CONVERSATION + ")";
private static final String CREATE_MESSAGE_DELETED_INDEX = "CREATE INDEX message_deleted_index ON " + Message.TABLENAME + "(" + Message.DELETED + ")";
private static final String CREATE_MESSAGE_RELATIVE_FILE_PATH_INDEX = "CREATE INDEX message_file_path_index ON " + Message.TABLENAME + "(" + Message.RELATIVE_FILE_PATH + ")";
private static final String CREATE_MESSAGE_TYPE_INDEX = "CREATE INDEX message_type_index ON " + Message.TABLENAME + "(" + Message.TYPE + ")";
private static final String CREATE_MESSAGE_INDEX_TABLE = "CREATE VIRTUAL TABLE messages_index USING FTS4(uuid TEXT PRIMARY KEY, body TEXT)";
private static final String CREATE_MESSAGE_INSERT_TRIGGER = "CREATE TRIGGER after_message_insert AFTER INSERT ON " + Message.TABLENAME + " BEGIN INSERT INTO messages_index (uuid,body) VALUES (new.uuid,new.body); END;";
private static final String CREATE_MESSAGE_UPDATE_TRIGGER = "CREATE TRIGGER after_message_update UPDATE of uuid,body ON " + Message.TABLENAME + " BEGIN update messages_index set body=new.body,uuid=new.uuid WHERE uuid=old.uuid; END;";
private static final String COPY_PREEXISTING_ENTRIES = "INSERT into messages_index(uuid,body) select uuid,body FROM " + Message.TABLENAME + ";";
private static final String CREATE_MESSAGE_INDEX_TABLE = "CREATE VIRTUAL TABLE messages_index USING fts4 (uuid,body,notindexed=\"uuid\",content=\"" + Message.TABLENAME + "\",tokenize='unicode61')";
private static final String CREATE_MESSAGE_INSERT_TRIGGER = "CREATE TRIGGER after_message_insert AFTER INSERT ON " + Message.TABLENAME + " BEGIN INSERT INTO messages_index(rowid,uuid,body) VALUES(NEW.rowid,NEW.uuid,NEW.body); END;";
private static final String CREATE_MESSAGE_UPDATE_TRIGGER = "CREATE TRIGGER after_message_update UPDATE OF uuid,body ON " + Message.TABLENAME + " BEGIN UPDATE messages_index SET body=NEW.body,uuid=NEW.uuid WHERE rowid=OLD.rowid; END;";
private static final String CREATE_MESSAGE_DELETE_TRIGGER = "CREATE TRIGGER after_message_delete AFTER DELETE ON " + Message.TABLENAME + " BEGIN DELETE FROM messages_index WHERE rowid=OLD.rowid; END;";
private static final String COPY_PREEXISTING_ENTRIES = "INSERT INTO messages_index(messages_index) VALUES('rebuild');";
private DatabaseBackend(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@ -187,6 +192,17 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return values;
}
public static boolean requiresMessageIndexRebuild() {
return requiresMessageIndexRebuild;
}
public void rebuildMessagesIndex() {
final SQLiteDatabase db = getWritableDatabase();
final Stopwatch stopwatch = Stopwatch.createStarted();
db.execSQL(COPY_PREEXISTING_ENTRIES);
Log.d(Config.LOGTAG,"rebuilt message index in "+ stopwatch.stop().toString());
}
public static synchronized DatabaseBackend getInstance(Context context) {
if (instance == null) {
instance = new DatabaseBackend(context);
@ -263,6 +279,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL(CREATE_MESSAGE_INDEX_TABLE);
db.execSQL(CREATE_MESSAGE_INSERT_TRIGGER);
db.execSQL(CREATE_MESSAGE_UPDATE_TRIGGER);
db.execSQL(CREATE_MESSAGE_DELETE_TRIGGER);
}
@Override
@ -515,11 +532,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.MARKABLE + " NUMBER DEFAULT 0");
}
if (oldVersion < 41 && newVersion >= 41) {
db.execSQL(CREATE_MESSAGE_INDEX_TABLE);
db.execSQL(CREATE_MESSAGE_INSERT_TRIGGER);
db.execSQL(CREATE_MESSAGE_UPDATE_TRIGGER);
db.execSQL(COPY_PREEXISTING_ENTRIES);
if (oldVersion < 39 && newVersion >= 39) {
db.execSQL(CREATE_RESOLVER_RESULTS_TABLE);
}
if (oldVersion < 42 && newVersion >= 42) {
@ -568,6 +582,19 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + Contact.TABLENAME + "' AND COLUMN_NAME = '" + Contact.RTP_CAPABILITY + "') THEN"
+ "ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + Contact.RTP_CAPABILITY + " TEXT");
}
if (oldVersion < 50 && newVersion >= 50) {
db.beginTransaction();
db.execSQL("DROP TRIGGER IF EXISTS after_message_insert;");
db.execSQL("DROP TRIGGER IF EXISTS after_message_update;");
db.execSQL("DROP TABLE IF EXISTS messages_index;");
db.execSQL(CREATE_MESSAGE_INDEX_TABLE);
db.execSQL(CREATE_MESSAGE_INSERT_TRIGGER);
db.execSQL(CREATE_MESSAGE_UPDATE_TRIGGER);
db.execSQL(CREATE_MESSAGE_DELETE_TRIGGER);
requiresMessageIndexRebuild = true;
db.setTransactionSuccessful();
db.endTransaction();
}
}
private void canonicalizeJids(SQLiteDatabase db) {
@ -793,7 +820,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
final SQLiteDatabase db = this.getReadableDatabase();
final StringBuilder SQL = new StringBuilder();
final String[] selectionArgs;
SQL.append("SELECT " + Message.TABLENAME + ".*," + Conversation.TABLENAME + '.' + Conversation.CONTACTJID + ',' + Conversation.TABLENAME + '.' + Conversation.ACCOUNT + ',' + Conversation.TABLENAME + '.' + Conversation.MODE + " FROM " + Message.TABLENAME + " join " + Conversation.TABLENAME + " on " + Message.TABLENAME + '.' + Message.CONVERSATION + '=' + Conversation.TABLENAME + '.' + Conversation.UUID + " join messages_index ON messages_index.uuid=messages.uuid where " + Message.ENCRYPTION + " NOT IN(" + Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE + ',' + Message.ENCRYPTION_PGP + ',' + Message.ENCRYPTION_DECRYPTION_FAILED + ',' + Message.ENCRYPTION_AXOLOTL_FAILED + ") AND " + Message.TYPE + " IN(" + Message.TYPE_TEXT + ',' + Message.TYPE_PRIVATE + ") AND messages_index.body MATCH ?");
SQL.append("SELECT " + Message.TABLENAME + ".*," + Conversation.TABLENAME + "." + Conversation.CONTACTJID + "," + Conversation.TABLENAME + "." + Conversation.ACCOUNT + "," + Conversation.TABLENAME + "." + Conversation.MODE + " FROM " + Message.TABLENAME + " JOIN " + Conversation.TABLENAME + " ON " + Message.TABLENAME + "." + Message.CONVERSATION + "=" + Conversation.TABLENAME + "." + Conversation.UUID + " JOIN messages_index ON messages_index.rowid=messages.rowid WHERE " + Message.ENCRYPTION + " NOT IN(" + Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE + "," + Message.ENCRYPTION_PGP + "," + Message.ENCRYPTION_DECRYPTION_FAILED + "," + Message.ENCRYPTION_AXOLOTL_FAILED + ") AND " + Message.TYPE + " IN(" + Message.TYPE_TEXT + "," + Message.TYPE_PRIVATE + ") AND messages_index.body MATCH ?");
if (uuid == null) {
selectionArgs = new String[]{FtsUtils.toMatchString(term)};
} else {
@ -1044,8 +1071,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
long start = SystemClock.elapsedRealtime();
final SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
String[] args = {conversation.getUuid()};
db.delete("messages_index", "uuid in (select uuid from messages where conversationUuid=?)", args);
final String[] args = {conversation.getUuid()};
int num = db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
db.setTransactionSuccessful();
db.endTransaction();
@ -1056,7 +1082,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
final String[] args = {String.valueOf(timestamp)};
SQLiteDatabase db = this.getReadableDatabase();
db.beginTransaction();
db.delete("messages_index", "uuid in (select uuid from messages where timeSent<?)", args);
db.delete(Message.TABLENAME, "timeSent<?", args);
db.setTransactionSuccessful();
db.endTransaction();

View File

@ -31,6 +31,9 @@ import android.util.LruCache;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.core.content.FileProvider;
import androidx.exifinterface.media.ExifInterface;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
@ -64,7 +67,6 @@ import eu.siacs.conversations.ui.RecordingActivity;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.ExifHelper;
import eu.siacs.conversations.utils.FileUtils;
import eu.siacs.conversations.utils.FileWriterException;
import eu.siacs.conversations.utils.MimeUtils;
@ -627,37 +629,38 @@ public class FileBackend {
private void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException {
Log.d(Config.LOGTAG, "copy file (" + uri.toString() + ") to private storage " + file.getAbsolutePath());
file.getParentFile().mkdirs();
OutputStream os = null;
InputStream is = null;
try {
file.createNewFile();
os = new FileOutputStream(file);
is = mXmppConnectionService.getContentResolver().openInputStream(uri);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
} catch (IOException e) {
throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
}
try (final OutputStream os = new FileOutputStream(file);
final InputStream is = mXmppConnectionService.getContentResolver().openInputStream(uri)) {
if (is == null) {
throw new FileCopyException(R.string.error_file_not_found);
}
try {
os.write(buffer, 0, length);
ByteStreams.copy(is, os);
} catch (IOException e) {
throw new FileWriterException();
}
}
try {
os.flush();
} catch (IOException e) {
throw new FileWriterException();
}
} catch (final FileNotFoundException e) {
cleanup(file);
throw new FileCopyException(R.string.error_file_not_found);
} catch (final FileWriterException e) {
cleanup(file);
throw new FileCopyException(R.string.error_unable_to_create_temporary_file);
} catch (final SecurityException e) {
cleanup(file);
throw new FileCopyException(R.string.error_security_exception);
} catch (final IOException e) {
cleanup(file);
throw new FileCopyException(R.string.error_io_exception);
} finally {
close(os);
close(is);
}
}
@ -708,7 +711,7 @@ public class FileBackend {
private void copyImageToPrivateStorage(File file, Uri image, int sampleSize) throws FileCopyException, ImageCompressionException {
final File parent = file.getParentFile();
if (parent.mkdirs()) {
if (parent != null && parent.mkdirs()) {
Log.d(Config.LOGTAG, "created parent directory");
}
InputStream is = null;
@ -753,13 +756,15 @@ public class FileBackend {
}
scaledBitmap.recycle();
} catch (final FileNotFoundException e) {
cleanup(file);
throw new FileCopyException(R.string.error_file_not_found);
} catch (IOException e) {
e.printStackTrace();
} catch (final IOException e) {
cleanup(file);
throw new FileCopyException(R.string.error_io_exception);
} catch (SecurityException e) {
cleanup(file);
throw new FileCopyException(R.string.error_security_exception_during_image_copy);
} catch (OutOfMemoryError e) {
} catch (final OutOfMemoryError e) {
++sampleSize;
if (sampleSize <= 3) {
copyImageToPrivateStorage(file, image, sampleSize);
@ -772,6 +777,14 @@ public class FileBackend {
}
}
private static void cleanup(final File file) {
try {
file.delete();
} catch (Exception e) {
}
}
public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException, ImageCompressionException {
Log.d(Config.LOGTAG, "copy image (" + image.toString() + ") to private storage " + file.getAbsolutePath());
copyImageToPrivateStorage(file, image, 0);
@ -808,19 +821,34 @@ public class FileBackend {
}
}
private int getRotation(File file) {
return getRotation(Uri.parse("file://" + file.getAbsolutePath()));
private int getRotation(final File file) {
try (final InputStream inputStream = new FileInputStream(file)) {
return getRotation(inputStream);
} catch (Exception e) {
return 0;
}
}
private int getRotation(Uri image) {
InputStream is = null;
try {
is = mXmppConnectionService.getContentResolver().openInputStream(image);
return ExifHelper.getOrientation(is);
} catch (FileNotFoundException e) {
private int getRotation(final Uri image) {
try (final InputStream is = mXmppConnectionService.getContentResolver().openInputStream(image)) {
return is == null ? 0 : getRotation(is);
} catch (final Exception e) {
return 0;
}
}
private static int getRotation(final InputStream inputStream) throws IOException {
final ExifInterface exif = new ExifInterface(inputStream);
final int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_180:
return 180;
case ExifInterface.ORIENTATION_ROTATE_90:
return 90;
case ExifInterface.ORIENTATION_ROTATE_270:
return 270;
default:
return 0;
} finally {
close(is);
}
}
@ -1468,7 +1496,8 @@ public class FileBackend {
this.resId = resId;
}
public @StringRes int getResId() {
public @StringRes
int getResId() {
return resId;
}
}

View File

@ -5,6 +5,8 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
import androidx.core.content.ContextCompat;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.io.CipherInputStream;
import org.bouncycastle.crypto.io.CipherOutputStream;
@ -118,7 +120,8 @@ public class AbstractConnectionManager {
}
public long getAutoAcceptFileSize() {
return this.mXmppConnectionService.getLongPreference("auto_accept_file_size", R.integer.auto_accept_filesize);
final long autoAcceptFileSize = this.mXmppConnectionService.getLongPreference("auto_accept_file_size", R.integer.auto_accept_filesize);
return autoAcceptFileSize <= 0 ? -1 : autoAcceptFileSize;
}
public boolean hasStoragePermission() {
@ -134,12 +137,8 @@ public class AbstractConnectionManager {
}
}
public PowerManager.WakeLock createWakeLock(final Thread thread) {
return createWakeLock("conversations:" + thread.getName());
}
public PowerManager.WakeLock createWakeLock(final String name) {
final PowerManager powerManager = (PowerManager) mXmppConnectionService.getSystemService(Context.POWER_SERVICE);
final PowerManager powerManager = ContextCompat.getSystemService(mXmppConnectionService, PowerManager.class);
return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
}

View File

@ -397,7 +397,7 @@ public class NotificationService {
notify(DELIVERY_FAILED_NOTIFICATION_ID, summaryNotification);
}
public void startRinging(final AbstractJingleConnection.Id id, final Set<Media> media) {
public synchronized void startRinging(final AbstractJingleConnection.Id id, final Set<Media> media) {
showIncomingCallNotification(id, media);
final NotificationManager notificationManager = (NotificationManager) mXmppConnectionService.getSystemService(Context.NOTIFICATION_SERVICE);
final int currentInterruptionFilter;

View File

@ -75,6 +75,8 @@ import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@ -184,8 +186,9 @@ public class XmppConnectionService extends Service {
private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp";
public final CountDownLatch restoredFromDatabaseLatch = new CountDownLatch(1);
private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor("FileAdding");
private final SerialSingleThreadExecutor mVideoCompressionExecutor = new SerialSingleThreadExecutor("VideoCompression");
private final static Executor FILE_OBSERVER_EXECUTOR = Executors.newSingleThreadExecutor();
private final static Executor FILE_ATTACHMENT_EXECUTOR = Executors.newSingleThreadExecutor();
private final static SerialSingleThreadExecutor VIDEO_COMPRESSION_EXECUTOR = new SerialSingleThreadExecutor("VideoCompression");
private final SerialSingleThreadExecutor mDatabaseWriterExecutor = new SerialSingleThreadExecutor("DatabaseWriter");
private final SerialSingleThreadExecutor mDatabaseReaderExecutor = new SerialSingleThreadExecutor("DatabaseReader");
private final SerialSingleThreadExecutor mNotificationExecutor = new SerialSingleThreadExecutor("NotificationExecutor");
@ -471,7 +474,6 @@ public class XmppConnectionService extends Service {
private OpenPgpServiceConnection pgpServiceConnection;
private PgpEngine mPgpEngine = null;
private WakeLock wakeLock;
private PowerManager pm;
private LruCache<String, Bitmap> mBitmapCache;
private final BroadcastReceiver mInternalEventReceiver = new InternalEventReceiver();
private final BroadcastReceiver mInternalScreenEventReceiver = new InternalEventReceiver();
@ -564,9 +566,9 @@ public class XmppConnectionService extends Service {
Log.d(Config.LOGTAG, "counterpart=" + message.getCounterpart());
final AttachFileToConversationRunnable runnable = new AttachFileToConversationRunnable(this, uri, type, message, callback);
if (runnable.isVideoMessage()) {
mVideoCompressionExecutor.execute(runnable);
VIDEO_COMPRESSION_EXECUTOR.execute(runnable);
} else {
mFileAddingExecutor.execute(runnable);
FILE_ATTACHMENT_EXECUTOR.execute(runnable);
}
}
@ -593,7 +595,7 @@ public class XmppConnectionService extends Service {
message.setType(Message.TYPE_IMAGE);
}
Log.d(Config.LOGTAG, "attachImage: type=" + message.getType());
mFileAddingExecutor.execute(() -> {
FILE_ATTACHMENT_EXECUTOR.execute(() -> {
try {
getFileBackend().copyImageToPrivateStorage(message, uri);
} catch (FileBackend.ImageCompressionException e) {
@ -1147,11 +1149,11 @@ public class XmppConnectionService extends Service {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
startContactObserver();
}
mFileAddingExecutor.execute(fileBackend::deleteHistoricAvatarPath);
FILE_OBSERVER_EXECUTOR.execute(fileBackend::deleteHistoricAvatarPath);
if (Compatibility.hasStoragePermission(this)) {
Log.d(Config.LOGTAG, "starting file observer");
mFileAddingExecutor.execute(this.fileObserver::startWatching);
mFileAddingExecutor.execute(this::checkForDeletedFiles);
FILE_OBSERVER_EXECUTOR.execute(this.fileObserver::startWatching);
FILE_OBSERVER_EXECUTOR.execute(this::checkForDeletedFiles);
}
if (Config.supportOpenPgp()) {
this.pgpServiceConnection = new OpenPgpServiceConnection(this, "org.sufficientlysecure.keychain", new OpenPgpServiceConnection.OnBound() {
@ -1172,7 +1174,7 @@ public class XmppConnectionService extends Service {
this.pgpServiceConnection.bindToService();
}
this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
final PowerManager pm = ContextCompat.getSystemService(this, PowerManager.class);
this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Conversations:Service");
toggleForegroundService();
@ -1267,8 +1269,8 @@ public class XmppConnectionService extends Service {
public void restartFileObserver() {
Log.d(Config.LOGTAG, "restarting file observer");
mFileAddingExecutor.execute(this.fileObserver::restartWatching);
mFileAddingExecutor.execute(this::checkForDeletedFiles);
FILE_OBSERVER_EXECUTOR.execute(this.fileObserver::restartWatching);
FILE_OBSERVER_EXECUTOR.execute(this::checkForDeletedFiles);
}
public void toggleScreenEventReceiver() {
@ -1885,7 +1887,10 @@ public class XmppConnectionService extends Service {
long diffConversationsRestore = SystemClock.elapsedRealtime() - startTimeConversationsRestore;
Log.d(Config.LOGTAG, "finished restoring conversations in " + diffConversationsRestore + "ms");
Runnable runnable = () -> {
long deletionDate = getAutomaticMessageDeletionDate();
if (DatabaseBackend.requiresMessageIndexRebuild()) {
DatabaseBackend.getInstance(this).rebuildMessagesIndex();
}
final long deletionDate = getAutomaticMessageDeletionDate();
mLastExpiryRun.set(SystemClock.elapsedRealtime());
if (deletionDate > 0) {
Log.d(Config.LOGTAG, "deleting messages that are older than " + AbstractGenerator.getTimestamp(deletionDate));
@ -1925,7 +1930,7 @@ public class XmppConnectionService extends Service {
private void restoreMessages(Conversation conversation) {
conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
conversation.findUnsentTextMessages(message -> markMessage(message, Message.STATUS_WAITING));
conversation.findUnreadMessages(message -> mNotificationService.pushFromBacklog(message));
conversation.findUnreadMessages(mNotificationService::pushFromBacklog);
}
public void loadPhoneContacts() {
@ -3337,35 +3342,26 @@ public class XmppConnectionService extends Service {
public void changeAffiliationInConference(final Conversation conference, Jid user, final MucOptions.Affiliation affiliation, final OnAffiliationChanged callback) {
final Jid jid = user.asBareJid();
IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString());
sendIqPacket(conference.getAccount(), request, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
final IqPacket request = this.mIqGenerator.changeAffiliation(conference, jid, affiliation.toString());
sendIqPacket(conference.getAccount(), request, (account, response) -> {
if (response.getType() == IqPacket.TYPE.RESULT) {
conference.getMucOptions().changeAffiliation(jid, affiliation);
getAvatarService().clear(conference);
if (callback != null) {
callback.onAffiliationChangedSuccessful(jid);
} else {
callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation);
Log.d(Config.LOGTAG, "changed affiliation of " + user + " to " + affiliation);
}
} else if (callback != null) {
callback.onAffiliationChangeFailed(jid, R.string.could_not_change_affiliation);
} else {
Log.d(Config.LOGTAG, "unable to change affiliation");
}
});
}
public void changeAffiliationsInConference(final Conversation conference, MucOptions.Affiliation before, MucOptions.Affiliation after) {
List<Jid> jids = new ArrayList<>();
for (MucOptions.User user : conference.getMucOptions().getUsers()) {
if (user.getAffiliation() == before && user.getRealJid() != null) {
jids.add(user.getRealJid());
}
}
IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString());
sendIqPacket(conference.getAccount(), request, mDefaultIqHandler);
}
public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role) {
IqPacket request = this.mIqGenerator.changeRole(conference, nick, role.toString());
Log.d(Config.LOGTAG, request.toString());
sendIqPacket(conference.getAccount(), request, (account, packet) -> {
if (packet.getType() != IqPacket.TYPE.RESULT) {
Log.d(Config.LOGTAG, account.getJid().asBareJid() + " unable to change role of " + nick);
@ -3928,9 +3924,13 @@ public class XmppConnectionService extends Service {
new Thread(() -> reconnectAccount(account, false, true)).start();
}
public void invite(Conversation conversation, Jid contact) {
public void invite(final Conversation conversation, final Jid contact) {
Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": inviting " + contact + " to " + conversation.getJid().asBareJid());
MessagePacket packet = mMessageGenerator.invite(conversation, contact);
final MucOptions.User user = conversation.getMucOptions().findUserByRealJid(contact.asBareJid());
if (user == null || user.getAffiliation() == MucOptions.Affiliation.OUTCAST) {
changeAffiliationInConference(conversation, contact, MucOptions.Affiliation.NONE, null);
}
final MessagePacket packet = mMessageGenerator.invite(conversation, contact);
sendMessagePacket(conversation.getAccount(), packet);
}

View File

@ -5,12 +5,19 @@ import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
import static eu.siacs.conversations.ui.XmppActivity.configureActionBar;
public class AboutActivity extends AppCompatActivity {
@Override
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@ -1,5 +1,6 @@
package eu.siacs.conversations.ui;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@ -7,6 +8,7 @@ import android.os.Bundle;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@ -86,6 +88,13 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
};
public static void open(final Activity activity, final Conversation conversation) {
Intent intent = new Intent(activity, ConferenceDetailsActivity.class);
intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
intent.putExtra("uuid", conversation.getUuid());
activity.startActivity(intent);
}
private final OnClickListener mNotifyStatusClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
@ -481,6 +490,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
this.binding.mucSubject.setTextAppearance(this, subject.length() > (hasTitle ? 128 : 196) ? R.style.TextAppearance_Conversations_Body1_Linkified : R.style.TextAppearance_Conversations_Subhead);
this.binding.mucSubject.setAutoLinkMask(0);
this.binding.mucSubject.setVisibility(View.VISIBLE);
this.binding.mucSubject.setMovementMethod(LinkMovementMethod.getInstance());
} else {
this.binding.mucSubject.setVisibility(View.GONE);
}

View File

@ -6,6 +6,8 @@ import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import eu.siacs.conversations.ui.util.SettingsUtils;
public class ConversationActivity extends AppCompatActivity {
@Override
@ -14,4 +16,10 @@ public class ConversationActivity extends AppCompatActivity {
startActivity(new Intent(this, ConversationsActivity.class));
finish();
}
@Override
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
}

View File

@ -186,10 +186,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), ConferenceDetailsActivity.class);
intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
intent.putExtra("uuid", conversation.getUuid());
startActivity(intent);
ConferenceDetailsActivity.open(getActivity(), conversation);
}
};
private final OnClickListener leaveMuc = new OnClickListener() {
@ -734,7 +731,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (body.length() == 0 || conversation == null) {
return;
}
if (conversation.getNextEncryption() == Message.ENCRYPTION_AXOLOTL && trustKeysIfNeeded(REQUEST_TRUST_KEYS_TEXT)) {
if (trustKeysIfNeeded(conversation, REQUEST_TRUST_KEYS_TEXT)) {
return;
}
final Message message;
@ -757,6 +754,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
private boolean trustKeysIfNeeded(final Conversation conversation, final int requestCode) {
return conversation.getNextEncryption() == Message.ENCRYPTION_AXOLOTL && trustKeysIfNeeded(requestCode);
}
protected boolean trustKeysIfNeeded(int requestCode) {
AxolotlService axolotlService = conversation.getAccount().getAxolotlService();
final List<Jid> targets = axolotlService.getCryptoTargets(conversation);
@ -824,6 +825,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
case REQUEST_TRUST_KEYS_ATTACHMENTS:
commitAttachments();
break;
case REQUEST_START_AUDIO_CALL:
triggerRtpSession(RtpSessionActivity.ACTION_MAKE_VOICE_CALL);
break;
case REQUEST_START_VIDEO_CALL:
triggerRtpSession(RtpSessionActivity.ACTION_MAKE_VIDEO_CALL);
break;
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
final List<Attachment> imageUris = Attachment.extractAttachments(getActivity(), data, Attachment.Type.IMAGE);
mediaPreviewAdapter.addMediaPreviews(imageUris);
@ -870,7 +877,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (anyNeedsExternalStoragePermission(attachments) && !hasPermissions(REQUEST_COMMIT_ATTACHMENTS, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
return;
}
if (conversation.getNextEncryption() == Message.ENCRYPTION_AXOLOTL && trustKeysIfNeeded(REQUEST_TRUST_KEYS_ATTACHMENTS)) {
if (trustKeysIfNeeded(conversation, REQUEST_TRUST_KEYS_ATTACHMENTS)) {
return;
}
final PresenceSelector.OnPresenceSelected callback = () -> {
@ -1262,10 +1269,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
activity.switchToContactDetails(conversation.getContact());
break;
case R.id.action_muc_details:
Intent intent = new Intent(getActivity(), ConferenceDetailsActivity.class);
intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
intent.putExtra("uuid", conversation.getUuid());
startActivity(intent);
ConferenceDetailsActivity.open(getActivity(), conversation);
break;
case R.id.action_invite:
startActivityForResult(ChooseContactActivity.create(activity, conversation), REQUEST_INVITE_TO_CONVERSATION);
@ -1365,7 +1369,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
Toast.makeText(getActivity(), R.string.only_one_call_at_a_time, Toast.LENGTH_LONG).show();
return;
}
final Contact contact = conversation.getContact();
if (contact.getPresences().anySupport(Namespace.JINGLE_MESSAGE)) {
triggerRtpSession(contact.getAccount(), contact.getJid().asBareJid(), action);
@ -1854,7 +1857,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (!message.hasFileOnRemoteHost()
&& xmppConnection != null
&& conversation.getMode() == Conversational.MODE_SINGLE
&& !xmppConnection.getFeatures().httpUpload(message.getFileParams().size)) {
&& !xmppConnection.getFeatures().httpUpload(message.getFileParams().getSize())) {
activity.selectPresence(conversation, () -> {
message.setCounterpart(conversation.getNextCounterpart());
activity.xmppConnectionService.resendFailedMessages(message);

View File

@ -30,6 +30,8 @@
package eu.siacs.conversations.ui;
import static eu.siacs.conversations.ui.ConversationFragment.REQUEST_DECRYPT_PGP;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Fragment;
@ -65,13 +67,16 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.OmemoSetting;
import eu.siacs.conversations.databinding.ActivityConversationsBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.interfaces.OnBackendConnected;
import eu.siacs.conversations.ui.interfaces.OnConversationArchived;
import eu.siacs.conversations.ui.interfaces.OnConversationRead;
import eu.siacs.conversations.ui.interfaces.OnConversationSelected;
import eu.siacs.conversations.ui.interfaces.OnConversationsListItemUpdated;
import eu.siacs.conversations.ui.util.ActionBarUtil;
import eu.siacs.conversations.ui.util.ActivityResult;
import eu.siacs.conversations.ui.util.ConversationMenuConfigurator;
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
@ -83,8 +88,6 @@ import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import static eu.siacs.conversations.ui.ConversationFragment.REQUEST_DECRYPT_PGP;
public class ConversationsActivity extends XmppActivity implements OnConversationSelected, OnConversationArchived, OnConversationsListItemUpdated, OnConversationRead, XmppConnectionService.OnAccountUpdate, XmppConnectionService.OnConversationUpdate, XmppConnectionService.OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnAffiliationChanged {
public static final String ACTION_VIEW_CONVERSATION = "eu.siacs.conversations.action.VIEW";
@ -425,16 +428,18 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
private void openConversation(Conversation conversation, Bundle extras) {
ConversationFragment conversationFragment = (ConversationFragment) getFragmentManager().findFragmentById(R.id.secondary_fragment);
final FragmentManager fragmentManager = getFragmentManager();
fragmentManager.executePendingTransactions();
ConversationFragment conversationFragment = (ConversationFragment) fragmentManager.findFragmentById(R.id.secondary_fragment);
final boolean mainNeedsRefresh;
if (conversationFragment == null) {
mainNeedsRefresh = false;
Fragment mainFragment = getFragmentManager().findFragmentById(R.id.main_fragment);
final Fragment mainFragment = fragmentManager.findFragmentById(R.id.main_fragment);
if (mainFragment instanceof ConversationFragment) {
conversationFragment = (ConversationFragment) mainFragment;
} else {
conversationFragment = new ConversationFragment();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_fragment, conversationFragment);
fragmentTransaction.addToBackStack(null);
try {
@ -562,17 +567,18 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
private void initializeFragments() {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fragment mainFragment = getFragmentManager().findFragmentById(R.id.main_fragment);
Fragment secondaryFragment = getFragmentManager().findFragmentById(R.id.secondary_fragment);
final FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
final Fragment mainFragment = fragmentManager.findFragmentById(R.id.main_fragment);
final Fragment secondaryFragment = fragmentManager.findFragmentById(R.id.secondary_fragment);
if (mainFragment != null) {
if (binding.secondaryFragment != null) {
if (mainFragment instanceof ConversationFragment) {
getFragmentManager().popBackStack();
transaction.remove(mainFragment);
transaction.commit();
getFragmentManager().executePendingTransactions();
transaction = getFragmentManager().beginTransaction();
fragmentManager.executePendingTransactions();
transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.secondary_fragment, mainFragment);
transaction.replace(R.id.main_fragment, new ConversationsOverviewFragment());
transaction.commit();
@ -583,7 +589,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
transaction.remove(secondaryFragment);
transaction.commit();
getFragmentManager().executePendingTransactions();
transaction = getFragmentManager().beginTransaction();
transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.main_fragment, secondaryFragment);
transaction.addToBackStack(null);
transaction.commit();
@ -601,18 +607,38 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
private void invalidateActionBarTitle() {
final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
Fragment mainFragment = getFragmentManager().findFragmentById(R.id.main_fragment);
if (actionBar == null) {
return;
}
final FragmentManager fragmentManager = getFragmentManager();
final Fragment mainFragment = fragmentManager.findFragmentById(R.id.main_fragment);
if (mainFragment instanceof ConversationFragment) {
final Conversation conversation = ((ConversationFragment) mainFragment).getConversation();
if (conversation != null) {
actionBar.setTitle(EmojiWrapper.transform(conversation.getName()));
actionBar.setDisplayHomeAsUpEnabled(true);
ActionBarUtil.setActionBarOnClickListener(
binding.toolbar,
(v) -> openConversationDetails(conversation)
);
return;
}
}
actionBar.setTitle(R.string.app_name);
actionBar.setDisplayHomeAsUpEnabled(false);
ActionBarUtil.resetActionBarOnClickListeners(binding.toolbar);
}
private void openConversationDetails(final Conversation conversation) {
if (conversation.getMode() == Conversational.MODE_MULTI) {
ConferenceDetailsActivity.open(this, conversation);
} else {
final Contact contact = conversation.getContact();
if (contact.isSelf()) {
switchToAccount(conversation.getAccount());
} else {
switchToContactDetails(contact);
}
}
}
@ -621,17 +647,18 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
if (performRedirectIfNecessary(conversation, false)) {
return;
}
Fragment mainFragment = getFragmentManager().findFragmentById(R.id.main_fragment);
final FragmentManager fragmentManager = getFragmentManager();
final Fragment mainFragment = fragmentManager.findFragmentById(R.id.main_fragment);
if (mainFragment instanceof ConversationFragment) {
try {
getFragmentManager().popBackStack();
} catch (IllegalStateException e) {
fragmentManager.popBackStack();
} catch (final IllegalStateException e) {
Log.w(Config.LOGTAG, "state loss while popping back state after archiving conversation", e);
//this usually means activity is no longer active; meaning on the next open we will run through this again
}
return;
}
Fragment secondaryFragment = getFragmentManager().findFragmentById(R.id.secondary_fragment);
final Fragment secondaryFragment = fragmentManager.findFragmentById(R.id.secondary_fragment);
if (secondaryFragment instanceof ConversationFragment) {
if (((ConversationFragment) secondaryFragment).getConversation() == conversation) {
Conversation suggestion = ConversationsOverviewFragment.getSuggestion(this, conversation);

View File

@ -39,6 +39,7 @@ import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.ui.util.LocationHelper;
import eu.siacs.conversations.ui.widget.Marker;
import eu.siacs.conversations.ui.widget.MyLocation;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
public abstract class LocationActivity extends ActionBarActivity implements LocationListener {
@ -68,6 +69,7 @@ public abstract class LocationActivity extends ActionBarActivity implements Loca
}
}
protected void updateLocationMarkers() {
clearMarkers();
}
@ -222,6 +224,7 @@ public abstract class LocationActivity extends ActionBarActivity implements Loca
@Override
protected void onResume() {
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
Configuration.getInstance().load(this, getPreferences());
map.onResume();
this.setMyLoc(null);

View File

@ -39,6 +39,7 @@ import java.util.logging.Logger;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.MTMDecision;
import eu.siacs.conversations.services.MemorizingTrustManager;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
public class MemorizingActivity extends AppCompatActivity implements OnClickListener, OnCancelListener {
@ -61,6 +62,8 @@ public class MemorizingActivity extends AppCompatActivity implements OnClickList
@Override
public void onResume() {
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
Intent i = getIntent();
decisionId = i.getIntExtra(MemorizingTrustManager.DECISION_INTENT_ID, MTMDecision.DECISION_INVALID);
int titleId = i.getIntExtra(MemorizingTrustManager.DECISION_TITLE_ID, R.string.mtm_accept_cert);

View File

@ -28,6 +28,7 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityRecordingBinding;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
import eu.siacs.conversations.utils.TimeFrameUtils;
@ -66,6 +67,12 @@ public class RecordingActivity extends Activity implements View.OnClickListener
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
@Override
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
@Override
protected void onStart() {
super.onStart();

View File

@ -1,5 +1,8 @@
package eu.siacs.conversations.ui;
import static java.util.Arrays.asList;
import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.PictureInPictureParams;
@ -35,6 +38,7 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoTrack;
@ -54,6 +58,7 @@ import eu.siacs.conversations.services.AppRTCAudioManager;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.MainThreadExecutor;
import eu.siacs.conversations.ui.util.Rationals;
import eu.siacs.conversations.utils.PermissionUtils;
import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.xml.Namespace;
@ -64,10 +69,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
import static java.util.Arrays.asList;
public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate {
public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate, eu.siacs.conversations.ui.widget.SurfaceViewRenderer.OnAspectRatioChanged {
public static final String EXTRA_WITH = "with";
public static final String EXTRA_SESSION_ID = "session_id";
@ -81,6 +83,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private static final List<RtpEndUserState> END_CARD = Arrays.asList(
RtpEndUserState.APPLICATION_ERROR,
RtpEndUserState.SECURITY_ERROR,
RtpEndUserState.DECLINED_OR_BUSY,
RtpEndUserState.CONNECTIVITY_ERROR,
RtpEndUserState.CONNECTIVITY_LOST_ERROR,
@ -88,7 +91,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
);
private static final List<RtpEndUserState> STATES_SHOWING_HELP_BUTTON = Arrays.asList(
RtpEndUserState.APPLICATION_ERROR,
RtpEndUserState.CONNECTIVITY_ERROR
RtpEndUserState.CONNECTIVITY_ERROR,
RtpEndUserState.SECURITY_ERROR
);
private static final List<RtpEndUserState> STATES_SHOWING_SWITCH_TO_CHAT = Arrays.asList(
RtpEndUserState.CONNECTING,
@ -443,12 +447,14 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
public void onStart() {
super.onStart();
mHandler.postDelayed(mTickExecutor, CALL_DURATION_UPDATE_INTERVAL);
this.binding.remoteVideo.setOnAspectRatioChanged(this);
}
@Override
public void onStop() {
mHandler.removeCallbacks(mTickExecutor);
binding.remoteVideo.release();
binding.remoteVideo.setOnAspectRatioChanged(null);
binding.localVideo.release();
final WeakReference<JingleRtpConnection> weakReference = this.rtpConnectionReference;
final JingleRtpConnection jingleRtpConnection = weakReference == null ? null : weakReference.get();
@ -512,9 +518,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@RequiresApi(api = Build.VERSION_CODES.O)
private void startPictureInPicture() {
try {
final Rational rational = this.binding.remoteVideo.getAspectRatio();
final Rational clippedRational = Rationals.clip(rational);
Log.d(Config.LOGTAG, "suggested rational " + rational + ". clipped to " + clippedRational);
enterPictureInPictureMode(
new PictureInPictureParams.Builder()
.setAspectRatio(new Rational(10, 16))
.setAspectRatio(clippedRational)
.build()
);
} catch (final IllegalStateException e) {
@ -523,6 +532,17 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
}
@Override
public void onAspectRatioChanged(final Rational rational) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isPictureInPicture()) {
final Rational clippedRational = Rationals.clip(rational);
Log.d(Config.LOGTAG, "suggested rational after aspect ratio change " + rational + ". clipped to " + clippedRational);
setPictureInPictureParams(new PictureInPictureParams.Builder()
.setAspectRatio(clippedRational)
.build());
}
}
private boolean deviceSupportsPictureInPicture() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
@ -668,6 +688,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
case APPLICATION_ERROR:
setTitle(R.string.rtp_state_application_failure);
break;
case SECURITY_ERROR:
setTitle(R.string.rtp_state_security_error);
break;
case ENDED:
throw new IllegalStateException("Activity should have called finishAndReleaseWakeLock();");
default:
@ -743,7 +766,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
RtpEndUserState.CONNECTIVITY_ERROR,
RtpEndUserState.CONNECTIVITY_LOST_ERROR,
RtpEndUserState.APPLICATION_ERROR,
RtpEndUserState.RETRACTED
RtpEndUserState.RETRACTED,
RtpEndUserState.SECURITY_ERROR
).contains(state)) {
this.binding.rejectCall.setContentDescription(getString(R.string.exit));
this.binding.rejectCall.setOnClickListener(this::exit);
@ -922,13 +946,17 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
if (END_CARD.contains(state) || state == RtpEndUserState.ENDING_CALL) {
binding.localVideo.setVisibility(View.GONE);
binding.localVideo.release();
binding.remoteVideo.setVisibility(View.GONE);
binding.remoteVideoWrapper.setVisibility(View.GONE);
binding.remoteVideo.release();
binding.pipLocalMicOffIndicator.setVisibility(View.GONE);
if (isPictureInPicture()) {
binding.appBarLayout.setVisibility(View.GONE);
binding.pipPlaceholder.setVisibility(View.VISIBLE);
if (state == RtpEndUserState.APPLICATION_ERROR || state == RtpEndUserState.CONNECTIVITY_ERROR) {
if (Arrays.asList(
RtpEndUserState.APPLICATION_ERROR,
RtpEndUserState.CONNECTIVITY_ERROR,
RtpEndUserState.SECURITY_ERROR)
.contains(state)) {
binding.pipWarning.setVisibility(View.VISIBLE);
binding.pipWaiting.setVisibility(View.GONE);
} else {
@ -944,7 +972,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
if (isPictureInPicture() && (state == RtpEndUserState.CONNECTING || state == RtpEndUserState.ACCEPTING_CALL)) {
binding.localVideo.setVisibility(View.GONE);
binding.remoteVideo.setVisibility(View.GONE);
binding.remoteVideoWrapper.setVisibility(View.GONE);
binding.appBarLayout.setVisibility(View.GONE);
binding.pipPlaceholder.setVisibility(View.VISIBLE);
binding.pipWarning.setVisibility(View.GONE);
@ -966,12 +994,17 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
if (remoteVideoTrack.isPresent()) {
ensureSurfaceViewRendererIsSetup(binding.remoteVideo);
addSink(remoteVideoTrack.get(), binding.remoteVideo);
binding.remoteVideo.setScalingType(
RendererCommon.ScalingType.SCALE_ASPECT_FILL,
RendererCommon.ScalingType.SCALE_ASPECT_FIT
);
if (state == RtpEndUserState.CONNECTED) {
binding.appBarLayout.setVisibility(View.GONE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
binding.remoteVideoWrapper.setVisibility(View.VISIBLE);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
binding.remoteVideo.setVisibility(View.GONE);
binding.remoteVideoWrapper.setVisibility(View.GONE);
}
if (isPictureInPicture() && !requireRtpConnection().isMicrophoneEnabled()) {
binding.pipLocalMicOffIndicator.setVisibility(View.VISIBLE);
@ -980,7 +1013,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
binding.remoteVideo.setVisibility(View.GONE);
binding.remoteVideoWrapper.setVisibility(View.GONE);
binding.pipLocalMicOffIndicator.setVisibility(View.GONE);
}
}

View File

@ -60,6 +60,7 @@ import java.util.Map;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.service.CameraManager;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.ui.widget.ScannerView;
/**
@ -181,6 +182,7 @@ public final class ScanActivity extends Activity implements SurfaceTextureListen
@Override
protected void onResume() {
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
maybeOpenCamera();
}

View File

@ -40,6 +40,7 @@ import eu.siacs.conversations.services.MemorizingTrustManager;
import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.xmpp.Jid;
@ -57,8 +58,10 @@ public class SettingsActivity extends XmppActivity implements
public static final String THEME = "theme";
public static final String SHOW_DYNAMIC_TAGS = "show_dynamic_tags";
public static final String OMEMO_SETTING = "omemo";
public static final String PREVENT_SCREENSHOTS = "prevent_screenshots";
public static final int REQUEST_CREATE_BACKUP = 0xbf8701;
private SettingsFragment mSettingsFragment;
@Override
@ -393,8 +396,15 @@ public class SettingsActivity extends XmppActivity implements
if (this.mTheme != theme) {
recreate();
}
} else if(name.equals(PREVENT_SCREENSHOTS)){
SettingsUtils.applyScreenshotPreventionSetting(this);
}
}
@Override
public void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
@Override

View File

@ -80,6 +80,7 @@ import eu.siacs.conversations.ui.util.PresenceSelector;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.ui.util.SettingsUtils;
import eu.siacs.conversations.utils.ThemeHelper;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
@ -819,8 +820,9 @@ public abstract class XmppActivity extends ActionBarActivity {
}
@Override
public void onResume() {
protected void onResume(){
super.onResume();
SettingsUtils.applyScreenshotPreventionSetting(this);
}
protected int findTheme() {

View File

@ -92,6 +92,8 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
attr = R.attr.media_preview_document;
} else if (mime.equals("application/gpx+xml")) {
attr = R.attr.media_preview_tour;
} else if (mime.startsWith("image/")) {
attr = R.attr.media_preview_image;
} else {
attr = R.attr.media_preview_unknown;
}

View File

@ -75,8 +75,10 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
view.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
context.startActivity(view);
} catch (ActivityNotFoundException e) {
} catch (final ActivityNotFoundException e) {
Toast.makeText(context, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
} catch (final SecurityException e) {
Toast.makeText(context, R.string.sharing_application_not_grant_permission, Toast.LENGTH_SHORT).show();
}
}

View File

@ -59,6 +59,7 @@ import eu.siacs.conversations.ui.text.DividerSpan;
import eu.siacs.conversations.ui.text.QuoteSpan;
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.MyLinkify;
import eu.siacs.conversations.ui.util.QuoteHelper;
import eu.siacs.conversations.ui.util.ViewUtil;
import eu.siacs.conversations.ui.widget.ClickableMovementMethod;
import eu.siacs.conversations.utils.CryptoHelper;
@ -182,7 +183,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
&& message.getMergedStatus() <= Message.STATUS_RECEIVED;
if (message.isFileOrImage() || transferable != null || MessageUtils.unInitiatedButKnownSize(message)) {
FileParams params = message.getFileParams();
filesize = params.size > 0 ? UIHelper.filesizeToString(params.size) : null;
filesize = params.size != null ? UIHelper.filesizeToString(params.size) : null;
if (transferable != null && (transferable.getStatus() == Transferable.STATUS_FAILED || transferable.getStatus() == Transferable.STATUS_CANCELLED)) {
error = true;
}
@ -357,6 +358,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
*/
private boolean handleTextQuotes(SpannableStringBuilder body, boolean darkBackground) {
boolean startsWithQuote = false;
int quoteDepth = 1;
while (QuoteHelper.bodyContainsQuoteStart(body) && quoteDepth <= Config.QUOTE_MAX_DEPTH) {
char previous = '\n';
int lineStart = -1;
int lineTextStart = -1;
@ -365,8 +368,9 @@ public class MessageAdapter extends ArrayAdapter<Message> {
char current = body.length() > i ? body.charAt(i) : '\n';
if (lineStart == -1) {
if (previous == '\n') {
if ((current == '>' && UIHelper.isPositionFollowedByQuoteableCharacter(body, i))
|| current == '\u00bb' && !UIHelper.isPositionFollowedByQuote(body, i)) {
if (
QuoteHelper.isPositionQuoteStart(body, i)
) {
// Line start with quote
lineStart = i;
if (quoteStart == -1) quoteStart = i;
@ -400,6 +404,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
// Apply spans to finishing open quote
applyQuoteSpan(body, quoteStart, body.length(), darkBackground);
}
quoteDepth++;
}
return startsWithQuote;
}

View File

@ -0,0 +1,88 @@
package eu.siacs.conversations.ui.util;
import android.content.Context;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.lang.reflect.Field;
public class ActionBarUtil {
public static void resetActionBarOnClickListeners(@NonNull View view) {
final View title = findActionBarTitle(view);
final View subtitle = findActionBarSubTitle(view);
if (title != null) {
title.setOnClickListener(null);
}
if (subtitle != null) {
subtitle.setOnClickListener(null);
}
}
public static void setActionBarOnClickListener(@NonNull View view,
@NonNull final View.OnClickListener onClickListener) {
final View title = findActionBarTitle(view);
final View subtitle = findActionBarSubTitle(view);
if (title != null) {
title.setOnClickListener(onClickListener);
}
if (subtitle != null) {
subtitle.setOnClickListener(onClickListener);
}
}
private static @Nullable View findActionBarTitle(@NonNull View root) {
return findActionBarItem(root, "action_bar_title", "mTitleTextView");
}
private static @Nullable
View findActionBarSubTitle(@NonNull View root) {
return findActionBarItem(root, "action_bar_subtitle", "mSubtitleTextView");
}
private static @Nullable View findActionBarItem(@NonNull View root,
@NonNull String resourceName,
@NonNull String toolbarFieldName) {
View result = findViewSupportOrAndroid(root, resourceName);
if (result == null) {
View actionBar = findViewSupportOrAndroid(root, "action_bar");
if (actionBar != null) {
result = reflectiveRead(actionBar, toolbarFieldName);
}
}
if (result == null && root.getClass().getName().endsWith("widget.Toolbar")) {
result = reflectiveRead(root, toolbarFieldName);
}
return result;
}
@SuppressWarnings("ConstantConditions")
private static @Nullable View findViewSupportOrAndroid(@NonNull View root,
@NonNull String resourceName) {
Context context = root.getContext();
View result = null;
if (result == null) {
int supportID = context.getResources().getIdentifier(resourceName, "id", context.getPackageName());
result = root.findViewById(supportID);
}
if (result == null) {
int androidID = context.getResources().getIdentifier(resourceName, "id", "android");
result = root.findViewById(androidID);
}
return result;
}
@SuppressWarnings("unchecked")
private static <T> T reflectiveRead(@NonNull Object object, @NonNull String fieldName) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(object);
} catch (final Exception ex) {
return null;
}
}
}

View File

@ -0,0 +1,102 @@
package eu.siacs.conversations.ui.util;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.UIHelper;
public class QuoteHelper {
public static final char QUOTE_CHAR = '>';
public static final char QUOTE_END_CHAR = '<'; // used for one check, not for actual quoting
public static final char QUOTE_ALT_CHAR = '»';
public static final char QUOTE_ALT_END_CHAR = '«';
public static boolean isPositionQuoteCharacter(CharSequence body, int pos){
// second part of logical check actually goes against the logic indicated in the method name, since it also checks for context
// but it's very useful
return body.charAt(pos) == QUOTE_CHAR || isPositionAltQuoteStart(body, pos);
}
public static boolean isPositionQuoteEndCharacter(CharSequence body, int pos){
return body.charAt(pos) == QUOTE_END_CHAR;
}
public static boolean isPositionAltQuoteCharacter (CharSequence body, int pos){
return body.charAt(pos) == QUOTE_ALT_CHAR;
}
public static boolean isPositionAltQuoteEndCharacter(CharSequence body, int pos){
return body.charAt(pos) == QUOTE_ALT_END_CHAR;
}
public static boolean isPositionAltQuoteStart(CharSequence body, int pos){
return isPositionAltQuoteCharacter(body, pos) && !isPositionFollowedByAltQuoteEnd(body, pos);
}
public static boolean isPositionFollowedByQuoteChar(CharSequence body, int pos) {
return body.length() > pos + 1 && isPositionQuoteCharacter(body, pos +1 );
}
// 'Prequote' means anything we require or can accept in front of a QuoteChar
public static boolean isPositionPrecededByPrequote(CharSequence body, int pos){
return UIHelper.isPositionPrecededByLineStart(body, pos);
}
public static boolean isPositionQuoteStart (CharSequence body, int pos){
return (isPositionQuoteCharacter(body, pos)
&& isPositionPrecededByPrequote(body, pos)
&& (UIHelper.isPositionFollowedByQuoteableCharacter(body, pos)
|| isPositionFollowedByQuoteChar(body, pos)));
}
public static boolean bodyContainsQuoteStart (CharSequence body){
for (int i = 0; i < body.length(); i++){
if (isPositionQuoteStart(body, i)){
return true;
}
}
return false;
}
public static boolean isPositionFollowedByAltQuoteEnd(CharSequence body, int pos) {
if (body.length() <= pos + 1 || Character.isWhitespace(body.charAt(pos + 1))) {
return false;
}
boolean previousWasWhitespace = false;
for (int i = pos + 1; i < body.length(); i++) {
char c = body.charAt(i);
if (c == '\n' || isPositionAltQuoteCharacter(body, i)) {
return false;
} else if (isPositionAltQuoteEndCharacter(body, i) && !previousWasWhitespace) {
return true;
} else {
previousWasWhitespace = Character.isWhitespace(c);
}
}
return false;
}
public static boolean isNestedTooDeeply (CharSequence line){
if (isPositionQuoteStart(line, 0)) {
int nestingDepth = 1;
for (int i = 1; i < line.length(); i++) {
if (isPositionQuoteStart(line, i)) {
nestingDepth++;
}
if (nestingDepth > (Config.QUOTING_MAX_DEPTH - 1)) {
return true;
}
}
}
return false;
}
public static String replaceAltQuoteCharsInText(String text){
for (int i = 0; i < text.length(); i++){
if (isPositionAltQuoteStart(text, i)){
text = text.substring(0, i) + QUOTE_CHAR + text.substring(i + 1);
}
}
return text;
}
}

View File

@ -0,0 +1,26 @@
package eu.siacs.conversations.ui.util;
import android.util.Rational;
public final class Rationals {
//between 2.39:1 and 1:2.39 (inclusive).
private static final Rational MIN = new Rational(100,239);
private static final Rational MAX = new Rational(239,100);
private Rationals() {
}
public static Rational clip(final Rational input) {
if (input.compareTo(MIN) < 0) {
return MIN;
}
if (input.compareTo(MAX) > 0) {
return MAX;
}
return input;
}
}

View File

@ -0,0 +1,20 @@
package eu.siacs.conversations.ui.util;
import android.app.Activity;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.view.Window;
import android.view.WindowManager;
public class SettingsUtils {
public static void applyScreenshotPreventionSetting(Activity activity){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
boolean preventScreenshots = preferences.getBoolean("prevent_screenshots", false);
Window activityWindow = activity.getWindow();
if(preventScreenshots){
activityWindow.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
} else {
activityWindow.clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
}
}
}

View File

@ -24,6 +24,7 @@ import java.util.concurrent.Executors;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.ui.util.QuoteHelper;
public class EditMessage extends EmojiWrapperEditText {
@ -142,7 +143,8 @@ public class EditMessage extends EmojiWrapperEditText {
}
public void insertAsQuote(String text) {
text = text.replaceAll("(\n *){2,}", "\n").replaceAll("(^|\n)", "$1> ").replaceAll("\n$", "");
text = QuoteHelper.replaceAltQuoteCharsInText(text);
text = text.replaceAll("(\n *){2,}", "\n").replaceAll("(^|\n)(" + QuoteHelper.QUOTE_CHAR + ")", "$1$2$2").replaceAll("(^|\n)([^" + QuoteHelper.QUOTE_CHAR + "])", "$1> $2").replaceAll("\n$", "");
Editable editable = getEditableText();
int position = getSelectionEnd();
if (position == -1) position = editable.length();

View File

@ -0,0 +1,48 @@
package eu.siacs.conversations.ui.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Rational;
import eu.siacs.conversations.Config;
public class SurfaceViewRenderer extends org.webrtc.SurfaceViewRenderer {
private Rational aspectRatio = new Rational(1,1);
private OnAspectRatioChanged onAspectRatioChanged;
public SurfaceViewRenderer(Context context) {
super(context);
}
public SurfaceViewRenderer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) {
super.onFrameResolutionChanged(videoWidth, videoHeight, rotation);
final int rotatedWidth = rotation != 0 && rotation != 180 ? videoHeight : videoWidth;
final int rotatedHeight = rotation != 0 && rotation != 180 ? videoWidth : videoHeight;
final Rational currentRational = this.aspectRatio;
this.aspectRatio = new Rational(rotatedWidth, rotatedHeight);
Log.d(Config.LOGTAG,"onFrameResolutionChanged("+rotatedWidth+","+rotatedHeight+","+aspectRatio+")");
if (currentRational.equals(this.aspectRatio) || onAspectRatioChanged == null) {
return;
}
onAspectRatioChanged.onAspectRatioChanged(this.aspectRatio);
}
public void setOnAspectRatioChanged(final OnAspectRatioChanged onAspectRatioChanged) {
this.onAspectRatioChanged = onAspectRatioChanged;
}
public Rational getAspectRatio() {
return this.aspectRatio;
}
public interface OnAspectRatioChanged {
void onAspectRatioChanged(final Rational rational);
}
}

View File

@ -1,161 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.siacs.conversations.utils;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
public class ExifHelper {
private static final String TAG = "CameraExif";
public static int getOrientation(InputStream is) {
if (is == null) {
return 0;
}
byte[] buf = new byte[8];
int length = 0;
// ISO/IEC 10918-1:1993(E)
while (read(is, buf, 2) && (buf[0] & 0xFF) == 0xFF) {
int marker = buf[1] & 0xFF;
// Check if the marker is a padding.
if (marker == 0xFF) {
continue;
}
// Check if the marker is SOI or TEM.
if (marker == 0xD8 || marker == 0x01) {
continue;
}
// Check if the marker is EOI or SOS.
if (marker == 0xD9 || marker == 0xDA) {
return 0;
}
// Get the length and check if it is reasonable.
if (!read(is, buf, 2)) {
return 0;
}
length = pack(buf, 0, 2, false);
if (length < 2) {
Log.e(TAG, "Invalid length");
return 0;
}
length -= 2;
// Break if the marker is EXIF in APP1.
if (marker == 0xE1 && length >= 6) {
if (!read(is, buf, 6)) return 0;
length -= 6;
if (pack(buf, 0, 4, false) == 0x45786966 &&
pack(buf, 4, 2, false) == 0) {
break;
}
}
// Skip other markers.
try {
is.skip(length);
} catch (IOException ex) {
return 0;
}
length = 0;
}
// JEITA CP-3451 Exif Version 2.2
if (length > 8) {
int offset = 0;
byte[] jpeg = new byte[length];
if (!read(is, jpeg, length)) {
return 0;
}
// Identify the byte order.
int tag = pack(jpeg, offset, 4, false);
if (tag != 0x49492A00 && tag != 0x4D4D002A) {
Log.e(TAG, "Invalid byte order");
return 0;
}
boolean littleEndian = (tag == 0x49492A00);
// Get the offset and check if it is reasonable.
int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
if (count < 10 || count > length) {
Log.e(TAG, "Invalid offset");
return 0;
}
offset += count;
length -= count;
// Get the count and go through all the elements.
count = pack(jpeg, offset - 2, 2, littleEndian);
while (count-- > 0 && length >= 12) {
// Get the tag and check if it is orientation.
tag = pack(jpeg, offset, 2, littleEndian);
if (tag == 0x0112) {
// We do not really care about type and count, do we?
int orientation = pack(jpeg, offset + 8, 2, littleEndian);
switch (orientation) {
case 1:
return 0;
case 3:
return 180;
case 6:
return 90;
case 8:
return 270;
}
Log.i(TAG, "Unsupported orientation");
return 0;
}
offset += 12;
length -= 12;
}
}
Log.i(TAG, "Orientation not found");
return 0;
}
private static int pack(byte[] bytes, int offset, int length,
boolean littleEndian) {
int step = 1;
if (littleEndian) {
offset += length - 1;
step = -1;
}
int value = 0;
while (length-- > 0) {
value = (value << 8) | (bytes[offset] & 0xFF);
offset += step;
}
return value;
}
private static boolean read(InputStream is, byte[] buf, int length) {
try {
return is.read(buf, 0, length) == length;
} catch (IOException ex) {
return false;
}
}
}

View File

@ -39,6 +39,7 @@ import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.http.AesGcmURL;
import eu.siacs.conversations.http.URL;
import eu.siacs.conversations.ui.util.QuoteHelper;
public class MessageUtils {
@ -69,8 +70,7 @@ public class MessageUtils {
continue;
}
final char c = line.charAt(0);
if (c == '>' && UIHelper.isPositionFollowedByQuoteableCharacter(line, 0)
|| (c == '\u00bb' && !UIHelper.isPositionFollowedByQuote(line, 0))) {
if (QuoteHelper.isNestedTooDeeply(line)) {
continue;
}
if (builder.length() != 0) {
@ -115,6 +115,6 @@ public class MessageUtils {
}
public static boolean unInitiatedButKnownSize(Message message) {
return message.getType() == Message.TYPE_TEXT && message.getTransferable() == null && message.isOOb() && message.getFileParams().size > 0 && message.getFileParams().url != null;
return message.getType() == Message.TYPE_TEXT && message.getTransferable() == null && message.isOOb() && message.getFileParams().size != null && message.getFileParams().url != null;
}
}

View File

@ -247,6 +247,7 @@ public final class MimeUtils {
add("audio/mpeg", "m4a");
add("audio/mpegurl", "m3u");
add("audio/ogg", "oga");
add("audio/opus", "opus");
add("audio/prs.sid", "sid");
add("audio/x-aiff", "aif");
add("audio/x-aiff", "aiff");
@ -567,6 +568,8 @@ public final class MimeUtils {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} catch (Exception e) {
return null;
}
return null;
}

View File

@ -254,6 +254,39 @@ public class Patterns {
+ "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
+ "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+ "|[1-9][0-9]|[0-9]))");
/**
* IPv6 address matcher for
* IPv6 addresses
* zero compressed IPv6 addresses (section 2.2 of rfc5952)
* link-local IPv6 addresses with zone index (section 11 of rfc4007)
* IPv4-Embedded IPv6 Address (section 2 of rfc6052)
* IPv4-mapped IPv6 addresses (section 2.1 of rfc2765)
* IPv4-translated addresses (section 2.1 of rfc2765)
*
* Taken from https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses/17871737#17871737
*/
public static final Pattern IP6_ADDRESS
= Pattern.compile(
"\\[" +
"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" +
"([0-9a-fA-F]{1,4}:){1,7}:|" +
"([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" +
"([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" +
"([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" +
"([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" +
"([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" +
"[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|" +
":((:[0-9a-fA-F]{1,4}){1,7}|:)|" +
"fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|" +
"::(ffff(:0{1,4}){0,1}:){0,1}" +
"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}" +
"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|" +
"([0-9a-fA-F]{1,4}:){1,4}:" +
"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}" +
"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))" +
"\\]"
);
/**
* Valid UCS characters defined in RFC 3987. Excludes space characters.
*/
@ -296,7 +329,7 @@ public class Patterns {
private static final String TLD = "(" + PUNYCODE_TLD + "|" + "[" + TLD_CHAR + "]{2,63}" +")";
private static final String HOST_NAME = "(" + IRI_LABEL + "\\.)+" + TLD;
public static final Pattern DOMAIN_NAME
= Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")");
= Pattern.compile("(" + HOST_NAME + "|" + IP6_ADDRESS + "|" + IP_ADDRESS +")");
private static final String PROTOCOL = "(?i:http|https|rtsp):\\/\\/";
/* A word boundary or end of input. This is to stop foo.sure from matching as foo.su */
private static final String WORD_BOUNDARY = "(?:\\b|$|^)";
@ -306,7 +339,7 @@ public class Patterns {
private static final String PORT_NUMBER = "\\:\\d{1,5}";
private static final String PATH_AND_QUERY = "\\/(?:(?:[" + LABEL_CHAR
+ "\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus optional query params
+ "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*";
+ "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_\\$])|(?:\\%[a-fA-F0-9]{2}))*";
/**
* Regular expression pattern to match most part of RFC 3987
* Internationalized URLs, aka IRIs.
@ -335,12 +368,12 @@ public class Patterns {
* {@link #IP_ADDRESS}
*/
private static final Pattern STRICT_DOMAIN_NAME
= Pattern.compile("(?:" + STRICT_HOST_NAME + "|" + IP_ADDRESS + ")");
= Pattern.compile("(?:" + STRICT_HOST_NAME + "|" + IP_ADDRESS + "|" + IP6_ADDRESS + ")");
/**
* Regular expression that matches domain names without a TLD
*/
private static final String RELAXED_DOMAIN_NAME =
"(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" +"?)+" + "|" + IP_ADDRESS + ")";
"(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" +"?)+" + "|" + IP_ADDRESS + "|" + IP6_ADDRESS + ")";
/**
* Regular expression to match strings that do not start with a supported protocol. The TLDs
* are expected to be one of the known TLDs.

View File

@ -32,6 +32,7 @@ import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.entities.RtpSessionStatus;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.services.ExportBackupService;
import eu.siacs.conversations.ui.util.QuoteHelper;
import eu.siacs.conversations.xmpp.Jid;
public class UIHelper {
@ -328,7 +329,7 @@ public class UIHelper {
continue;
}
char first = l.charAt(0);
if ((first != '>' || !isPositionFollowedByQuoteableCharacter(l, 0)) && first != '\u00bb') {
if ((!QuoteHelper.isPositionQuoteStart(l, 0))) {
CharSequence line = CharSequenceUtils.trim(l);
if (line.length() == 0) {
continue;
@ -372,6 +373,23 @@ public class UIHelper {
return input.length() > 256 ? StylingHelper.subSequence(input, 0, 256) : input;
}
public static boolean isPositionPrecededByBodyStart(CharSequence body, int pos){
// true if not a single linebreak before current position
for (int i = pos - 1; i >= 0; i--){
if (body.charAt(i) != ' '){
return false;
}
}
return true;
}
public static boolean isPositionPrecededByLineStart(CharSequence body, int pos){
if (isPositionPrecededByBodyStart(body, pos)){
return true;
}
return body.charAt(pos - 1) == '\n';
}
public static boolean isPositionFollowedByQuoteableCharacter(CharSequence body, int pos) {
return !isPositionFollowedByNumber(body, pos)
&& !isPositionFollowedByEmoticon(body, pos)
@ -404,6 +422,7 @@ public class UIHelper {
final char first = body.charAt(pos + 1);
return first == ';'
|| first == ':'
|| first == '.' // do not quote >.< (but >>.<)
|| closingBeforeWhitespace(body, pos + 1);
}
}
@ -413,31 +432,13 @@ public class UIHelper {
final char c = body.charAt(i);
if (Character.isWhitespace(c)) {
return false;
} else if (c == '<' || c == '>') {
} else if (QuoteHelper.isPositionQuoteCharacter(body, pos) || QuoteHelper.isPositionQuoteEndCharacter(body, pos)) {
return body.length() == i + 1 || Character.isWhitespace(body.charAt(i + 1));
}
}
return false;
}
public static boolean isPositionFollowedByQuote(CharSequence body, int pos) {
if (body.length() <= pos + 1 || Character.isWhitespace(body.charAt(pos + 1))) {
return false;
}
boolean previousWasWhitespace = false;
for (int i = pos + 1; i < body.length(); i++) {
char c = body.charAt(i);
if (c == '\n' || c == '»') {
return false;
} else if (c == '«' && !previousWasWhitespace) {
return true;
} else {
previousWasWhitespace = Character.isWhitespace(c);
}
}
return false;
}
public static String getDisplayName(MucOptions.User user) {
Contact contact = user.getContact();
if (contact != null) {
@ -475,9 +476,6 @@ public class UIHelper {
}
public static String getFileDescriptionString(final Context context, final Message message) {
if (message.getType() == Message.TYPE_IMAGE) {
return context.getString(R.string.image);
}
final String mime = message.getMimeType();
if (mime == null) {
return context.getString(R.string.file);
@ -487,7 +485,9 @@ public class UIHelper {
return context.getString(R.string.video);
} else if (mime.equals("image/gif")) {
return context.getString(R.string.gif);
} else if (mime.startsWith("image/")) {
} else if (mime.equals("image/svg+xml")) {
return context.getString(R.string.vector_graphic);
} else if (mime.startsWith("image/") || message.getType() == Message.TYPE_IMAGE) {
return context.getString(R.string.image);
} else if (mime.contains("pdf")) {
return context.getString(R.string.pdf_document);
@ -503,6 +503,8 @@ public class UIHelper {
return context.getString(R.string.ebook);
} else if (mime.equals("application/gpx+xml")) {
return context.getString(R.string.gpx_track);
} else if (mime.equals("text/plain")) {
return context.getString(R.string.plain_text_document);
} else {
return mime;
}

View File

@ -136,6 +136,7 @@ public abstract class AbstractJingleConnection {
TERMINATED_DECLINED_OR_BUSY, //equal to 'ENDED' (after other party declined the call)
TERMINATED_CONNECTIVITY_ERROR, //equal to 'ENDED' (but after network failures; ui will display retry button)
TERMINATED_CANCEL_OR_TIMEOUT, //more or less the same as retracted; caller pressed end call before session was accepted
TERMINATED_APPLICATION_FAILURE
TERMINATED_APPLICATION_FAILURE,
TERMINATED_SECURITY_ERROR
}
}

View File

@ -1239,11 +1239,11 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
}
@Override
public long getFileSize() {
public Long getFileSize() {
if (this.file != null) {
return this.file.getExpectedSize();
} else {
return 0;
return null;
}
}

View File

@ -3,6 +3,9 @@ package eu.siacs.conversations.xmpp.jingle;
import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
@ -12,7 +15,10 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
@ -71,7 +77,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
State.TERMINATED_DECLINED_OR_BUSY,
State.TERMINATED_CONNECTIVITY_ERROR,
State.TERMINATED_CANCEL_OR_TIMEOUT,
State.TERMINATED_APPLICATION_FAILURE
State.TERMINATED_APPLICATION_FAILURE,
State.TERMINATED_SECURITY_ERROR
);
private static final Map<State, Collection<State>> VALID_TRANSITIONS;
@ -81,7 +88,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
transitionBuilder.put(State.NULL, ImmutableList.of(
State.PROPOSED,
State.SESSION_INITIALIZED,
State.TERMINATED_APPLICATION_FAILURE
State.TERMINATED_APPLICATION_FAILURE,
State.TERMINATED_SECURITY_ERROR
));
transitionBuilder.put(State.PROPOSED, ImmutableList.of(
State.ACCEPTED,
@ -89,6 +97,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
State.REJECTED,
State.RETRACTED,
State.TERMINATED_APPLICATION_FAILURE,
State.TERMINATED_SECURITY_ERROR,
State.TERMINATED_CONNECTIVITY_ERROR //only used when the xmpp connection rebinds
));
transitionBuilder.put(State.PROCEED, ImmutableList.of(
@ -97,6 +106,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
State.SESSION_INITIALIZED_PRE_APPROVED,
State.TERMINATED_SUCCESS,
State.TERMINATED_APPLICATION_FAILURE,
State.TERMINATED_SECURITY_ERROR,
State.TERMINATED_CONNECTIVITY_ERROR //at this state used for error bounces of the proceed message
));
transitionBuilder.put(State.SESSION_INITIALIZED, ImmutableList.of(
@ -105,7 +115,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
State.TERMINATED_DECLINED_OR_BUSY,
State.TERMINATED_CONNECTIVITY_ERROR, //at this state used for IQ errors and IQ timeouts
State.TERMINATED_CANCEL_OR_TIMEOUT,
State.TERMINATED_APPLICATION_FAILURE
State.TERMINATED_APPLICATION_FAILURE,
State.TERMINATED_SECURITY_ERROR
));
transitionBuilder.put(State.SESSION_INITIALIZED_PRE_APPROVED, ImmutableList.of(
State.SESSION_ACCEPTED,
@ -113,14 +124,16 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
State.TERMINATED_DECLINED_OR_BUSY,
State.TERMINATED_CONNECTIVITY_ERROR, //at this state used for IQ errors and IQ timeouts
State.TERMINATED_CANCEL_OR_TIMEOUT,
State.TERMINATED_APPLICATION_FAILURE
State.TERMINATED_APPLICATION_FAILURE,
State.TERMINATED_SECURITY_ERROR
));
transitionBuilder.put(State.SESSION_ACCEPTED, ImmutableList.of(
State.TERMINATED_SUCCESS,
State.TERMINATED_DECLINED_OR_BUSY,
State.TERMINATED_CONNECTIVITY_ERROR,
State.TERMINATED_CANCEL_OR_TIMEOUT,
State.TERMINATED_APPLICATION_FAILURE
State.TERMINATED_APPLICATION_FAILURE,
State.TERMINATED_SECURITY_ERROR
));
VALID_TRANSITIONS = transitionBuilder.build();
}
@ -164,8 +177,9 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
case CANCEL:
case TIMEOUT:
return State.TERMINATED_CANCEL_OR_TIMEOUT;
case FAILED_APPLICATION:
case SECURITY_ERROR:
return State.TERMINATED_SECURITY_ERROR;
case FAILED_APPLICATION:
case UNSUPPORTED_TRANSPORTS:
case UNSUPPORTED_APPLICATIONS:
return State.TERMINATED_APPLICATION_FAILURE;
@ -235,7 +249,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
private void receiveTransportInfo(final JinglePacket jinglePacket) {
if (isInState(State.SESSION_INITIALIZED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED)) {
//Due to the asynchronicity of processing session-init we might move from NULL|PROCEED to INITIALIZED only after transport-info has been received
if (isInState(State.NULL, State.PROCEED, State.SESSION_INITIALIZED, State.SESSION_INITIALIZED_PRE_APPROVED, State.SESSION_ACCEPTED)) {
respondOk(jinglePacket);
final RtpContentMap contentMap;
try {
@ -298,22 +313,22 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
}
private RtpContentMap receiveRtpContentMap(final JinglePacket jinglePacket, final boolean expectVerification) {
private ListenableFuture<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);
final ListenableFuture<AxolotlService.OmemoVerifiedPayload<RtpContentMap>> future = id.account.getAxolotlService().decrypt((OmemoVerifiedRtpContentMap) receivedContentMap, id.with);
return Futures.transform(future, omemoVerifiedPayload -> {
//TODO test if an exception here triggers a correct abort
omemoVerification.setOrEnsureEqual(omemoVerifiedPayload);
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received verifiable DTLS fingerprint via " + omemoVerification);
return omemoVerifiedPayload.getPayload();
} else if (expectVerification) {
throw new SecurityException("DTLS fingerprint was unexpectedly not verifiable");
}, MoreExecutors.directExecutor());
} else if (Config.REQUIRE_RTP_VERIFICATION || expectVerification) {
return Futures.immediateFailedFuture(
new SecurityException("DTLS fingerprint was unexpectedly not verifiable")
);
} else {
return receivedContentMap;
return Futures.immediateFuture(receivedContentMap);
}
}
@ -332,9 +347,23 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
return;
}
final RtpContentMap contentMap;
final ListenableFuture<RtpContentMap> future = receiveRtpContentMap(jinglePacket, false);
Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
@Override
public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
receiveSessionInitiate(jinglePacket, rtpContentMap);
}
@Override
public void onFailure(@NonNull final Throwable throwable) {
respondOk(jinglePacket);
sendSessionTerminate(Reason.ofThrowable(throwable), throwable.getMessage());
}
}, MoreExecutors.directExecutor());
}
private void receiveSessionInitiate(final JinglePacket jinglePacket, final RtpContentMap contentMap) {
try {
contentMap = receiveRtpContentMap(jinglePacket, false);
contentMap.requireContentDescriptions();
contentMap.requireDTLSFingerprint();
} catch (final RuntimeException e) {
@ -388,9 +417,25 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
terminateWithOutOfOrder(jinglePacket);
return;
}
final RtpContentMap contentMap;
final ListenableFuture<RtpContentMap> future = receiveRtpContentMap(jinglePacket, this.omemoVerification.hasFingerprint());
Futures.addCallback(future, new FutureCallback<RtpContentMap>() {
@Override
public void onSuccess(@Nullable RtpContentMap rtpContentMap) {
receiveSessionAccept(jinglePacket, rtpContentMap);
}
@Override
public void onFailure(@NonNull final Throwable throwable) {
respondOk(jinglePacket);
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents in session-accept", throwable);
webRTCWrapper.close();
sendSessionTerminate(Reason.ofThrowable(throwable), throwable.getMessage());
}
}, MoreExecutors.directExecutor());
}
private void receiveSessionAccept(final JinglePacket jinglePacket, final RtpContentMap contentMap) {
try {
contentMap = receiveRtpContentMap(jinglePacket, this.omemoVerification.hasFingerprint());
contentMap.requireContentDescriptions();
contentMap.requireDTLSFingerprint();
} catch (final RuntimeException e) {
@ -488,17 +533,21 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
this.webRTCWrapper.setRemoteDescription(sdp).get();
addIceCandidatesFromBlackLog();
org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createAnswer().get();
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription);
sendSessionAccept(respondingRtpContentMap);
this.webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
prepareSessionAccept(webRTCSessionDescription);
} catch (final Exception e) {
Log.d(Config.LOGTAG, "unable to send session accept", Throwables.getRootCause(e));
webRTCWrapper.close();
sendSessionTerminate(Reason.FAILED_APPLICATION);
failureToAcceptSession(e);
}
}
private void failureToAcceptSession(final Throwable throwable) {
if (isTerminated()) {
return;
}
Log.d(Config.LOGTAG, "unable to send session accept", Throwables.getRootCause(throwable));
webRTCWrapper.close();
sendSessionTerminate(Reason.ofThrowable(throwable));
}
private void addIceCandidatesFromBlackLog() {
while (!this.pendingIceCandidates.isEmpty()) {
processCandidates(this.pendingIceCandidates.poll());
@ -506,24 +555,53 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
}
private void sendSessionAccept(final RtpContentMap rtpContentMap) {
this.responderRtpContentMap = rtpContentMap;
this.transitionOrThrow(State.SESSION_ACCEPTED);
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);
private void prepareSessionAccept(final org.webrtc.SessionDescription webRTCSessionDescription) {
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription);
this.responderRtpContentMap = respondingRtpContentMap;
final ListenableFuture<RtpContentMap> outgoingContentMapFuture = prepareOutgoingContentMap(respondingRtpContentMap);
Futures.addCallback(outgoingContentMapFuture,
new FutureCallback<RtpContentMap>() {
@Override
public void onSuccess(final RtpContentMap outgoingContentMap) {
sendSessionAccept(outgoingContentMap, webRTCSessionDescription);
}
} else {
outgoingContentMap = rtpContentMap;
@Override
public void onFailure(@NonNull Throwable throwable) {
failureToAcceptSession(throwable);
}
final JinglePacket sessionAccept = outgoingContentMap.toJinglePacket(JinglePacket.Action.SESSION_ACCEPT, id.sessionId);
},
MoreExecutors.directExecutor()
);
}
private void sendSessionAccept(final RtpContentMap rtpContentMap, final org.webrtc.SessionDescription webRTCSessionDescription) {
if (isTerminated()) {
Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": preparing session accept was too slow. already terminated. nothing to do.");
return;
}
transitionOrThrow(State.SESSION_ACCEPTED);
final JinglePacket sessionAccept = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_ACCEPT, id.sessionId);
send(sessionAccept);
try {
webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
} catch (Exception e) {
failureToAcceptSession(e);
}
}
private ListenableFuture<RtpContentMap> prepareOutgoingContentMap(final RtpContentMap rtpContentMap) {
if (this.omemoVerification.hasDeviceId()) {
ListenableFuture<AxolotlService.OmemoVerifiedPayload<OmemoVerifiedRtpContentMap>> verifiedPayloadFuture = id.account.getAxolotlService()
.encrypt(rtpContentMap, id.with, omemoVerification.getDeviceId());
return Futures.transform(verifiedPayloadFuture, verifiedPayload -> {
omemoVerification.setOrEnsureEqual(verifiedPayload);
return verifiedPayload.getPayload();
}, MoreExecutors.directExecutor());
} else {
return Futures.immediateFuture(rtpContentMap);
}
}
synchronized void deliveryMessage(final Jid from, final Element message, final String serverMessageId, final long timestamp) {
@ -754,52 +832,93 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} catch (final WebRTCWrapper.InitializationException e) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to initialize WebRTC");
webRTCWrapper.close();
sendJingleMessage("retract", id.with.asBareJid());
transitionOrThrow(State.TERMINATED_APPLICATION_FAILURE);
this.finish();
sendRetract(Reason.ofThrowable(e));
return;
}
try {
org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createOffer().get();
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription);
sendSessionInitiate(rtpContentMap, targetState);
this.webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
prepareSessionInitiate(webRTCSessionDescription, targetState);
} catch (final Exception e) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to sendSessionInitiate", Throwables.getRootCause(e));
failureToInitiateSession(e, targetState);
}
}
private void failureToInitiateSession(final Throwable throwable, final State targetState) {
if (isTerminated()) {
return;
}
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to sendSessionInitiate", Throwables.getRootCause(throwable));
webRTCWrapper.close();
final Reason reason = Reason.ofThrowable(throwable);
if (isInState(targetState)) {
sendSessionTerminate(Reason.FAILED_APPLICATION);
sendSessionTerminate(reason);
} else {
sendRetract(reason);
}
}
private void sendRetract(final Reason reason) {
//TODO embed reason into retract
sendJingleMessage("retract", id.with.asBareJid());
transitionOrThrow(State.TERMINATED_APPLICATION_FAILURE);
transitionOrThrow(reasonToState(reason));
this.finish();
}
}
}
private void sendSessionInitiate(final RtpContentMap rtpContentMap, final State targetState) {
private void prepareSessionInitiate(final org.webrtc.SessionDescription webRTCSessionDescription, final State targetState) {
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription);
this.initiatorRtpContentMap = rtpContentMap;
this.transitionOrThrow(targetState);
//TODO do on background thread?
final RtpContentMap outgoingContentMap = encryptSessionInitiate(rtpContentMap);
final JinglePacket sessionInitiate = outgoingContentMap.toJinglePacket(JinglePacket.Action.SESSION_INITIATE, id.sessionId);
send(sessionInitiate);
final ListenableFuture<RtpContentMap> outgoingContentMapFuture = encryptSessionInitiate(rtpContentMap);
Futures.addCallback(outgoingContentMapFuture, new FutureCallback<RtpContentMap>() {
@Override
public void onSuccess(final RtpContentMap outgoingContentMap) {
sendSessionInitiate(outgoingContentMap, webRTCSessionDescription, targetState);
}
private RtpContentMap encryptSessionInitiate(final RtpContentMap rtpContentMap) {
if (this.omemoVerification.hasDeviceId()) {
final AxolotlService.OmemoVerifiedPayload<OmemoVerifiedRtpContentMap> verifiedPayload;
@Override
public void onFailure(@NonNull final Throwable throwable) {
failureToInitiateSession(throwable, targetState);
}
}, MoreExecutors.directExecutor());
}
private void sendSessionInitiate(final RtpContentMap rtpContentMap, final org.webrtc.SessionDescription webRTCSessionDescription, final State targetState) {
if (isTerminated()) {
Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": preparing session was too slow. already terminated. nothing to do.");
return;
}
this.transitionOrThrow(targetState);
final JinglePacket sessionInitiate = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_INITIATE, id.sessionId);
send(sessionInitiate);
try {
verifiedPayload = id.account.getAxolotlService().encrypt(rtpContentMap, id.with, omemoVerification.getDeviceId());
} catch (final CryptoFailedException e) {
this.webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
} catch (Exception e) {
failureToInitiateSession(e, targetState);
}
}
private ListenableFuture<RtpContentMap> encryptSessionInitiate(final RtpContentMap rtpContentMap) {
if (this.omemoVerification.hasDeviceId()) {
final ListenableFuture<AxolotlService.OmemoVerifiedPayload<OmemoVerifiedRtpContentMap>> verifiedPayloadFuture = id.account.getAxolotlService()
.encrypt(rtpContentMap, id.with, omemoVerification.getDeviceId());
final ListenableFuture<RtpContentMap> future = Futures.transform(verifiedPayloadFuture, verifiedPayload -> {
omemoVerification.setSessionFingerprint(verifiedPayload.getFingerprint());
return verifiedPayload.getPayload();
}, MoreExecutors.directExecutor());
if (Config.REQUIRE_RTP_VERIFICATION) {
return future;
}
return Futures.catching(
future,
CryptoFailedException.class,
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();
},
MoreExecutors.directExecutor()
);
} else {
return rtpContentMap;
return Futures.immediateFuture(rtpContentMap);
}
}
@ -919,6 +1038,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return RtpEndUserState.CONNECTING;
}
case SESSION_ACCEPTED:
//TODO refactor this out into separate method (that uses switch for better readability)
final PeerConnection.PeerConnectionState state;
try {
state = webRTCWrapper.getState();
@ -959,6 +1079,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return rtpConnectionStarted == 0 ? RtpEndUserState.CONNECTIVITY_ERROR : RtpEndUserState.CONNECTIVITY_LOST_ERROR;
case TERMINATED_APPLICATION_FAILURE:
return RtpEndUserState.APPLICATION_ERROR;
case TERMINATED_SECURITY_ERROR:
return RtpEndUserState.SECURITY_ERROR;
}
throw new IllegalStateException(String.format("%s has no equivalent EndUserState", this.state));
}
@ -997,7 +1119,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return false;
}
final FingerprintStatus status = id.account.getAxolotlService().getFingerprintTrust(fingerprint);
return status != null && status.getTrust() == FingerprintStatus.Trust.VERIFIED;
return status != null && status.isVerified();
}
public synchronized void acceptCall() {
@ -1219,9 +1341,13 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
if (newState == PeerConnection.PeerConnectionState.CLOSED && this.rtpConnectionEnded == 0) {
this.rtpConnectionEnded = SystemClock.elapsedRealtime();
}
//TODO 'DISCONNECTED' might be an opportunity to renew the offer and send a transport-replace
//TODO exact syntax is yet to be determined but transport-replace sounds like the most reasonable
//as there is no content-replace
//TODO 'failed' means we need to restart ICE
//
//TODO 'disconnected' can probably be ignored as "This is a less stringent test than failed
// and may trigger intermittently and resolve just as spontaneously on less reliable networks,
// or during temporary disconnections. When the problem resolves, the connection may return
// to the connected state."
// Obviously the UI needs to reflect this new state with a 'reconnecting' display or something
if (Arrays.asList(PeerConnection.PeerConnectionState.FAILED, PeerConnection.PeerConnectionState.DISCONNECTED).contains(newState)) {
if (isTerminated()) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": not sending session-terminate after connectivity error because session is already in state " + this.state);

View File

@ -55,7 +55,7 @@ public class OmemoVerification {
throw new IllegalStateException("No session fingerprint has been previously provided");
}
if (!sessionFingerprint.equals(this.sessionFingerprint)) {
throw new IllegalStateException("Session Fingerprints did not match");
throw new SecurityException("Session Fingerprints did not match");
}
if (this.deviceId == null) {
throw new IllegalStateException("No Device Id has been previously provided");

View File

@ -13,5 +13,6 @@ public enum RtpEndUserState {
CONNECTIVITY_ERROR, //network error; retry button
CONNECTIVITY_LOST_ERROR, //network error but for call duration > 0
RETRACTED, //user pressed home or power button during 'ringing' - shows retry button
APPLICATION_ERROR //something rather bad happened; libwebrtc failed or we got in IQ-error
APPLICATION_ERROR, //something rather bad happened; libwebrtc failed or we got in IQ-error
SECURITY_ERROR //problem with DTLS (missing) or verification
}

View File

@ -37,7 +37,7 @@ class ToneManager {
private static ToneState of(final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
if (isInitiator) {
if (asList(RtpEndUserState.RINGING, RtpEndUserState.CONNECTING).contains(state)) {
if (asList(RtpEndUserState.FINDING_DEVICE, RtpEndUserState.RINGING, RtpEndUserState.CONNECTING).contains(state)) {
return ToneState.RINGING;
}
if (state == RtpEndUserState.DECLINED_OR_BUSY) {

View File

@ -3,7 +3,10 @@ package eu.siacs.conversations.xmpp.jingle.stanzas;
import androidx.annotation.NonNull;
import com.google.common.base.CaseFormat;
import com.google.common.base.Throwables;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.CryptoFailedException;
import eu.siacs.conversations.xmpp.jingle.RtpContentMap;
public enum Reason {
@ -51,4 +54,15 @@ public enum Reason {
return FAILED_APPLICATION;
}
}
public static Reason ofThrowable(final Throwable throwable) {
final Throwable root = Throwables.getRootCause(throwable);
if (root instanceof RuntimeException) {
return of((RuntimeException) root);
}
if (root instanceof CryptoFailedException) {
return SECURITY_ERROR;
}
return FAILED_APPLICATION;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -86,7 +86,25 @@
</RelativeLayout>
<org.webrtc.SurfaceViewRenderer
<LinearLayout
android:id="@+id/remote_video_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/app_bar_layout"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:background="@color/black"
android:gravity="center"
android:visibility="gone">
<eu.siacs.conversations.ui.widget.SurfaceViewRenderer
android:id="@+id/remote_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<eu.siacs.conversations.ui.widget.SurfaceViewRenderer
android:id="@+id/local_video"
android:layout_width="@dimen/local_video_preview_width"
android:layout_height="@dimen/local_video_preview_height"
@ -97,15 +115,6 @@
android:visibility="gone"
app:elevation="4dp" />
<org.webrtc.SurfaceViewRenderer
android:id="@+id/remote_video"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/app_bar_layout"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:visibility="gone" />
<ImageView
android:id="@+id/verified"
android:layout_width="wrap_content"

View File

@ -4,6 +4,7 @@
<string name="action_add">محادثة جديدة</string>
<string name="action_accounts">إدارة الحسابات</string>
<string name="action_account">إدارة الحساب</string>
<string name="action_end_conversation">أغلق المحادثة</string>
<string name="action_contact_details">بيانات جهة الإتصال</string>
<string name="action_muc_details">تفاصيل مجموعة المحادثة</string>
<string name="channel_details">تفاصيل القناة</string>
@ -27,7 +28,7 @@
<string name="title_activity_block_list">قائمة المحجوبين</string>
<string name="just_now">الآن</string>
<string name="minute_ago">منذ 1 دقيقة</string>
<string name="minutes_ago">دقائق %d منذ</string>
<string name="minutes_ago">منذ %d دقائق</string>
<string name="sending">ارسال</string>
<string name="message_decrypting">حل شيفرة الرسالة. الرجاء الإنتظار ...</string>
<string name="pgp_message">رسالة مشفرة عبر OpenPGP</string>
@ -38,12 +39,14 @@
<string name="moderator">مشرف</string>
<string name="participant">مشترك</string>
<string name="visitor">زائر</string>
<string name="remove_contact_text">هل تريد حذف %sمن قائمة إتصالك؟ المحادثات مع هذا الشخص لن تحذف.</string>
<string name="block_contact_text">هل ترغب في حجب %s من ارسال الرسائل لك?</string>
<string name="unblock_contact_text">هل ترغب في انهاء حجب %s والسماح له بمراسلتك?</string>
<string name="block_domain_text">هل تريد حجب جميع جهات الإتصال من %s?</string>
<string name="unblock_domain_text">الغاء حجب جميع جهات الإتصال من %s?</string>
<string name="contact_blocked">جهة الاتصال محجوبه</string>
<string name="blocked">محجوب</string>
<string name="remove_bookmark_text">هل تريد حذف %sمن قائمة المفضلة؟ المحادثات مع هذا المفضل لن تحذف.</string>
<string name="register_account">تسجيل حساب جديد في سيرفر</string>
<string name="change_password_on_server">تغيير كلمة المرور في سيرفر</string>
<string name="share_with">مشاركة مع</string>
@ -61,11 +64,16 @@
<string name="unblock">الغاء حجب</string>
<string name="save">حفظ</string>
<string name="ok">موافق</string>
<string name="crash_report_title">%1$sتعطّل</string>
<string name="send_now">ارسال الآن</string>
<string name="send_never">لا تسألني ثانية</string>
<string name="problem_connecting_to_account">لا يمكن الإتصال بالحساب</string>
<string name="problem_connecting_to_accounts">لايمكن الإتصال بحسابات متعددة</string>
<string name="attach_file">ارفاق ملف</string>
<string name="add_contact">اضافة جهة اتصال</string>
<string name="send_failed">فشل التسليم</string>
<string name="preparing_image">الإستعداد لإرسال الصورة</string>
<string name="preparing_images">الإستعداد لإرسال الصور</string>
<string name="sharing_files_please_wait">جاري إرسال الملفات. الرجاء الإنتظار ...</string>
<string name="action_clear_history">حذف سجل المحفوظات</string>
<string name="clear_conversation_history">حذف سجل المحفوظات للمحادثة</string>
@ -77,6 +85,7 @@
<string name="send_omemo_message">إرسال رسالة مشفرة عبر OMEMO</string>
<string name="send_omemo_x509_message">إبعث رسالة مشفَّرة بـ أومي مو OMEMO</string>
<string name="send_pgp_message">إرسال رسالة مشفرة عبر OpenPGP</string>
<string name="your_nick_has_been_changed">إسم مستخدم جديد تحت الإستعمال</string>
<string name="send_unencrypted">إرسال بدون تشفير</string>
<string name="decryption_failed">فشل فك التشفير. ربما ليس لديك المفتاح الخاص الصحيح.</string>
<string name="openkeychain_required">OpenKeychain</string>
@ -96,6 +105,7 @@
<string name="pref_vibrate_summary">إهتز عند وصول رسالة جديدة</string>
<string name="pref_led">إشعار ضوئي</string>
<string name="pref_ringtone">التنبيه الصوتي</string>
<string name="pref_notification_sound">تنبيه صوتي</string>
<string name="pref_notification_grace_period">فترة السماح</string>
<string name="pref_advanced_options">متقدم</string>
<string name="pref_never_send_crash">لا ترسل تقارير أخطاء</string>
@ -112,6 +122,7 @@
<string name="attach_choose_picture">اختيار صورة</string>
<string name="attach_take_picture">التقاط صورة</string>
<string name="error_not_an_image_file">الملف الذي حددته ليس صورة</string>
<string name="error_compressing_image">لا يمكن تحويل ملف الصورة</string>
<string name="error_file_not_found">الملف غير موجود</string>
<string name="account_status_unknown">غير معروف</string>
<string name="account_status_disabled">معطلٌ موقتاً</string>
@ -145,6 +156,7 @@
<string name="block_jabber_id">احجب عنوان XMPP</string>
<string name="account_settings_example_jabber_id">username@example.com</string>
<string name="password">كلمة السر</string>
<string name="error_out_of_memory">الذاكرة مليئة، صورة كبيرة جدا</string>
<string name="add_phone_book_text">هل تود إضافة %s إلى سجل عناوينك ؟</string>
<string name="server_info_show_more">معلومات عن المضيف</string>
<string name="server_info_mam">XEP-0313: إدارة أرشيف الرسائل</string>
@ -159,6 +171,7 @@
<string name="server_info_available">متاح</string>
<string name="server_info_unavailable">غير متاح</string>
<string name="last_seen_now">آخر ظهور الآن</string>
<string name="last_seen_min">آخر مشاهدة منذ دقيقة</string>
<string name="last_seen_mins">آخر ظهور منذ %d دقيقة</string>
<string name="last_seen_hours">آخر ظهور منذ %d ساعة</string>
<string name="last_seen_days">آخر ظهور منذ %d يوم</string>
@ -642,4 +655,5 @@
<string name="open_backup">افتح النسخة الاحتياطية</string>
<string name="please_enter_password">يرجى إدخال الكلمة السرية للحساب</string>
<string name="rtp_state_declined_or_busy">مشغول</string>
<string name="more_options">خيارات أخرى</string>
</resources>

View File

@ -46,6 +46,9 @@
<string name="moderator">নির্ধারক</string>
<string name="participant">অংশগ্রহণকারী</string>
<string name="visitor">অতিথি</string>
<string name="remove_contact_text">আপনি কি আপনার পরিচিতি তালিকা থেকে %s-কে অপসারণ করতে চান? এই যোগাযোগের সাথে কথোপকথনগুলি সরানো হবে না।</string>
<string name="block_contact_text">%s-কে বার্তা পাঠানো থেকে ব্লক করতে চান?</string>
<string name="unblock_contact_text">আপনি কি %s-কে আনব্লক করতে চান এবং তাদের আপনাকে বার্তা পাঠানোর অনুমতি দিতে চান?</string>
<string name="contact_blocked">ব্যক্তিটিকে ব্লক্ করা হয়েছে</string>
<string name="blocked">ব্লক্ করা আছে</string>
<string name="register_account">সার্ভারে একটি নতুন অ্যকাউন্ট খোলা যাক</string>
@ -90,8 +93,26 @@
<string name="send_omemo_message">OMEMO সাঙ্কেতিক বার্তা পাঠানো হোক</string>
<string name="send_omemo_x509_message">v\\OMEMO সাঙ্কেতিক বার্তা পাঠানো হোক</string>
<string name="send_pgp_message">OpenPGP সাঙ্কেতিক বার্তা পাঠানো হোক</string>
<string name="your_nick_has_been_changed">নতুন নাম ব্যবহার করা হচ্ছে</string>
<string name="send_unencrypted">এনক্রিপ্ট না করেই পাঠানো হোক</string>
<string name="decryption_failed">ডিক্রিপ্ট করা যায়নি। হয়তো আপনার কাছে সঠিক Private Key নেই।</string>
<string name="restart">রিস্টার্ট্</string>
<string name="install">ইনস্টল্</string>
<string name="openkeychain_not_installed">OpenKeychain ইনস্টল্ করতে হবে</string>
<string name="offering">প্রস্তাব দেওয়া হচ্ছে...</string>
<string name="waiting">অপেক্ষা করা হচ্ছে...</string>
<string name="no_pgp_key">কোনো OpenPGP Key খুঁজে পাওয়া যায়নি</string>
<string name="bookmarks">বুকমার্ক করা যেগুলি</string>
<string name="search">খোঁজা যাক</string>
<string name="block_contact">এই ব্যক্তিকে ব্লক্ করা যাক</string>
<string name="unblock_contact">ব্লকটা সরিয়ে ফেলা যাক</string>
<string name="vcard">পরিচিত ব্যক্তি</string>
<string name="dialog_manage_certs_negativebutton">না, থাক।</string>
<string name="search_contacts">পরিচিত ব্যক্তিদের মধ্যে খোঁজা যাক</string>
<string name="search_messages">বার্তাগুলির মধ্যে খোঁজা যাক</string>
<string name="pref_start_search">সরাসরিভাবেই খোঁজা যাক</string>
<string name="join_public_channel">পাবলিক চ্যানেলে যোগ দেওয়া যাক</string>
<string name="create_private_group_chat">ব্যক্তিগত গ্রুপ চ্যাট তৈরি করুন</string>
<string name="create_public_channel">পাবলিক চ্যানেল তৈরি করা যাক</string>
<string name="discover_channels">বর্তমান চ্যানেলগুলির মধ্যে থেকে খোঁজা যাক</string>
</resources>

View File

@ -156,6 +156,7 @@
<string name="error_file_not_found">Soubor nenalezen</string>
<string name="error_io_exception">Obecná I/O chyba. Že by již nebylo volné místo?</string>
<string name="error_security_exception_during_image_copy">Aplikace, kterou jste použil(a) k výběru obrázku, neposkytla dostatečná oprávnění ke čtení souboru.\n\n<small>Použijte jiného správce souborů k výběru obrázku</small>.</string>
<string name="error_security_exception">Aplikace kterou jste použili pro nasdílení tohoto souboru nemá dostatečná oprávnění.</string>
<string name="account_status_unknown">Neznámý</string>
<string name="account_status_disabled">Dočasně vypnuto</string>
<string name="account_status_online">Online</string>
@ -170,6 +171,7 @@
<string name="account_status_regis_not_sup">Registrace není podporována serverem</string>
<string name="account_status_regis_invalid_token">Chybný registrační token</string>
<string name="account_status_tls_error">Vyjednávání TLS selhalo</string>
<string name="account_status_tls_error_domain">Doménu nelze ověřit</string>
<string name="account_status_policy_violation">Porušení podmínek</string>
<string name="account_status_incompatible_server">Nekompatibilní server</string>
<string name="account_status_stream_error">Chyba přenosu</string>
@ -406,6 +408,7 @@
<string name="could_not_modify_conference_options">Nebylo možné změnit nastavení skupinového chatu</string>
<string name="never">Nikdy</string>
<string name="until_further_notice">Než opět změním</string>
<string name="snooze">Posunout</string>
<string name="reply">Odpovědět</string>
<string name="mark_as_read">Označit jako přečtené</string>
<string name="pref_input_options">Vstup</string>
@ -434,6 +437,8 @@
<string name="no_application_found_to_display_location">Nebyla nalezena aplikace pro zobrazení pozice</string>
<string name="location">Pozice</string>
<string name="title_undo_swipe_out_conversation">Conversation zavřena</string>
<string name="title_undo_swipe_out_group_chat">Opustil(a) soukromý skupinový chat</string>
<string name="title_undo_swipe_out_channel">Opustil(a) veřejný kanál</string>
<string name="pref_dont_trust_system_cas_title">Nedůvěřovat systémovým CA</string>
<string name="pref_dont_trust_system_cas_summary">Všechny certifikáty musí být schváleny ručně</string>
<string name="pref_remove_trusted_certificates_title">Odstranit certifikáty</string>
@ -466,6 +471,7 @@
<string name="download_failed_could_not_write_file">Stažení selhalo: Nelze zapsat soubor</string>
<string name="account_status_tor_unavailable">Tor síť není dostupná</string>
<string name="account_status_bind_failure">Bind chyba</string>
<string name="account_status_host_unknown">Server není zodpovědný za tuto doménu</string>
<string name="server_info_broken">Rozbité</string>
<string name="pref_presence_settings">Dostupnost</string>
<string name="pref_away_when_screen_off">Pryč při uzamčení zařízení</string>
@ -495,6 +501,7 @@
<string name="pref_use_tor_summary">Vedení všech připojení po Tor síti vyžaduje aplikaci Orbot</string>
<string name="account_settings_hostname">Hostname</string>
<string name="account_settings_port">Port</string>
<string name="hostname_or_onion">Server nebo .onion adresa</string>
<string name="not_a_valid_port">Toto není platné číslo portu</string>
<string name="not_valid_hostname">Toto není platné hostname</string>
<string name="connected_accounts">%1$d z %2$d účtů připojeno</string>
@ -533,6 +540,7 @@
<string name="send_corrected_message">Odeslat opravenou zprávu</string>
<string name="no_keys_just_confirm">Tento osobní otisk byl již bezpečně ověřen. Ťuknutím na \"Hotovo\" pouze potvrzujete, že %s je členem tohoto skupinového chatu.</string>
<string name="this_account_is_disabled">Tento účet byl vypnut</string>
<string name="security_error_invalid_file_access">Bezpečnostní chyba: Neplatný přístup k souboru</string>
<string name="no_application_to_share_uri">Nebyla nalezena aplikace umožňující sdílení URI</string>
<string name="share_uri_with">Sdílet URI s…</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy je aplikace odvozená z populárního XMPP klientu Conversations, s funkcí automatického objevování kontaktů.<br><br>Po zadání Vašeho telefonního čísla Vám Quicksy automaticky—na základě čísel ve Vašem telefonním seznamu—navrhne možné kontakty.<br><br>Přihlášením se do služby potvrzujete souhlas s našimi <a href="https://quicksy.im/#privacy">zásadami pro ochranu osobních údajů</a>.]]></string>
@ -744,6 +752,7 @@
<string name="pref_use_share_location_plugin_summary">Použít Plugin pro sdílení pozice namísto interní mapy</string>
<string name="copy_link">Kopírovat webovou adresu</string>
<string name="copy_jabber_id">Kopírovat XMPP adresu</string>
<string name="p1_s3_filetransfer">HTTP sdílení souborů pro S3</string>
<string name="pref_start_search">Přímé vyhledávání</string>
<string name="pref_start_search_summary">Na úvodní obrazovce otevřít klávesnici a umístit kurzor do vyhledávacího pole</string>
<string name="group_chat_avatar">Avatar skupinového chatu</string>
@ -883,6 +892,7 @@
<string name="this_looks_like_channel">Toto vypadá jako adresa kanálu</string>
<string name="share_backup_files">Sdílet soubory zálohy</string>
<string name="conversations_backup">Záloha Conversations</string>
<string name="event">Událost</string>
<string name="open_backup">Otevřít zálohu</string>
<string name="not_a_backup_file">Soubor, který jste zvolili, není soubor zálohy Conversations</string>
<string name="account_already_setup">Tento účet byl již nastaven</string>
@ -932,6 +942,7 @@
<string name="could_not_switch_camera">Nebylo možné přepnout kameru</string>
<string name="add_to_favorites">Připnout nahoru</string>
<string name="remove_from_favorites">Odepnout shora</string>
<string name="gpx_track">GPX trasa</string>
<string name="could_not_correct_message">Nebylo možné opravit zprávu</string>
<string name="search_all_conversations">Všechny konverzace</string>
<string name="search_this_conversation">Tato konverzace</string>
@ -961,7 +972,9 @@
<string name="more_options">Více možností</string>
<string name="no_application_found">Nenalezena žádná aplikace</string>
<string name="invite_to_app">Pozvat do Conversations</string>
<string name="unable_to_parse_invite">Nelze načíst pozvánku</string>
<string name="server_does_not_support_easy_onboarding_invites">Server nepodporuje vytváření pozvánek</string>
<string name="no_active_accounts_support_this">Žádný z aktivních účtů tuto funkci nepodporuje</string>
<string name="backup_started_message">Zálohování zahájeno. Budete upozorněni, jakmile bude záloha hotova.</string>
<string name="unable_to_enable_video">Nelze povolit video.</string>
</resources>

View File

@ -150,6 +150,7 @@
<string name="error_file_not_found">Fil ikke fundet</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">Appen du brugte til at dele denne fil har ikke givet nok tilladelser.</string>
<string name="account_status_unknown">Ukendt</string>
<string name="account_status_disabled">Midlertidigt deaktiveret</string>
<string name="account_status_online">Online</string>
@ -164,6 +165,7 @@
<string name="account_status_regis_not_sup">Registrering er ikke understøttet af server</string>
<string name="account_status_regis_invalid_token">Ugyldig registreringstoken</string>
<string name="account_status_tls_error">TLS forhandling mislykkedes</string>
<string name="account_status_tls_error_domain">Domæne kan ikke verificeres</string>
<string name="account_status_policy_violation">Brud på retningslinjer</string>
<string name="account_status_incompatible_server">Inkompatibel server</string>
<string name="account_status_stream_error">Strømfejl</string>
@ -960,4 +962,5 @@
<string name="server_does_not_support_easy_onboarding_invites">Server understøtter ikke generering af invitationer</string>
<string name="no_active_accounts_support_this">Ingen aktive konti understøtter denne funktion</string>
<string name="backup_started_message">Sikkerhedskopieringen er startet. Du får en notifikation, når den er afsluttet.</string>
<string name="unable_to_enable_video">Kunne ikke aktivere video.</string>
</resources>

View File

@ -76,7 +76,7 @@
<string name="send_now">Jetzt abschicken</string>
<string name="send_never">Nie mehr nachfragen</string>
<string name="problem_connecting_to_account">Verbindung zum Konto konnte nicht hergestellt werden</string>
<string name="problem_connecting_to_accounts">Verbindung zu mehreren Konto konnte nicht hergestellt werden</string>
<string name="problem_connecting_to_accounts">Verbindung zu mehreren Konten konnte nicht hergestellt werden</string>
<string name="touch_to_fix">Antippen, um deine Konten zu verwalten</string>
<string name="attach_file">Datei auswählen</string>
<string name="not_in_roster">Diesen fehlenden Kontakt zu deiner Kontaktliste hinzufügen?</string>
@ -94,7 +94,7 @@
<string name="choose_presence">Gerät auswählen</string>
<string name="send_unencrypted_message">Unverschlüsselt schreiben…</string>
<string name="send_message">Nachricht senden</string>
<string name="send_message_to_x">Sende Nachricht an %s</string>
<string name="send_message_to_x">Nachricht an %s senden</string>
<string name="send_omemo_message">OMEMO-verschlüsselt schreiben…</string>
<string name="send_omemo_x509_message">v\\OMEMO-verschlüsselte Nachricht senden</string>
<string name="send_pgp_message">OpenPGP-verschlüsselt schreiben…</string>
@ -112,7 +112,7 @@
<string name="contact_has_no_pgp_key">Deine Nachricht konnte nicht verschlüsselt werden, weil dein Kontakt seinen öffentlichen Schlüssel nicht bekannt gibt.\n\n<small>Bitte sage deinem Kontakt, er möge OpenPGP einrichten.</small></string>
<string name="no_pgp_keys">Keine OpenPGP-Schlüssel gefunden</string>
<string name="contacts_have_no_pgp_keys">Deine Nachrichten konnten nicht verschlüsselt werden, weil deine Kontakte ihre öffentlichen Schlüssel nicht bekannt geben.\n\n<small>Bitte sage ihnen, sie mögen OpenPGP einrichten.</small></string>
<string name="pref_general">Allgemeines</string>
<string name="pref_general">Allgemein</string>
<string name="pref_accept_files">Dateien annehmen</string>
<string name="pref_accept_files_summary">Dateien automatisch annehmen, die kleiner sind als …</string>
<string name="pref_attachments">Anhänge</string>
@ -132,6 +132,8 @@
<string name="pref_never_send_crash_summary">Mit dem Einsenden von Absturzberichten hilfst du bei der Weiterentwicklung</string>
<string name="pref_confirm_messages">Lese- und Empfangsbestätigung senden</string>
<string name="pref_confirm_messages_summary">Informiere deine Kontakte, wenn du eine Nachricht empfangen und gelesen hast</string>
<string name="pref_prevent_screenshots">Screenshots verhindern</string>
<string name="pref_prevent_screenshots_summary">Ausblenden von App-Inhalten im App-Switcher und Blockieren von Screenshots</string>
<string name="pref_ui_options">Benutzeroberfläche</string>
<string name="openpgp_error">OpenKeychain verursachte einen Fehler.</string>
<string name="bad_key_for_encryption">Fehlerhafter Schlüssel für die Verschlüsselung.</string>
@ -149,7 +151,7 @@
<string name="error_compressing_image">Bilddatei konnte nicht konvertiert werden</string>
<string name="error_file_not_found">Datei nicht gefunden</string>
<string name="error_io_exception">Allgemeiner Fehler. Vielleicht hast du keinen Speicherplatz mehr?</string>
<string name="error_security_exception_during_image_copy">Die App, mit der du das Bild ausgesucht hast, hat keine Rechte eingeräumt, um das Bild zu betrachten.\n\n<small>Benutze einen anderen Dateimanager, um ein Bild auszuwählen</small>.</string>
<string name="error_security_exception_during_image_copy">Die App, mit der du das Bild ausgesucht hast, hat nicht die erforderlichen Berechtigungen, um das Bild zu betrachten.\n\n<small>Benutze einen anderen Dateimanager, um ein Bild auszuwählen</small>.</string>
<string name="error_security_exception">Die App, die du zum Teilen dieser Datei verwendet hast, hat nicht die erforderlichen Berechtigungen bereitgestellt.</string>
<string name="account_status_unknown">Unbekannt</string>
<string name="account_status_disabled">Vorübergehend abgeschaltet</string>
@ -168,7 +170,7 @@
<string name="account_status_tls_error_domain">Domain nicht überprüfbar</string>
<string name="account_status_policy_violation">Verstoß gegen die Richtlinien</string>
<string name="account_status_incompatible_server">Inkompatibler Server</string>
<string name="account_status_stream_error">Stream Fehler</string>
<string name="account_status_stream_error">Stream-Fehler</string>
<string name="account_status_stream_opening_error">Fehler beim Öffnen des Streams</string>
<string name="encryption_choice_unencrypted">Unverschlüsselt</string>
<string name="encryption_choice_otr">OTR</string>
@ -180,7 +182,7 @@
<string name="mgmt_account_publish_pgp">Öffentlichen OpenPGP-Schlüssel veröffentlichen</string>
<string name="unpublish_pgp">Öffentlichen OpenPGP-Schlüssel verwerfen</string>
<string name="unpublish_pgp_message">Bist du sicher, dass du deinen öffentlichen OpenPGP-Schlüssel aus deiner Anwesenheitsmitteilung entfernen möchtest?\nDeine Kontakte können dir dann keine OpenPGP-verschlüsselten Nachrichten senden.</string>
<string name="openpgp_has_been_published">Öffentlicher OpenPGP-Schlüssel veröffentlicht</string>
<string name="openpgp_has_been_published">Öffentlicher OpenPGP-Schlüssel veröffentlicht.</string>
<string name="mgmt_account_enable">Konto aktivieren </string>
<string name="mgmt_account_are_you_sure">Bist du dir sicher?</string>
<string name="mgmt_account_delete_confirm_text">Die Löschung deines Kontos löscht deinen gesamten Gesprächsverlauf</string>
@ -250,14 +252,14 @@
<string name="topic">Thema</string>
<string name="joining_conference">Gruppenchat wird beigetreten…</string>
<string name="leave">Verlassen</string>
<string name="contact_added_you">Der Kontakt hat dich zur Kontaktliste hinzugefügt</string>
<string name="contact_added_you">Kontakt hat dich zur Kontaktliste hinzugefügt</string>
<string name="add_back">Auch hinzufügen</string>
<string name="contact_has_read_up_to_this_point">%s hat bis zu diesem Punkt gelesen</string>
<string name="contacts_have_read_up_to_this_point">%s haben bis zu diesem Punkt gelesen</string>
<string name="contacts_and_n_more_have_read_up_to_this_point">%1$s +%2$d andere haben bis zu diesem Punkt gelesen</string>
<string name="everyone_has_read_up_to_this_point">Alle haben bis zu diesem Punkt gelesen</string>
<string name="publish">Veröffentlichen</string>
<string name="touch_to_choose_picture">Avatar antippen, um ein Bild aus Galerie auszuwählen</string>
<string name="touch_to_choose_picture">Avatar antippen, um ein Bild aus der Galerie auszuwählen</string>
<string name="publishing">Veröffentliche…</string>
<string name="error_publish_avatar_server_reject">Der Server hat die Veröffentlichung des Avatars abgelehnt.</string>
<string name="error_publish_avatar_converting">Bild konnte nicht konvertiert werden</string>
@ -304,7 +306,7 @@
<string name="using_account">verwende Konto %s</string>
<string name="hosted_on">gehostet bei %s</string>
<string name="checking_x">%s auf HTTP-Host wird überprüft</string>
<string name="not_connected_try_again">Nicht verbunden, bitte später versuchen</string>
<string name="not_connected_try_again">Du bist nicht verbunden. Bitte versuche es später noch einmal</string>
<string name="check_x_filesize">%s-Größe prüfen</string>
<string name="check_x_filesize_on_host">%1$s-Größe auf %2$s prüfen</string>
<string name="message_options">Nachrichtenoptionen</string>
@ -371,7 +373,7 @@
<string name="current_password">Aktuelles Passwort</string>
<string name="new_password">Neues Passwort</string>
<string name="password_should_not_be_empty">Passwort kann nicht leer sein</string>
<string name="enable_all_accounts">Alle Konten aktiveren</string>
<string name="enable_all_accounts">Alle Konten aktivieren</string>
<string name="disable_all_accounts">Alle Konten abschalten</string>
<string name="perform_action_with">Aktion durchführen mit</string>
<string name="no_affiliation">Keine Zugehörigkeit</string>
@ -414,6 +416,7 @@
<string name="audio">Audio</string>
<string name="video">Video</string>
<string name="image">Bild</string>
<string name="vector_graphic">Vektorgrafik</string>
<string name="pdf_document">PDF-Dokument</string>
<string name="apk">Android App</string>
<string name="vcard">Kontakt</string>
@ -789,7 +792,7 @@
<string name="verify_x">Überprüfe %s</string>
<string name="we_have_sent_you_an_sms_to_x"><![CDATA[Wir haben dir eine SMS an <b>%s</b> geschickt.]]></string>
<string name="we_have_sent_you_another_sms">Wir haben dir eine weitere SMS mit einem 6-stelligen Code geschickt.</string>
<string name="please_enter_pin_below">Gib bitte den 6-stellige PIN unten ein.</string>
<string name="please_enter_pin_below">Gib bitte die 6-stellige PIN unten ein.</string>
<string name="resend_sms">SMS erneut versenden</string>
<string name="resend_sms_in">SMS erneut versenden (%s)</string>
<string name="wait_x">Bitte warten (%s)</string>
@ -912,6 +915,7 @@
<string name="rtp_state_connectivity_lost_error">Verbindung unterbrochen</string>
<string name="rtp_state_retracted">Anruf zurückgenommen</string>
<string name="rtp_state_application_failure">App-Fehler</string>
<string name="rtp_state_security_error">Verifikationsproblem</string>
<string name="hang_up">Auflegen</string>
<string name="ongoing_call">Laufender Anruf</string>
<string name="ongoing_video_call">Laufender Videoanruf</string>
@ -963,4 +967,6 @@
<string name="no_active_accounts_support_this">Keine aktiven Konten unterstützen diese Funktion</string>
<string name="backup_started_message">Das Backup wurde gestartet. Du bekommst eine Benachrichtigung sobald es fertig ist.</string>
<string name="unable_to_enable_video">Video kann nicht aktiviert werden.</string>
<string name="plain_text_document">Textdokument</string>
</resources>

View File

@ -53,7 +53,7 @@
<string name="unblock_domain_text">Άρση αποκλεισμού όλων των επαφών από το %s;</string>
<string name="contact_blocked">Η επαφή αποκλείστηκε</string>
<string name="blocked">Αποκλεισμένος</string>
<string name="remove_bookmark_text">Θέλετε να αφαιρέσετε το %s ως σελιδοδείκτη; Οι συζητήσεις που σχετίζονται με αυτόν τον σελιδοδείκτη δεν θα αφαιρεθούν.</string>
<string name="remove_bookmark_text">Θέλετε να αφαιρέσετε το %s από σελιδοδείκτη; Οι συζητήσεις που σχετίζονται με αυτόν τον σελιδοδείκτη δεν θα αφαιρεθούν.</string>
<string name="register_account">Εγγραφή νέου λογαριασμού στον διακομιστή</string>
<string name="change_password_on_server">Αλλαγή συνθηματικού στον διακομιστή</string>
<string name="share_with">Διαμοιρασμός με...</string>
@ -89,7 +89,7 @@
<string name="clear_conversation_history">Καθαρισμός ιστορικού Συζήτησης</string>
<string name="clear_histor_msg">Θέλετε να διαγράψετε όλα τα μηνύματα αυτής της συζήτησης;\n\n<b>Προσοχή:</b> Αυτή η ενέργεια δεν θα επηρεάσει μηνύματα που είναι αποθηκευμένα σε άλλες συσκευές ή εξυπηρετητές.</string>
<string name="delete_file_dialog">Διαγραφή αρχείου</string>
<string name="delete_file_dialog_msg">Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το αρχείο;\n\n<b>Προσοχή</b> Αυτή η ενέργεια δεν θα διαγράψει αντίγραφα αυτού του αρχείου που είναι αποθηκευμένα σε άλλες συσκευές ή εξυπηρετητές.</string>
<string name="delete_file_dialog_msg">Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το αρχείο;\n\n<b>Προσοχή:</b> Αυτή η ενέργεια δεν θα διαγράψει αντίγραφα αυτού του αρχείου που είναι αποθηκευμένα σε άλλες συσκευές ή εξυπηρετητές.</string>
<string name="also_end_conversation">Κλείσιμο της συζήτησης αμέσως μετά</string>
<string name="choose_presence">Επιλογή συσκευής</string>
<string name="send_unencrypted_message">Αποστολή μη κρυπτογραφημένου μηνύματος</string>
@ -114,7 +114,7 @@
<string name="contacts_have_no_pgp_keys">Δεν ήταν δυνατή η κρυπτογράφηση του μηνύματός σας γιατί οι επαφές σας δεν ανακοινώνουν το δημόσιο κλειδί τους.\n\n<small>Παρακαλώ ζητήστε από τις επαφές σας να εγκαταστήσουν το OpenPGP.</small></string>
<string name="pref_general">Γενικά</string>
<string name="pref_accept_files">Αποδοχή αρχείων</string>
<string name="pref_accept_files_summary">Αυτόματη αποδοχή αρχείων μικρότερα από...</string>
<string name="pref_accept_files_summary">Αυτόματη αποδοχή αρχείων μικρότερων από...</string>
<string name="pref_attachments">Συνημμένα</string>
<string name="pref_notification_settings">Ειδοποίηση</string>
<string name="pref_vibrate">Δόνηση</string>
@ -124,6 +124,7 @@
<string name="pref_ringtone">Κουδούνισμα</string>
<string name="pref_notification_sound">Ήχος ειδοποίησης</string>
<string name="pref_notification_sound_summary">Ήχος ειδοποίησης για νέα μηνύματα</string>
<string name="pref_call_ringtone_summary">Ήχος κουδουνίσματος για εισερχόμενες κλήσεις</string>
<string name="pref_notification_grace_period">Περίοδος Χάριτος</string>
<string name="pref_notification_grace_period_summary">Ο χρόνος σίγασης ειδοποιήσεων αφότου ανιχνευθεί δραστηριότητα σε μια από τις άλλες συσκευές σας.</string>
<string name="pref_advanced_options">Για προχωρημένους</string>
@ -133,7 +134,7 @@
<string name="pref_confirm_messages_summary">Επιτρέψτε στις επαφές σας να γνωρίζουν όταν έχετε λάβει και διαβάσει τα μηνύματά τους</string>
<string name="pref_ui_options">Διεπαφή χρήστη</string>
<string name="openpgp_error">Το OpenKeychain ανέφερε κάποιο σφάλμα.</string>
<string name="bad_key_for_encryption">Σφάλμα στο κλειδί κρυπτογράφησης</string>
<string name="bad_key_for_encryption">Σφάλμα στο κλειδί κρυπτογράφησης.</string>
<string name="accept">Αποδοχή</string>
<string name="error">Έχει συμβεί κάποιο σφάλμα</string>
<string name="recording_error">Σφάλμα</string>
@ -149,6 +150,7 @@
<string name="error_file_not_found">Το αρχείο δεν βρέθηκε</string>
<string name="error_io_exception">Γενικό σφάλμα εισόδου/εξόδου. Ίσως δεν έχετε ελεύθερο χώρο αποθήκευσης;</string>
<string name="error_security_exception_during_image_copy">Η εφαρμογή που χρησιμοποιήσατε για να επιλέξετε αυτή την εικόνα δεν παραχώρησε αρκετά δικαιώματα για την ανάγνωση του αρχείου.\n\n<small>Χρησιμοποιήστε διαφορετικό διαχειριστή αρχείων για να επιλέξετε μια εικόνα</small></string>
<string name="error_security_exception">Η εφαρμογή που χρησιμοποιήσατε για να διαμοιραστείτε αυτό το αρχείο δεν παρείχε αρκετά δικαιώματα.</string>
<string name="account_status_unknown">Άγνωστο</string>
<string name="account_status_disabled">Προσωρινά απενεργοποιημένο</string>
<string name="account_status_online">Σε σύνδεση</string>
@ -163,6 +165,7 @@
<string name="account_status_regis_not_sup">Ο διακομιστής δεν υποστηρίζει εγγραφή</string>
<string name="account_status_regis_invalid_token">Άκυρο κουπόνι εγγραφής</string>
<string name="account_status_tls_error">Αποτυχία διαπραγμάτευσης TLS</string>
<string name="account_status_tls_error_domain">Ο τομέας δεν είναι επαληθεύσιμος</string>
<string name="account_status_policy_violation">Παραβίαση κανονισμού</string>
<string name="account_status_incompatible_server">Μη συμβατός διακομιστής</string>
<string name="account_status_stream_error">Σφάλμα μετάδοσης</string>
@ -204,11 +207,11 @@
<string name="server_info_unavailable">μη διαθέσιμος</string>
<string name="missing_public_keys">Ελλειπείς ανακοινώσεις δημοσίων κλειδιών</string>
<string name="last_seen_now">συνδέθηκε τελευταία φορά μόλις τώρα</string>
<string name="last_seen_min">τελευταία σύνδεση πριν από 1 λεπτό</string>
<string name="last_seen_min">τελευταία σύνδεση πριν από ένα λεπτό</string>
<string name="last_seen_mins">τελευταία σύνδεση πριν από %d λεπτά</string>
<string name="last_seen_hour">τελευταία σύνδεση πριν από 1 ώρα</string>
<string name="last_seen_hour">τελευταία σύνδεση πριν από μία ώρα</string>
<string name="last_seen_hours">τελευταία σύνδεση πριν από %d ώρες</string>
<string name="last_seen_day">τελευταία σύνδεση πριν από 1 μέρα</string>
<string name="last_seen_day">τελευταία σύνδεση πριν από μία μέρα</string>
<string name="last_seen_days">τελευταία σύνδεση πριν από %d μέρες</string>
<string name="install_openkeychain">Κρυπτογραφημένο μήνυμα. Παρακαλώ εγκαταστήστε το OpenKeychain για αποκρυπτογράφηση.</string>
<string name="openpgp_messages_found">Βρέθηκαν νέα μηνύματα κρυπτογραφημένα με OpenPGP</string>
@ -217,7 +220,7 @@
<string name="omemo_fingerprint_x509">v\\Αποτύπωμα OMEMO</string>
<string name="omemo_fingerprint_selected_message">Αποτύπωμα OMEMO (πηγή μηνύματος)</string>
<string name="omemo_fingerprint_x509_selected_message">v\\Αποτύπωμα OMEMO (πηγή μηνύματος)</string>
<string name="other_devices">\'Αλλες συσκευές</string>
<string name="other_devices">Άλλες συσκευές</string>
<string name="trust_omemo_fingerprints">Επαλήθευση των αποτυπωμάτων OMEMO</string>
<string name="fetching_keys">Μεταφόρτωση κλειδιών...</string>
<string name="done">Έγινε</string>
@ -327,7 +330,7 @@
<string name="notification_create_backup_title">Δημιουργία αντιγράφων ασφαλείας</string>
<string name="notification_backup_created_title">Το αντίγραφο ασφαλείας σας έχει δημιουργηθεί</string>
<string name="notification_backup_created_subtitle">Τα αρχεία του αντιγράφου ασφαλείας έχουν αποθηκευτεί στο %s</string>
<string name="restoring_backup">Επαναφορά αντιγράφου ασφαλείας</string>
<string name="restoring_backup">Γίνεται επαναφορά αντιγράφου ασφαλείας</string>
<string name="notification_restored_backup_title">Έχει γίνει επαναφορά του αντιγράφου ασφαλείας σας</string>
<string name="notification_restored_backup_subtitle">Μην παραλείψετε να ενεργοποιήσετε τον λογαριασμό.</string>
<string name="choose_file">Επιλογή αρχείου</string>
@ -338,7 +341,7 @@
<string name="open_x_file">Άνοιγμα του %s</string>
<string name="sending_file">αποστολή (ολοκλήρωση %1$d%%)</string>
<string name="preparing_file">Προετοιμασία του αρχείου για διαμοιρασμό</string>
<string name="x_file_offered_for_download">%s προσφέρθηκε για μεταφόρτωση</string>
<string name="x_file_offered_for_download">Το %s προσφέρθηκε για μεταφόρτωση</string>
<string name="cancel_transmission">Ακύρωση μετάδοσης</string>
<string name="file_transmission_failed">ο διαμοιρασμός του αρχείου απέτυχε</string>
<string name="file_transmission_cancelled">η μεταφορά αρχείου ακυρώθηκε</string>
@ -418,10 +421,10 @@
<string name="sending_x_file">Αποστολή του %s</string>
<string name="offering_x_file">Προσφορά του %s</string>
<string name="hide_offline">Απόκρυψη των εκτός σύνδεσης</string>
<string name="contact_is_typing">Η επαφή %s πληκτρολογεί...</string>
<string name="contact_has_stopped_typing">Η επαφή %s σταμάτησε να πληκτρολογεί</string>
<string name="contacts_are_typing">Οι επαφές %s πληκτρολογούν...</string>
<string name="contacts_have_stopped_typing">Η επαφές %s σταμάτησαν να πληκτρολογούν</string>
<string name="contact_is_typing">Ο/Η %s πληκτρολογεί...</string>
<string name="contact_has_stopped_typing">Ο/Η %s σταμάτησε να πληκτρολογεί</string>
<string name="contacts_are_typing">Οι %s πληκτρολογούν...</string>
<string name="contacts_have_stopped_typing">Οι %s σταμάτησαν να πληκτρολογούν</string>
<string name="pref_chat_states">Ειδοποιήσεις πληκτρολόγησης</string>
<string name="pref_chat_states_summary">Επιτρέψτε στις επαφές σας να γνωρίζουν πότε γράφετε μηνύματα προς αυτές</string>
<string name="send_location">Αποστολή τοποθεσίας</string>
@ -451,7 +454,7 @@
<string name="search_contacts">Αναζήτηση επαφών</string>
<string name="search_bookmarks">Αναζήτηση σελιδοδεικτών</string>
<string name="send_private_message">Αποστολή ιδιωτικού μηνύματος</string>
<string name="user_has_left_conference">Η επαφή %1$s αποχώρησε από την ομαδική συζήτηση</string>
<string name="user_has_left_conference">Ο/Η %1$s αποχώρησε από την ομαδική συζήτηση</string>
<string name="username">Όνομα χρήστη</string>
<string name="username_hint">Όνομα χρήστη</string>
<string name="invalid_username">Αυτό δεν είναι έγκυρο όνομα χρήστη</string>
@ -464,6 +467,8 @@
<string name="account_status_host_unknown">Ο διακομιστής δεν είναι υπεύθυνος για αυτόν τον τομέα</string>
<string name="server_info_broken">Χαλασμένος</string>
<string name="pref_presence_settings">Διαθεσιμότητα</string>
<string name="pref_away_when_screen_off">Εκτός χρήσης όταν η οθόνη είναι κλειδωμένη</string>
<string name="pref_away_when_screen_off_summary">Εμφάνιση παρουσίας ως εκτός χρήσης όταν η συσκευή κλειδώνεται</string>
<string name="pref_dnd_on_silent_mode">Απασχολημένος/η όταν βρίσκεται σε σιωπηρή λειτουργία</string>
<string name="pref_dnd_on_silent_mode_summary">Σημειώνει την παρουσία σας ως Απασχολημένος/η όταν η συσκευή είναι σε κατάσταση σιωπής</string>
<string name="pref_treat_vibrate_as_silent">Χρήση της κατάστασης δόνησης ως σιωπηρή κατάσταση</string>
@ -501,7 +506,7 @@
<string name="load_more_messages">Φόρτωση περισσότερων μηνυμάτων</string>
<string name="shared_file_with_x">Το αρχείο διαμοιράστηκε με την επαφή %s</string>
<string name="shared_image_with_x">Η εικόνα διαμοιράστηκε με την επαφή %s</string>
<string name="shared_images_with_x">Η εικόνες διαμοιράστηκαν με την επαφή %s</string>
<string name="shared_images_with_x">Οι εικόνες διαμοιράστηκαν με την επαφή %s</string>
<string name="shared_text_with_x">Το κείμενο διαμοιράστηκε με την επαφή %s</string>
<string name="no_storage_permission">Απόδοση δικαιώματος στο %1$s για πρόσβαση στον εξωτερικό αποθηκευτικό χώρο</string>
<string name="no_camera_permission">Απόδοση δικαιώματος στο %1$s για πρόσβαση στην φωτογραφική μηχανή</string>
@ -577,7 +582,7 @@
<string name="missing_internet_permission">Απόδοση δικαιώματος χρήσης Internet</string>
<string name="me">Εγώ</string>
<string name="contact_asks_for_presence_subscription">Η επαφή ζητά συνδρομή σε υπηρεσία παρουσίας</string>
<string name="allow">Επιτρέπω</string>
<string name="allow">Να επιτραπεί</string>
<string name="no_permission_to_access_x">Δεν υπάρχει δικαίωμα για πρόσβαση στο %s</string>
<string name="remote_server_not_found">Δεν βρέθηκε ο απομακρυσμένος διακομιστής</string>
<string name="remote_server_timeout">Λήξη χρόνου για τον απομακρυσμένο διακομιστή</string>
@ -833,7 +838,7 @@
<string name="restore_warning">Μην χρησιμοποιείτε τη λειτουργία επαναφοράς αντιγράφων ασφαλείας για να κλωνοποιήσετε (ταυτόχρονη εκτέλεση) μια εγκατάσταση. Η επαναφορά αντιγράφου ασφαλείας προσφέρεται μόνο για μεταφορές ή σε περίπτωση που έχετε χάσει την αρχική συσκευή.</string>
<string name="unable_to_restore_backup">Αδυναμία επαναφοράς αντιγράφου ασφαλείας.</string>
<string name="unable_to_decrypt_backup">Αδυναμία αποκρυπτογράφησης του αντιγράφου ασφαλείας. Είναι ο κωδικός σωστός;</string>
<string name="backup_channel_name">Δημιουργία &amp; Επαναφορά αντιγράφων ασφαλείας</string>
<string name="backup_channel_name">Δημιουργία &amp; Επαναφορά</string>
<string name="enter_jabber_id">Εισάγετε τη διεύθυνση XMPP</string>
<string name="create_group_chat">Δημιουργία ομαδικής συζήτησης</string>
<string name="join_public_channel">Είσοδος σε δημόσιο κανάλι</string>
@ -893,8 +898,8 @@
<string name="please_enable_an_account">Παρακαλώ ενεργοποιήστε έναν λογαριασμό</string>
<string name="make_call">Νέα κλήση</string>
<string name="rtp_state_incoming_call">Εισερχόμενη κλήση</string>
<string name="rtp_state_incoming_video_call">Εισερχόμενη κλήση βίντεο</string>
<string name="rtp_state_connecting">Σύνδεση</string>
<string name="rtp_state_incoming_video_call">Εισερχόμενη βιντεοκλήση</string>
<string name="rtp_state_connecting">Γίνεται σύνδεση</string>
<string name="rtp_state_connected">Συνδέθηκε</string>
<string name="rtp_state_accepting_call">Αποδοχή κλήσης</string>
<string name="rtp_state_ending_call">Τερματισμός κλήσης</string>
@ -909,7 +914,7 @@
<string name="rtp_state_application_failure">Αποτυχία εφαρμογής</string>
<string name="hang_up">Τερματισμός κλήσης</string>
<string name="ongoing_call">Κλήση σε εξέλιξη</string>
<string name="ongoing_video_call">Κλήση βίντεο σε εξέλιξη</string>
<string name="ongoing_video_call">Βιντεοκλήση σε εξέλιξη</string>
<string name="disable_tor_to_make_call">Απενεργοποίηστε το Tor για να κάνετε κλήσεις</string>
<string name="incoming_call">Εισερχόμενη κλήση</string>
<string name="incoming_call_duration">Εισερχόμενη κλήση · %s</string>
@ -918,7 +923,7 @@
<string name="outgoing_call_duration">Εξερχόμενη κλήση · %s</string>
<string name="missed_call">Αναπάντηση κλήση</string>
<string name="audio_call">Κλήση ήχου</string>
<string name="video_call">Κλήση βίντεο</string>
<string name="video_call">Βιντεοκλήση</string>
<string name="help">Βοήθεια</string>
<string name="switch_to_conversation">Εναλλαγή στη συζήτηση</string>
<string name="microphone_unavailable">Το μικρόφωνο δεν είναι διαθέσιμο</string>
@ -949,11 +954,13 @@
<item quantity="one">Κάποιο μήνυμα δεν ήταν δυνατό να παραδοθεί</item>
<item quantity="other">Κάποια μηνύματα δεν ήταν δυνατό να παραδοθούν</item>
</plurals>
<string name="failed_deliveries">Αποτυχημένες διανομές</string>
<string name="failed_deliveries">Αποτυχημένες παραδόσεις</string>
<string name="more_options">Περισσότερες επιλογές</string>
<string name="no_application_found">Δεν βρέθηκε εφαρμογή</string>
<string name="invite_to_app">Πρόσκληση στο Conversations</string>
<string name="unable_to_parse_invite">Αδυναμία ανάγνωσης πρόσκλησης</string>
<string name="server_does_not_support_easy_onboarding_invites">Ο διακομιστής δεν υποστηρίζει την δημιουργία προσκλήσεων</string>
<string name="no_active_accounts_support_this">Κανένας από τους ενεργούς λογαριασμούς δεν υποστηρίζει αυτό το χαρακτηριστικό</string>
<string name="backup_started_message">Το αντίγραφο ασφαλείας δημιουργείται. Θα λάβετε ειδοποίηση όταν ολοκληρωθεί.</string>
<string name="unable_to_enable_video">Αδυναμία ενεργοποίησης βίντεο.</string>
</resources>

View File

@ -124,6 +124,7 @@
<string name="pref_ringtone">Tono de llamada</string>
<string name="pref_notification_sound">Sonido de notificación</string>
<string name="pref_notification_sound_summary">Sonido de notificación para nuevos mensajes</string>
<string name="pref_call_ringtone_summary">Tono para las nuevas llamadas</string>
<string name="pref_notification_grace_period">Periodo de gracia</string>
<string name="pref_notification_grace_period_summary">El periodo de tiempo en el que las notificaciones están silenciadas tras detectar actividad en otro de tus dispositivos.</string>
<string name="pref_advanced_options">Avanzado</string>
@ -149,6 +150,7 @@
<string name="error_file_not_found">Archivo no encontrado</string>
<string name="error_io_exception">Error general. ¿Es posible que no tengas espacio en disco?</string>
<string name="error_security_exception_during_image_copy">La aplicación usaste para seleccionar esta imagen no proporcionó suficientes permisos para leer el archivo.\n\n<small>Utiliza un explorador de archivos diferente para seleccionar la imagen</small></string>
<string name="error_security_exception">La aplicación que has utilizado para compartir este archivo no presentó permisos suficientes</string>
<string name="account_status_unknown">Desconocido</string>
<string name="account_status_disabled">Deshabilitado temporalmente</string>
<string name="account_status_online">Conectado</string>
@ -163,6 +165,7 @@
<string name="account_status_regis_not_sup">El servidor no soporta registros</string>
<string name="account_status_regis_invalid_token">Token de registro inválido</string>
<string name="account_status_tls_error">Error de negociación TLS</string>
<string name="account_status_tls_error_domain">Dominio no verificable</string>
<string name="account_status_policy_violation">Policy violation</string>
<string name="account_status_incompatible_server">Servidor incompatible</string>
<string name="account_status_stream_error">Error de flujo</string>
@ -215,6 +218,8 @@
<string name="openpgp_key_id">OpenPGP Key ID</string>
<string name="omemo_fingerprint">Huella digital OMEMO</string>
<string name="omemo_fingerprint_x509">Huella digital v\\OMEMO</string>
<string name="omemo_fingerprint_selected_message">Huella digital OMEMO (origen del mensaje)</string>
<string name="omemo_fingerprint_x509_selected_message">Huella digital v\\OMEMO (origen del mensaje)</string>
<string name="other_devices">Otros dispositivos</string>
<string name="trust_omemo_fingerprints">Huellas digitales OMEMO de confianza</string>
<string name="fetching_keys">Buscando claves...</string>
@ -462,6 +467,8 @@
<string name="account_status_host_unknown">El servidor no es responsable de este dominio</string>
<string name="server_info_broken">Error</string>
<string name="pref_presence_settings">Disponibilidad</string>
<string name="pref_away_when_screen_off">Ausente cuando el dispositivo esté bloqueado</string>
<string name="pref_away_when_screen_off_summary">Mostrar como Ausente cuando el dispositivo esté bloqueado</string>
<string name="pref_dnd_on_silent_mode">Ocupado en modo silencio</string>
<string name="pref_dnd_on_silent_mode_summary">Mostrar como Ocupado cuando el dispositivo esté en modo silencio</string>
<string name="pref_treat_vibrate_as_silent">Modo vibración como modo silencio</string>
@ -954,4 +961,6 @@
<string name="unable_to_parse_invite">No se ha podido leer la invitación</string>
<string name="server_does_not_support_easy_onboarding_invites">El servidor no soporta la creación de invitaciones</string>
<string name="no_active_accounts_support_this">Ninguna cuenta activa soporta esta característica</string>
<string name="backup_started_message">La copia de seguridad ha empezado. Recibirás una notificación cuando se haya completado.</string>
<string name="unable_to_enable_video">No se ha podido habilitar el vídeo.</string>
</resources>

View File

@ -124,6 +124,7 @@
<string name="pref_ringtone">Sonnerie</string>
<string name="pref_notification_sound">Son des notifications</string>
<string name="pref_notification_sound_summary">Son de notification pour les nouveaux messages</string>
<string name="pref_call_ringtone_summary">Sonnerie d\'appel entrant</string>
<string name="pref_notification_grace_period">Période sans notification</string>
<string name="pref_notification_grace_period_summary">La durée pendant laquelle les notifications sont désactivées après la détection d\'une activité sur l\'un de vos autres appareils.</string>
<string name="pref_advanced_options">Avancé</string>
@ -149,6 +150,7 @@
<string name="error_file_not_found">Impossible de trouver le fichier</string>
<string name="error_io_exception">Erreur générale d\'E/S. Avez-vous encore de l\'espace libre?</string>
<string name="error_security_exception_during_image_copy">L\'application utilisée ne donne pas la permission de lire l\'image.\n\n<small>Utilisez une autre application pour choisir une image.</small></string>
<string name="error_security_exception">L\'app avec laquelle vous avez partagé ce fichier n\'a pas fourni assez de permissions.</string>
<string name="account_status_unknown">Inconnu</string>
<string name="account_status_disabled">Désactivé temporairement</string>
<string name="account_status_online">En ligne</string>
@ -163,6 +165,7 @@
<string name="account_status_regis_not_sup">Inscription non supportée par le serveur</string>
<string name="account_status_regis_invalid_token">Jeton dinscription invalide</string>
<string name="account_status_tls_error">La négociation TLS a échoué</string>
<string name="account_status_tls_error_domain">Domaine non vérifiable</string>
<string name="account_status_policy_violation">Violation de politique</string>
<string name="account_status_incompatible_server">Serveur incompatible</string>
<string name="account_status_stream_error">Erreur de flux</string>
@ -215,6 +218,7 @@
<string name="openpgp_key_id">ID de clé OpenPGP</string>
<string name="omemo_fingerprint">Empreinte OMEMO</string>
<string name="omemo_fingerprint_x509">v\\Empreinte OMEMO</string>
<string name="omemo_fingerprint_selected_message">Empreinte OMEMO (origine du message)</string>
<string name="other_devices">Autres appareils</string>
<string name="trust_omemo_fingerprints">Faire confiance aux empreintes OMEMO</string>
<string name="fetching_keys">Récupération des clés…</string>
@ -462,6 +466,7 @@
<string name="account_status_host_unknown">Le serveur n\'est pas responsable pour ce domaine</string>
<string name="server_info_broken">Détraqué</string>
<string name="pref_presence_settings">Disponibilité</string>
<string name="pref_away_when_screen_off">Absent quand l\'appareil est verrouillé</string>
<string name="pref_dnd_on_silent_mode">Occupé en mode silence</string>
<string name="pref_dnd_on_silent_mode_summary">Occupé lorsque l\'appareil est en mode silencieux</string>
<string name="pref_treat_vibrate_as_silent">Indisponible en mode vibreur</string>

View File

@ -85,12 +85,12 @@
<string name="preparing_image">Preparándose para enviar a imaxe</string>
<string name="preparing_images">Preparándose para enviar imaxes</string>
<string name="sharing_files_please_wait">Compartindo ficheiros. Por favor agarde...</string>
<string name="action_clear_history">Limpar historial</string>
<string name="clear_conversation_history">Limpar historial de conversa</string>
<string name="action_clear_history">Baleirar historial</string>
<string name="clear_conversation_history">Eliminar historial da conversa</string>
<string name="clear_histor_msg">¿Queres eliminar as mensaxes desta conversa?\n\n<b>Aviso:</b> Esto non lle afecta as mensaxes gardadas noutros dispositivos ou servidores. </string>
<string name="delete_file_dialog">Eliminar ficheiro</string>
<string name="delete_file_dialog_msg">Está segura de querer eliminar este ficheiro?\n\n<b>Aviso:</b> Esto non eliminará as copias de este ficheiro que están gardadas en outros dispositivos ou servidores.</string>
<string name="also_end_conversation">Pechar esta conversa a posteriori</string>
<string name="also_end_conversation">Pechar a conversa tras baleirar</string>
<string name="choose_presence">Escoller dispositivo</string>
<string name="send_unencrypted_message">Enviar mensaxe non cifrada</string>
<string name="send_message">Enviar mensaxe</string>
@ -131,7 +131,9 @@
<string name="pref_never_send_crash">Nunca enviar informe de erros</string>
<string name="pref_never_send_crash_summary">Ao enviar trazas do sistema estás axudando ao desenvolvemento</string>
<string name="pref_confirm_messages">Confirmación de mensaxes</string>
<string name="pref_confirm_messages_summary">Permitir aos teus contactos saber se recibiches e liches as súas mensaxes</string>
<string name="pref_confirm_messages_summary">Permitir aos teus contactos saber se recibiches e leches as súas mensaxes</string>
<string name="pref_prevent_screenshots">Evitar capturas de pantalla</string>
<string name="pref_prevent_screenshots_summary">Agochar os contidos da app no cambiador de apps e bloquear pantallazos</string>
<string name="pref_ui_options">Interface</string>
<string name="openpgp_error">OpenKeychain producíu un erro.</string>
<string name="bad_key_for_encryption">Chave incorrecta para cifrar.</string>
@ -144,12 +146,12 @@
<string name="ask_for_presence_updates">Solicitar actualizacións de presenza</string>
<string name="attach_choose_picture">Seleccionar imaxe</string>
<string name="attach_take_picture">Facer foto</string>
<string name="preemptively_grant">Por defecto otorgar peticiones de suscripción</string>
<string name="preemptively_grant">Por defecto conceder solicitudes de subscrición</string>
<string name="error_not_an_image_file">O arquivo seleccionado non é unha imaxe</string>
<string name="error_compressing_image">Non se puido converter o ficheiro de imaxe</string>
<string name="error_file_not_found">Arquivo non atopado</string>
<string name="error_io_exception">Erro xeral de I/O. ¿Quedaches sen espazo no disco?</string>
<string name="error_security_exception_during_image_copy">A app utilizada para escoller esta imaxe non deu permisos suficientes para ler o ficheiro.\n\n<small>Usa un xestor de ficheiros diferente para escoller a imaxe</small></string>
<string name="error_security_exception_during_image_copy">A app utilizada para seleccionar esta imaxe non deu permisos suficientes para ler o ficheiro.\n\n<small>Usa un xestor de ficheiros diferente para elexir a imaxe</small></string>
<string name="error_security_exception">A app que usaches para compartir este ficheiro non concedeu os permisos suficientes.</string>
<string name="account_status_unknown">Descoñecido</string>
<string name="account_status_disabled">Desactivado temporalmente</string>
@ -252,7 +254,7 @@
<string name="leave">Saír</string>
<string name="contact_added_you">Contacto engadido a túa lista de contactos</string>
<string name="add_back">Volver a engadir</string>
<string name="contact_has_read_up_to_this_point">%s leeu ate este punto</string>
<string name="contact_has_read_up_to_this_point">%s leu ata este punto</string>
<string name="contacts_have_read_up_to_this_point">%s leu ate este punto</string>
<string name="contacts_and_n_more_have_read_up_to_this_point">%1$s + %2$d outras leron ata este punto</string>
<string name="everyone_has_read_up_to_this_point">Todas leron ate este punto</string>
@ -349,8 +351,8 @@
<string name="no_application_found_to_open_file">Non se atopou unha app para abrir o ficheiro</string>
<string name="no_application_found_to_open_link">Non se atopou app para abrir a ligazón</string>
<string name="no_application_found_to_view_contact">Non se atopou app para ver o contacto</string>
<string name="pref_show_dynamic_tags">Etiquetas dinámicas</string>
<string name="pref_show_dynamic_tags_summary">Mostrar etiquetas de só lectura baixo os contactos</string>
<string name="pref_show_dynamic_tags">Información do estado</string>
<string name="pref_show_dynamic_tags_summary">Mostra o estado debaixo do nome do contacto</string>
<string name="enable_notifications">Habilitar notificacións</string>
<string name="no_conference_server_found">Non se atopou ningún servidor de conversa en grupo</string>
<string name="conference_creation_failed">Non se puido crear a conversa en grupo</string>
@ -414,6 +416,7 @@
<string name="audio">son</string>
<string name="video">video</string>
<string name="image">imaxe</string>
<string name="vector_graphic">gráfico de vector</string>
<string name="pdf_document">documento PDF</string>
<string name="apk">App Android</string>
<string name="vcard">Contacto</string>
@ -614,14 +617,14 @@
<string name="not_trusted">Non confiables</string>
<string name="invalid_barcode">Código de barras 2D non válido</string>
<string name="pref_clean_cache_summary">Baleirar o cartafol da caché (utilizado pola cámara)</string>
<string name="pref_clean_cache">Limpar caché</string>
<string name="pref_clean_cache">Baleirar caché</string>
<string name="pref_clean_private_storage">Baleirar almacenaxe privada</string>
<string name="pref_clean_private_storage_summary">Baleirar a almacenaxe privada onde se gardan os ficheiros (poderán volver a descargarse desde o servidor)</string>
<string name="i_followed_this_link_from_a_trusted_source">Seguín esta ligazón desde unha fonte de confianza</string>
<string name="verifying_omemo_keys_trusted_source">Vai verificar as chaves OMEMO de %1$s despois de pulsar na ligazón. Esto só é seguro si sigueu esta ligazón desde unha fonte de confianza onde só %2$s a podería ter publicado.</string>
<string name="verifying_omemo_keys_trusted_source">Vas verificar as chaves OMEMO de %1$s despois de premer na ligazón. Esto só é seguro se seguiches esta ligazón desde unha fonte de confianza onde só %2$s a podería ter publicado.</string>
<string name="verify_omemo_keys">Validar chaves OMEMO</string>
<string name="show_inactive_devices">Mostrar inactivos</string>
<string name="hide_inactive_devices">Amagar inactivos</string>
<string name="hide_inactive_devices">Agochar inactivos</string>
<string name="distrust_omemo_key">Retirar confianza a dispositivo</string>
<string name="distrust_omemo_key_text">Tes a certeza de que queres eliminar a verificación deste dispositivo?\nEste dispositivo e as súas mensaxes serán marcados como \"Non confiable\".</string>
<plurals name="seconds">
@ -707,8 +710,8 @@
<string name="pref_omemo_setting_summary_default_on">OMEMO utilizarase por defecto para as novas conversas.</string>
<string name="pref_omemo_setting_summary_default_off">OMEMO terá que ser activado explícitamente para novas conversacións.</string>
<string name="create_shortcut">Crear acceso directo</string>
<string name="pref_font_size">Tamaño da fonte</string>
<string name="pref_font_size_summary">O tamaño de fonte relativo a esta app</string>
<string name="pref_font_size">Tamaño da letra</string>
<string name="pref_font_size_summary">O tamaño relativo da letra que utiliza a app.</string>
<string name="default_on">Activado por defecto</string>
<string name="default_off">Desactivado por defecto</string>
<string name="small">Pequena</string>
@ -912,6 +915,7 @@
<string name="rtp_state_connectivity_lost_error">Perdeuse a conexión</string>
<string name="rtp_state_retracted">Chamada cortada</string>
<string name="rtp_state_application_failure">Fallo na aplicación</string>
<string name="rtp_state_security_error">Problema na verificación</string>
<string name="hang_up">Colgar</string>
<string name="ongoing_call">Chamada en curso</string>
<string name="ongoing_video_call">Videochamada en curso</string>
@ -963,4 +967,6 @@
<string name="no_active_accounts_support_this">Ningunha conta activa soporta esta función</string>
<string name="backup_started_message">Comezou a creación da copia de apoio. Recibirás unha notificación cando remate.</string>
<string name="unable_to_enable_video">Non se puido activar o vídeo.</string>
<string name="plain_text_document">Documento de texto plano</string>
</resources>

View File

@ -53,6 +53,7 @@
<string name="unblock_domain_text">%s összes partnerének tiltását feloldja?</string>
<string name="contact_blocked">Partner tiltva</string>
<string name="blocked">Tiltva</string>
<string name="remove_bookmark_text">Szeretné eltávolítani ezt a könyvjelzőkből: %s? Ezzel a könyvjelzővel megjelölt beszélgetései nem lesznek eltávolítva.</string>
<string name="register_account">Új fiók regisztrálása a kiszolgálón</string>
<string name="change_password_on_server">Jelszó megváltoztatása a kiszolgálón</string>
<string name="share_with">Megosztás ezzel…</string>
@ -70,6 +71,8 @@
<string name="unblock">Tiltás feloldása</string>
<string name="save">Mentés</string>
<string name="ok">Rendben</string>
<string name="crash_report_title">%1$s összeomlott</string>
<string name="crash_report_message">Azzal, hogy az XMPP fiókja használatával beküldi a veremkiíratásokat, elősegítheti a %1$s alkalmazás folyamatos fejlesztését.</string>
<string name="send_now">Küldés most</string>
<string name="send_never">Sose kérdezzen újra</string>
<string name="problem_connecting_to_account">Nem sikerült kapcsolódni a fiókhoz</string>
@ -99,6 +102,7 @@
<string name="send_unencrypted">Küldés titkosítatlanul</string>
<string name="decryption_failed">A visszafejtés sikertelen. Talán nem rendelkezik a megfelelő személyes kulccsal.</string>
<string name="openkeychain_required">OpenKeychain</string>
<string name="openkeychain_required_long"><![CDATA[A %1$s az <b>OpenKeychain</b> alkalmazást használja az üzenetek titkosításához és visszafejtéséhez, valamint a személyes kulcsai kezeléséhez.<br><br>Ez GPLv3+ szerint licencelt, és elérhető az F-Droid és a Google Play szoftveráruházakban.<br><br><small>(Ezután indítsa újra a %1$s alkalmazást.)</small>]]></string>
<string name="restart">Újraindítás</string>
<string name="install">Telepítés</string>
<string name="openkeychain_not_installed">Telepítse az OpenKeychain alkalmazás</string>
@ -120,6 +124,7 @@
<string name="pref_ringtone">Csengőhang</string>
<string name="pref_notification_sound">Értesítési hang</string>
<string name="pref_notification_sound_summary">Értesítési hang új üzeneteknél</string>
<string name="pref_call_ringtone_summary">Csengőhang bejövő hívásnál</string>
<string name="pref_notification_grace_period">Türelmi idő</string>
<string name="pref_notification_grace_period_summary">Az időtartam, amíg az értesítések némítva vannak az egyéb eszközei egyikén történt tevékenység észlelése után.</string>
<string name="pref_advanced_options">Speciális</string>
@ -127,7 +132,10 @@
<string name="pref_never_send_crash_summary">A veremkiíratások elküldésével segíti a fejlesztést</string>
<string name="pref_confirm_messages">Üzenetek megerősítése</string>
<string name="pref_confirm_messages_summary">Tudassa a partnereivel, hogy megkapta és elolvasta az üzeneteiket</string>
<string name="pref_prevent_screenshots">Képernyőfotó készítésének megakadályozása</string>
<string name="pref_prevent_screenshots_summary">Az alkalmazás tartalmának elrejtése az alkalmazásváltóban és a képernyőfotók blokkolása</string>
<string name="pref_ui_options">Felhasználói felület</string>
<string name="openpgp_error">Az OpenKeychain hibát produkált.</string>
<string name="bad_key_for_encryption">Rossz kulcs a titkosításhoz.</string>
<string name="accept">Elfogadás</string>
<string name="error">Hiba történt</string>
@ -143,6 +151,8 @@
<string name="error_compressing_image">Nem sikerült átalakítani a képet</string>
<string name="error_file_not_found">A fájl nem található</string>
<string name="error_io_exception">Általános bemeneti/kimeneti hiba. Talán elfogyott a tárolóhely?</string>
<string name="error_security_exception_during_image_copy">A kép kiválasztásához használt alkalmazás nem biztosított számunkra elegendő jogosultságot a fájl olvasásához.\n\n<small>Használjon másik fájlkezelőt a kép kiválasztásához</small></string>
<string name="error_security_exception">A fájl megosztásához használt alkalmazás nem biztosított számunkra elegendő jogosultságot.</string>
<string name="account_status_unknown">Ismeretlen</string>
<string name="account_status_disabled">Átmenetileg letiltva</string>
<string name="account_status_online">Elérhető</string>
@ -157,6 +167,7 @@
<string name="account_status_regis_not_sup">A kiszolgáló nem támogatja a regisztrációt</string>
<string name="account_status_regis_invalid_token">Érvénytelen regisztrációs token</string>
<string name="account_status_tls_error">A TLS-egyeztetés sikertelen</string>
<string name="account_status_tls_error_domain">Tartomány nem ellenőrizhető</string>
<string name="account_status_policy_violation">Irányelv megsértése</string>
<string name="account_status_incompatible_server">Nem kompatibilis kiszolgáló</string>
<string name="account_status_stream_error">Adatfolyamhiba</string>
@ -174,6 +185,7 @@
<string name="openpgp_has_been_published">Az OpenPGP nyilvános kulcs közzé lett téve.</string>
<string name="mgmt_account_enable">Fiók engedélyezése</string>
<string name="mgmt_account_are_you_sure">Biztos benne?</string>
<string name="mgmt_account_delete_confirm_text">A fiók törlésével az összes beszélgetési előzményei is eltávolításra kerülnek</string>
<string name="attach_record_voice">Hang rögzítése</string>
<string name="account_settings_jabber_id">XMPP-cím</string>
<string name="block_jabber_id">XMPP-cím tiltása</string>
@ -204,6 +216,7 @@
<string name="last_seen_day">egy napja volt aktív</string>
<string name="last_seen_days">%d napja volt aktív</string>
<string name="install_openkeychain">Titkosított üzenet. Telepítse az OpenKeychain alkalmazást a visszafejtéshez.</string>
<string name="openpgp_messages_found">Új OpenPGP titkosítású üzenetek találhatók</string>
<string name="openpgp_key_id">OpenPGP kulcsazonosító</string>
<string name="omemo_fingerprint">OMEMO ujjlenyomat</string>
<string name="omemo_fingerprint_x509">v\\OMEMO ujjlenyomat</string>
@ -394,6 +407,7 @@
<string name="audio">hang</string>
<string name="video">videó</string>
<string name="image">kép</string>
<string name="vector_graphic">vektorgrafika</string>
<string name="pdf_document">PDF-dokumentum</string>
<string name="apk">Android alkalmazás</string>
<string name="vcard">Partner</string>
@ -447,6 +461,8 @@
<string name="account_status_host_unknown">A kiszolgáló nem felelős a tartományért</string>
<string name="server_info_broken">Törött</string>
<string name="pref_presence_settings">Elérhetőség</string>
<string name="pref_away_when_screen_off">Távol, ha az eszköz le van zárva</string>
<string name="pref_away_when_screen_off_summary">Mutasson „Távoli”-ként, ha az eszköz le van zárva</string>
<string name="pref_treat_vibrate_as_silent">Rezgés kezelése csendes módként</string>
<string name="pref_show_connection_options">Kiterjesztett kapcsolati beállítások</string>
<string name="pref_show_connection_options_summary">Gépnév és port beállításainak megjelenítése egy fiók beállításakor</string>

View File

@ -132,6 +132,8 @@
<string name="pref_never_send_crash_summary">Se scegli di inviare una segnalazione dellerrore aiuterai lo sviluppo</string>
<string name="pref_confirm_messages">Conferma messaggi</string>
<string name="pref_confirm_messages_summary">Fai sapere ai tuoi contatti quando hai ricevuto e letto i loro messaggi</string>
<string name="pref_prevent_screenshots">Impedisci cattura schermo</string>
<string name="pref_prevent_screenshots_summary">Nascondi i contenuti dell\'app nell\'elenco recenti e blocca la cattura delle schermate</string>
<string name="pref_ui_options">Interfaccia utente</string>
<string name="openpgp_error">OpenKeychain ha generato un errore.</string>
<string name="bad_key_for_encryption">Chiave di cifratura non valida.</string>
@ -150,6 +152,7 @@
<string name="error_file_not_found">File non trovato</string>
<string name="error_io_exception">Errore di I/O generico. Forse hai esaurito lo spazio?</string>
<string name="error_security_exception_during_image_copy">Lapp che hai usato per selezionare questa immagine non ha fornito autorizzazioni sufficienti per leggere il file.\n\n<small>Usa un gestore di file differente per scegliere unimmagine</small></string>
<string name="error_security_exception">L\'app che hai usato per condividere questo file non ha fornito autorizzazioni sufficienti.</string>
<string name="account_status_unknown">Sconosciuto</string>
<string name="account_status_disabled">Disattivato temporaneamente</string>
<string name="account_status_online">Online</string>
@ -164,6 +167,7 @@
<string name="account_status_regis_not_sup">Registrazione non supportata dal server</string>
<string name="account_status_regis_invalid_token">Token di registrazione non valido</string>
<string name="account_status_tls_error">Negoziazione TLS fallita</string>
<string name="account_status_tls_error_domain">Dominio non verificabile</string>
<string name="account_status_policy_violation">Violazione della policy</string>
<string name="account_status_incompatible_server">Server non compatibile</string>
<string name="account_status_stream_error">Errore di stream</string>
@ -412,6 +416,7 @@
<string name="audio">audio</string>
<string name="video">video</string>
<string name="image">immagine</string>
<string name="vector_graphic">grafica vettoriale</string>
<string name="pdf_document">Documento PDF</string>
<string name="apk">Applicazione Android</string>
<string name="vcard">Contatto</string>
@ -910,6 +915,7 @@
<string name="rtp_state_connectivity_lost_error">Connessione persa</string>
<string name="rtp_state_retracted">Chiamata ritirata</string>
<string name="rtp_state_application_failure">Errore dell\'app</string>
<string name="rtp_state_security_error">Problema di verifica</string>
<string name="hang_up">Riaggancia</string>
<string name="ongoing_call">Chiamata in corso</string>
<string name="ongoing_video_call">Chiamata video in corso</string>
@ -960,4 +966,7 @@
<string name="server_does_not_support_easy_onboarding_invites">Il server non supporta la generazione di inviti</string>
<string name="no_active_accounts_support_this">Nessun account attivo supporta questa funzione</string>
<string name="backup_started_message">Il backup è iniziato. Riceverai una notifica una volta completato.</string>
<string name="unable_to_enable_video">Impossibile attivare il video.</string>
<string name="plain_text_document">Documento di testo</string>
</resources>

View File

@ -40,7 +40,7 @@
<string name="invalid_muc_nick">正しくないニックネームです</string>
<string name="admin">管理者</string>
<string name="owner">所有者</string>
<string name="moderator">モデレーター</string>
<string name="moderator">調停者</string>
<string name="participant">参加者</string>
<string name="visitor">訪問者</string>
<string name="remove_contact_text">連絡先名簿から %s を削除しますか? この連絡先との会話は削除されません。</string>
@ -71,23 +71,23 @@
<string name="crash_report_title">%1$s がクラッシュしました</string>
<string name="crash_report_message">あなたの XMPP アカウントを使用してスタックトレースの送信をすることで、 %1$s の継続的な開発を支援します。</string>
<string name="send_now">今すぐ送信</string>
<string name="send_never">今後表示しない</string>
<string name="send_never">今後表示しない</string>
<string name="problem_connecting_to_account">アカウントに接続できません</string>
<string name="problem_connecting_to_accounts">複数のアカウントに接続できません</string>
<string name="touch_to_fix">タップしてアカウントを管理</string>
<string name="attach_file">添付ファイル</string>
<string name="not_in_roster">連絡先が連絡先名簿にありません。追加しますか?</string>
<string name="attach_file">ファイルを添付</string>
<string name="not_in_roster">連絡先が連絡先名簿にありません。名簿に追加しますか?</string>
<string name="add_contact">連絡先を追加</string>
<string name="send_failed">配信に失敗しました</string>
<string name="preparing_image">送用画像の準備中</string>
<string name="preparing_images">送用画像の準備中</string>
<string name="preparing_image">用画像の準備中</string>
<string name="preparing_images">用画像の準備中</string>
<string name="sharing_files_please_wait">ファイル共有中。しばらくお待ちください…</string>
<string name="action_clear_history">履歴をクリア</string>
<string name="clear_conversation_history">会話履歴をクリア</string>
<string name="action_clear_history">履歴を消去</string>
<string name="clear_conversation_history">会話履歴を消去</string>
<string name="clear_histor_msg">この会話のすべてのメッセージを削除してもよろしいですか?\n\n<b>警告:</b> 他のデバイスやサーバーに保存されているメッセージのコピーには影響しません。</string>
<string name="delete_file_dialog">ファイルを削除</string>
<string name="delete_file_dialog_msg">このファイルを削除してもよろしいですか?\n\n<b>警告:</b> これは、他のデバイスやサーバーに保存されているファイルのコピーは削除しません。</string>
<string name="also_end_conversation">の後、この会話を閉じる</string>
<string name="also_end_conversation">の後、この会話を閉じる</string>
<string name="choose_presence">デバイスを選択</string>
<string name="send_unencrypted_message">暗号化されていないメッセージを送信</string>
<string name="send_message">メッセージを送信</string>
@ -96,8 +96,8 @@
<string name="send_omemo_x509_message">v\\OMEMO 暗号化メッセージを送信</string>
<string name="send_pgp_message">OpenPGP 暗号化メッセージを送信</string>
<string name="your_nick_has_been_changed">ニックネームが変更されました</string>
<string name="send_unencrypted">暗号化されていない送信</string>
<string name="decryption_failed">復号に失敗しました。おそらく秘密鍵が正しくないようです</string>
<string name="send_unencrypted">暗号化せずに送信</string>
<string name="decryption_failed">復号に失敗しました。適切な秘密鍵を持っていないのかもしれません</string>
<string name="openkeychain_required">OpenKeychain</string>
<string name="openkeychain_required_long"><![CDATA[ %1$s は <b>OpenKeychain</b> を利用して、メッセージの暗号化および復号、そしてあなたの公開鍵を管理します。<br><br>それは GPLv3+ ライセンスの下で、F-Droid および Google Play から利用可能です。<br><br><small>(後で %1$s を再起動してください。)</small>]]></string>
<string name="restart">再起動</string>
@ -126,9 +126,11 @@
<string name="pref_notification_grace_period_summary">別のデバイスでの操作を検知した際に、通知を止める時間の長さ</string>
<string name="pref_advanced_options">詳細</string>
<string name="pref_never_send_crash">クラッシュレポートを送信しない</string>
<string name="pref_never_send_crash_summary">スタックトレースを送信して、Conversations の継続的な開発を支援します</string>
<string name="pref_confirm_messages">メッセージ確認</string>
<string name="pref_never_send_crash_summary">スタックトレースを送信すると、 Conversations の開発を支援します</string>
<string name="pref_confirm_messages">メッセージ確認</string>
<string name="pref_confirm_messages_summary">あなたがメッセージを受信して読んだときに、連絡先に知らせます</string>
<string name="pref_prevent_screenshots">スクリーンショットを防ぐ</string>
<string name="pref_prevent_screenshots_summary">アプリスイッチャーでアプリの内容を隠し、スクリーンショットを防ぐ</string>
<string name="pref_ui_options">UI</string>
<string name="openpgp_error">OpenKeychain でエラーが発生しました。</string>
<string name="bad_key_for_encryption">暗号化の鍵が不正です。</string>
@ -139,14 +141,15 @@
<string name="send_presence_updates">参加アップデートを送信</string>
<string name="receive_presence_updates">参加アップデートを受信</string>
<string name="ask_for_presence_updates">参加アップデートを問合せ</string>
<string name="attach_choose_picture">写真を選択</string>
<string name="attach_choose_picture">画像を選択</string>
<string name="attach_take_picture">写真を撮影</string>
<string name="preemptively_grant">事前にサブスクリプション要求を許可する</string>
<string name="error_not_an_image_file">選択したファイルは画像ではありません</string>
<string name="error_compressing_image">画像ファイルを変換できません</string>
<string name="error_file_not_found">ファイルが見つかりません</string>
<string name="error_io_exception">一般的な I/O エラー。おそらく空き容量がなくなっていませんか?</string>
<string name="error_security_exception_during_image_copy">あなたが画像の選択のために使用したアプリは、読み取りに必要なアクセス権がありません。\n\n<small>別のファイルマネージャを使用して、画像を選択してください。</small></string>
<string name="error_io_exception">一般的な入出力エラー。空き容量がなくなっていませんか?</string>
<string name="error_security_exception_during_image_copy">あなたが画像の選択のために使用したアプリは、読み取りに必要なアクセス権がありません。\n\n<small>画像を選択するために、別のファイルマネージャーを使ってください</small></string>
<string name="error_security_exception">このファイルを共有するために使用したアプリは、十分な許可が与えられていませんでした。</string>
<string name="account_status_unknown">不明</string>
<string name="account_status_disabled">一時的に無効</string>
<string name="account_status_online">オンライン</string>
@ -154,13 +157,14 @@
<string name="account_status_offline">オフライン</string>
<string name="account_status_unauthorized">許可されていません</string>
<string name="account_status_not_found">サーバーが見つかりません</string>
<string name="account_status_no_internet">接続エラー</string>
<string name="account_status_no_internet">接続なし</string>
<string name="account_status_regis_fail">登録に失敗しました</string>
<string name="account_status_regis_conflict">ユーザー名は既に使用されています</string>
<string name="account_status_regis_success">登録が完了しました</string>
<string name="account_status_regis_not_sup">サーバー登録をサポートしていません</string>
<string name="account_status_regis_not_sup">サーバー登録をサポートしていません</string>
<string name="account_status_regis_invalid_token">トークンが無効です</string>
<string name="account_status_tls_error">TLS ネゴシエーションに失敗しました</string>
<string name="account_status_tls_error_domain">検証不可能なドメイン</string>
<string name="account_status_policy_violation">ポリシー違反</string>
<string name="account_status_incompatible_server">互換性のないサーバー</string>
<string name="account_status_stream_error">ストリーム エラー</string>
@ -174,7 +178,7 @@
<string name="mgmt_account_publish_avatar">アバターを公開</string>
<string name="mgmt_account_publish_pgp">OpenPGP 公開鍵を公開</string>
<string name="unpublish_pgp">OpenPGP 公開鍵を削除</string>
<string name="unpublish_pgp_message">告知から OpenPGP 公開鍵を削除してもよろしいですか?\n連絡先はあなたに OpenPGP 暗号化メッセージを送信できなくなります。</string>
<string name="unpublish_pgp_message">在告知から OpenPGP 公開鍵を削除してもよろしいですか?\n連絡先はあなたに OpenPGP 暗号化メッセージを送信できなくなります。</string>
<string name="openpgp_has_been_published">OpenPGP 公開鍵を公開しました。</string>
<string name="mgmt_account_enable">アカウントを有効にする</string>
<string name="mgmt_account_are_you_sure">よろしいですか?</string>
@ -188,11 +192,11 @@
<string name="error_out_of_memory">メモリ不足です。画像が大きすぎます</string>
<string name="add_phone_book_text">%s をお使いのアドレス帳に追加しますか?</string>
<string name="server_info_show_more">サーバー情報</string>
<string name="server_info_mam">XEP-0313: メッセージ アーカイブ管理</string>
<string name="server_info_carbon_messages">XEP-0280: メッセージ カーボン</string>
<string name="server_info_mam">XEP-0313: メッセージ中央管理</string>
<string name="server_info_carbon_messages">XEP-0280: メッセージ複写</string>
<string name="server_info_csi">XEP-0352: クライアント状態表示</string>
<string name="server_info_blocking">XEP-0191: ブロッキング コマンド</string>
<string name="server_info_roster_version">XEP-0237: 名簿バージョニング</string>
<string name="server_info_roster_version">XEP-0237: 名簿バージョン管理</string>
<string name="server_info_stream_management">XEP-0198: ストリーム管理</string>
<string name="server_info_external_service_discovery">XEP-0215: 外部サービスの発見</string>
<string name="server_info_pep">XEP-0163: PEP (アバター / OMEMO)</string>
@ -209,7 +213,7 @@
<string name="last_seen_day">1日前に会いました</string>
<string name="last_seen_days">%d日前に会いました</string>
<string name="install_openkeychain">暗号化されたメッセージです。復号するには OpenKeychain をインストールしてください。</string>
<string name="openpgp_messages_found">新しい OpenPGP 暗号化されたメッセージが見つかりました</string>
<string name="openpgp_messages_found">新しい OpenPGP 暗号化されたメッセージが見つかりました</string>
<string name="openpgp_key_id">OpenPGP 鍵 ID</string>
<string name="omemo_fingerprint">OMEMO フィンガープリント</string>
<string name="omemo_fingerprint_x509">v\\OMEMO フィンガープリント</string>
@ -231,7 +235,7 @@
<string name="join">参加</string>
<string name="channel_full_jid_example">channel@conference.example.com/nick</string>
<string name="channel_bare_jid_example">channel@conference.example.com</string>
<string name="save_as_bookmark">ブックマークとして保存</string>
<string name="save_as_bookmark">ブックマーク保存</string>
<string name="delete_bookmark">ブックマークを削除</string>
<string name="destroy_room">グループチャットを破棄する</string>
<string name="destroy_channel">談話室を破棄する</string>
@ -252,14 +256,14 @@
<string name="publish">公開</string>
<string name="touch_to_choose_picture">アバターをタップしてギャラリーから画像を選択します</string>
<string name="publishing">公開中…</string>
<string name="error_publish_avatar_server_reject">サーバーがあなたの公開を拒否しました</string>
<string name="error_publish_avatar_server_reject">サーバーはあなたが公開するものを拒否しました</string>
<string name="error_publish_avatar_converting">画像を変換できません</string>
<string name="error_saving_avatar">ディスクにアバターを保存できませんでした</string>
<string name="error_saving_avatar">ディスクにアバターを保存できません</string>
<string name="or_long_press_for_default">(または長押しするとデフォルトに戻します)</string>
<string name="error_publish_avatar_no_server_support">ご利用のサーバーは、アバターの公開をサポートしていません</string>
<string name="private_message">ささやいた</string>
<string name="private_message_to">%s へ</string>
<string name="send_private_message_to">非公開メッセージを %s 送信</string>
<string name="send_private_message_to">非公開メッセージを %s 送信</string>
<string name="connect">接続</string>
<string name="account_already_exists">このアカウントは既に存在します</string>
<string name="next">次へ</string>
@ -280,15 +284,15 @@
<string name="pref_expert_options_summary">ご利用には注意してください</string>
<string name="title_activity_about_x">%s について</string>
<string name="title_pref_quiet_hours">消音時間</string>
<string name="title_pref_quiet_hours_start_time">開始時</string>
<string name="title_pref_quiet_hours_end_time">終了時</string>
<string name="title_pref_quiet_hours_start_time">開始時</string>
<string name="title_pref_quiet_hours_end_time">終了時</string>
<string name="title_pref_enable_quiet_hours">消音時間を有効にする</string>
<string name="pref_quiet_hours_summary">消音時間の間、通知は無音になります</string>
<string name="pref_expert_options_other">その他</string>
<string name="pref_autojoin">ブックマークと同期</string>
<string name="pref_autojoin_summary">ブックマークに従って、グループチャットに自動で参加します。</string>
<string name="toast_message_omemo_fingerprint">OMEMO フィンガープリントをクリップボードにコピーしました</string>
<string name="conference_banned">このグループチャットから追い出されています</string>
<string name="conference_banned">このグループチャットから出禁にされています</string>
<string name="conference_members_only">このグループチャットはメンバー制です</string>
<string name="conference_resource_constraint">リソース制約</string>
<string name="conference_kicked">このグループチャットから蹴り出されています</string>
@ -296,22 +300,22 @@
<string name="conference_unknown_error">既にこのグループチャットに参加していません</string>
<string name="using_account">アカウント %s を使用</string>
<string name="hosted_on">%s 上でホストされた</string>
<string name="checking_x">HTTP ホストの %s を確認中</string>
<string name="checking_x">HTTP ホストの %s を確認中</string>
<string name="not_connected_try_again">接続されていません。後でもう一度お試しください</string>
<string name="check_x_filesize">%s サイズを確認</string>
<string name="check_x_filesize_on_host">%2$s で %1$s のサイズを確認</string>
<string name="check_x_filesize">%s の大きさを確認</string>
<string name="check_x_filesize_on_host">%2$s で %1$s の大きさを確認</string>
<string name="message_options">メッセージオプション</string>
<string name="quote">引用</string>
<string name="paste_as_quote">引用として貼り付け</string>
<string name="copy_original_url">元の URL をコピー</string>
<string name="send_again">再送</string>
<string name="file_url">ファイル URL</string>
<string name="file_url">ファイル URL</string>
<string name="url_copied_to_clipboard">URL をクリップボードにコピーしました</string>
<string name="jabber_id_copied_to_clipboard">XMPP アドレスをクリップボードにコピーしました</string>
<string name="error_message_copied_to_clipboard">エラーメッセージをクリップボードにコピーしました</string>
<string name="web_address">ウェブアドレス</string>
<string name="scan_qr_code">2D バーコードをスキャン</string>
<string name="show_qr_code">2D バーコードを表示</string>
<string name="scan_qr_code">二次元バーコードをスキャン</string>
<string name="show_qr_code">二次元バーコードを表示</string>
<string name="show_block_list">ブロック一覧を表示</string>
<string name="account_details">アカウントの詳細</string>
<string name="confirm">確認</string>
@ -348,16 +352,16 @@
<string name="no_conference_server_found">グループチャットのサーバーが見つかりませんでした</string>
<string name="conference_creation_failed">グループチャットを作成できません</string>
<string name="account_image_description">アカウントのアバター</string>
<string name="copy_omemo_clipboard_description">OMEMO フィンガープリントをクリップボードにコピー</string>
<string name="copy_omemo_clipboard_description">クリップボードに OMEMO フィンガープリントをコピー</string>
<string name="regenerate_omemo_key">OMEMO 鍵を再生成</string>
<string name="clear_other_devices">デバイスをクリア</string>
<string name="clear_other_devices_desc">OMEMO の告知から他のすべてのデバイスをクリアしてもよろしいですか? お使いのデバイスが次回接続したとき、それらは自分自身を再告知しますが、その間に送信されたメッセージを受信できない場合があります。</string>
<string name="clear_other_devices">デバイスを消去</string>
<string name="clear_other_devices_desc">OMEMO の告知から他のすべてのデバイスを消去してもよろしいですか? お使いのデバイスが次回接続したとき、それらのデバイスは自分自身を再告知しますが、その間に送信されたメッセージを受信できない場合があります。</string>
<string name="error_no_keys_to_trust_server_error">この連絡先で使用可能な鍵がありません。\nサーバーから新しい鍵を取得できませんでした。連絡先のサーバーに問題がある可能性があります。</string>
<string name="error_no_keys_to_trust_presence">この連絡先で利用可能な鍵はありません。\n双方に存在サブスクリプションあることを確認してください。</string>
<string name="error_trustkeys_title">何か問題が発生しました</string>
<string name="error_no_keys_to_trust_presence">この連絡先で利用可能な鍵はありません。\n双方に存在サブスクリプションあることを確認してください。</string>
<string name="error_trustkeys_title">何か問題が発生しました</string>
<string name="fetching_history_from_server">サーバーから履歴を取得中</string>
<string name="no_more_history_on_server">サーバーにこれ以上履歴ありません</string>
<string name="updating">アップデート中…</string>
<string name="no_more_history_on_server">サーバーにこれ以上履歴ありません</string>
<string name="updating">更新中…</string>
<string name="password_changed">パスワードを変更しました!</string>
<string name="could_not_change_password">パスワードを変更できません</string>
<string name="change_password">パスワードを変更</string>
@ -381,10 +385,10 @@
<string name="remove_from_room">グループチャットから削除</string>
<string name="remove_from_channel">談話室から削除</string>
<string name="could_not_change_affiliation">%s の所属を変更できません</string>
<string name="ban_from_conference">グループチャットから追い出す</string>
<string name="ban_from_channel">談話室から追い出す</string>
<string name="removing_from_public_conference">あなたは公開談話室から %s を削除しようとしています。その唯一の手段は、そのユーザーを永久に追い出すことです。</string>
<string name="ban_now">今すぐ追い出す</string>
<string name="ban_from_conference">グループチャットから出禁に</string>
<string name="ban_from_channel">談話室から出禁に</string>
<string name="removing_from_public_conference">あなたは公開談話室から %s を削除しようとしています。その唯一の手段は、そのユーザーを永久に出禁にことです。</string>
<string name="ban_now">今すぐ出禁に</string>
<string name="could_not_change_role">%s の役割を変更できません</string>
<string name="conference_options">非公開グループチャットの環境設定</string>
<string name="channel_options">公開談話室の環境設定</string>
@ -395,18 +399,19 @@
<string name="modified_conference_options">グループチャットのオプションが変更されました!</string>
<string name="could_not_modify_conference_options">グループチャットのオプションを変更できませんでした</string>
<string name="never">なし</string>
<string name="until_further_notice">通知がるまで</string>
<string name="until_further_notice">通知がるまで</string>
<string name="snooze">スヌーズ</string>
<string name="reply">返信する</string>
<string name="mark_as_read">既読にする</string>
<string name="pref_input_options">入力</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">Enter 送信</string>
<string name="pref_enter_is_send_summary">メッセージの送信に Enter キーを使用します。このオプションが無効でも、常に Ctrl+Enter でメッセージを送信できます。</string>
<string name="pref_display_enter_key">Enter キーを表示</string>
<string name="pref_display_enter_key_summary">絵文字キーを Enter キーに変更</string>
<string name="audio">音声</string>
<string name="video">ビデオ</string>
<string name="image">画像</string>
<string name="vector_graphic">ベクター画像</string>
<string name="pdf_document">PDF 文書</string>
<string name="apk">Android アプリ</string>
<string name="vcard">連絡先</string>
@ -430,15 +435,15 @@
<string name="pref_dont_trust_system_cas_title">システムの CA を信頼しない</string>
<string name="pref_dont_trust_system_cas_summary">すべての証明書を手動で承認する必要があります</string>
<string name="pref_remove_trusted_certificates_title">証明書を削除</string>
<string name="pref_remove_trusted_certificates_summary">手動で承認した証明書を削除します</string>
<string name="toast_no_trusted_certs">手動で承認した証明書ありません</string>
<string name="pref_remove_trusted_certificates_summary">手動で承認した証明書を削除</string>
<string name="toast_no_trusted_certs">手動で承認した証明書ありません</string>
<string name="dialog_manage_certs_title">証明書を削除</string>
<string name="dialog_manage_certs_positivebutton">選択を削除</string>
<string name="dialog_manage_certs_negativebutton">キャンセル</string>
<string name="dialog_manage_certs_positivebutton">選択したものを削除</string>
<string name="dialog_manage_certs_negativebutton">中止</string>
<plurals name="toast_delete_certificates">
<item quantity="other">%d 証明書を削除しました</item>
<item quantity="other">%d個の証明書を削除しました</item>
</plurals>
<string name="pref_quick_action_summary">“送信”ボタンをクイックアクションで置き換えます</string>
<string name="pref_quick_action_summary">“送信”ボタンをクイックアクションで置き換え</string>
<string name="pref_quick_action">クイックアクション</string>
<string name="none">なし</string>
<string name="recently_used">最近使用した</string>
@ -456,24 +461,24 @@
<string name="download_failed_could_not_write_file">ダウンロードに失敗しました: ファイルに書き込みできません</string>
<string name="account_status_tor_unavailable">Tor ネットワークが利用できません</string>
<string name="account_status_bind_failure">バインド失敗</string>
<string name="account_status_host_unknown">サーバーがこのドメインに応答しません</string>
<string name="account_status_host_unknown">そのサーバーはこのドメインに責任を持ちません</string>
<string name="server_info_broken">壊れています</string>
<string name="pref_presence_settings">在席状況</string>
<string name="pref_away_when_screen_off">デバイスがロックされときは離席</string>
<string name="pref_away_when_screen_off_summary">デバイスがロックされときは離席と表示</string>
<string name="pref_dnd_on_silent_mode">サイレントモードは取込中</string>
<string name="pref_dnd_on_silent_mode_summary">デバイスがサイレントモードのは取込中と表示</string>
<string name="pref_away_when_screen_off">デバイスがロックされているときは離席</string>
<string name="pref_away_when_screen_off_summary">デバイスがロックされているときは離席と表示</string>
<string name="pref_dnd_on_silent_mode">サイレントモードのときは取込中</string>
<string name="pref_dnd_on_silent_mode_summary">デバイスがサイレントモードのときは取込中と表示</string>
<string name="pref_treat_vibrate_as_silent">バイブレートをサイレントモードとして扱う</string>
<string name="pref_treat_vibrate_as_dnd_summary">デバイスがバイブレートは取込中と表示</string>
<string name="pref_treat_vibrate_as_dnd_summary">デバイスがバイブレートのときは取込中と表示</string>
<string name="pref_show_connection_options">拡張接続設定</string>
<string name="pref_show_connection_options_summary">アカウントを設定するときにホスト名とポートの設定を表示します</string>
<string name="hostname_example">xmpp.example.com</string>
<string name="action_add_account_with_certificate">証明書でログイン</string>
<string name="unable_to_parse_certificate">証明書を解析できません</string>
<string name="mam_prefs">アーカイブ設定</string>
<string name="server_side_mam_prefs">サーバー側のアーカイブ設定</string>
<string name="fetching_mam_prefs">アーカイブ設定を取得しています。しばらくお待ちください…</string>
<string name="unable_to_fetch_mam_prefs">アーカイブ設定を取得できません</string>
<string name="mam_prefs">アーカイブ設定</string>
<string name="server_side_mam_prefs">サーバー側のアーカイブ設定</string>
<string name="fetching_mam_prefs">アーカイブ設定を取得しています。しばらくお待ちください…</string>
<string name="unable_to_fetch_mam_prefs">アーカイブ設定を取得できません</string>
<string name="captcha_required">キャプチャが要求されました</string>
<string name="captcha_hint">上の画像からテキストを入力してください</string>
<string name="certificate_chain_is_not_trusted">信頼されていない証明書チェーン</string>
@ -488,18 +493,18 @@
<string name="account_settings_hostname">ホスト名</string>
<string name="account_settings_port">ポート</string>
<string name="hostname_or_onion">サーバーまたは .onion のアドレス</string>
<string name="not_a_valid_port">これは有効なポート番号ではありません</string>
<string name="not_valid_hostname">これは有効なホスト名ではありません</string>
<string name="connected_accounts">%1$d / %2$d アカウントが接続しました</string>
<string name="not_a_valid_port">有効なポート番号ではありません</string>
<string name="not_valid_hostname">有効なホスト名ではありません</string>
<string name="connected_accounts">%2$d個中%1$d個のアカウントが接続しました</string>
<plurals name="x_messages">
<item quantity="other">%d メッセージ</item>
<item quantity="other">%d件のメッセージ</item>
</plurals>
<string name="load_more_messages">さらにメッセージをロード</string>
<string name="load_more_messages">さらにメッセージを読み込む</string>
<string name="shared_file_with_x">%s でファイル共有</string>
<string name="shared_image_with_x">%s で画像共有</string>
<string name="shared_images_with_x">%s で画像共有</string>
<string name="shared_text_with_x">%s でテキスト共有</string>
<string name="no_storage_permission">%1$s に外部ストレージへのアクセス権を付与</string>
<string name="no_storage_permission">%1$s に外部ストレージへのアクセス権を付与してください</string>
<string name="no_camera_permission">%1$s にカメラへのアクセス権を付与</string>
<string name="sync_with_contacts">連絡先と同期</string>
<string name="sync_with_contacts_long">%1$s はあなたのアドレス帳にアクセスして、あなたのXMPP 連絡先名簿と照合する権限を求めています。\nこれにより、連絡先のフルネームとアバターが表示されます。\n\n%1$s は、あなたのサーバーに何かをアップロードすることなく、あなたのアドレス帳を読み込んでローカルに照合するだけです。</string>
@ -513,7 +518,7 @@
<string name="always">常に</string>
<string name="large_images_only">大きい画像のみ</string>
<string name="battery_optimizations_enabled">電池最適化が有効</string>
<string name="battery_optimizations_enabled_explained">お使いのデバイスは、%1$s で通知の遅延やメッセージの損失につながる可能性のある、重い電池の最適化を使用しています。\nそれを無効にすることをお勧めします。</string>
<string name="battery_optimizations_enabled_explained">お使いのデバイスは、%1$s で通知の遅延やメッセージの損失につながる可能性のある、重い電池の最適化を使用しています。\nそれを無効にすることをお勧めします。</string>
<string name="battery_optimizations_enabled_dialog">お使いのデバイスは、%1$s で通知の遅延やメッセージの損失につながる可能性のある、重い電池の最適化を使用しています。\n\n今、それらを無効にするように求められます。</string>
<string name="disable">無効</string>
<string name="selection_too_large">選択した範囲が大きすぎます</string>
@ -528,7 +533,7 @@
<string name="share_uri_with">…で URI を共有</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy は人気の XMPP クライアント Conversations の派生で、連絡先の自動検出機能を備えています。<br><br>電話番号を入力して登録すると、アドレス帳に登録されている電話番号をもとに、Quicksyが自動的に連絡先を提案します。<br><br>登録すると、<a href="https://quicksy.im/#privacy">我々のプライバシーポリシー</a>に同意することになります。]]></string>
<string name="agree_and_continue">同意して続行</string>
<string name="magic_create_text">conversations.im のアカウント作成のための指南が設定されています。¹\nconversations.im をプロバイダーとして選択した場合、あなたの完全なXMPPアドレスを与えることで、他のプロバイダーのユーザーと連絡をとることができます。</string>
<string name="magic_create_text">conversations.im 上にアカウントを作成する設定の指南です。¹\nconversations.im をプロバイダーとして選択した場合、あなたの完全な XMPP アドレスを他のプロバイダーのユーザーに示すことで、その人と連絡をとることができます。</string>
<string name="your_full_jid_will_be">あなたの完全なXMPPアドレスは: %s</string>
<string name="create_account">アカウントを作成</string>
<string name="use_own_provider">独自のプロバイダーを使用する</string>
@ -538,8 +543,8 @@
<string name="status_message">ステータスメッセージ</string>
<string name="presence_chat">いつでもチャットできます</string>
<string name="presence_online">オンライン</string>
<string name="presence_away">離席</string>
<string name="presence_xa">利用不可</string>
<string name="presence_away">離席</string>
<string name="presence_xa">不在</string>
<string name="presence_dnd">取込中</string>
<string name="secure_password_generated">安全なパスワードが生成されました</string>
<string name="device_does_not_support_battery_op">お使いのデバイスは電池最適化の停止をサポートしていません</string>
@ -553,13 +558,13 @@
<string name="gp_medium"></string>
<string name="gp_long"></string>
<string name="pref_broadcast_last_activity">ブロードキャストを使用</string>
<string name="pref_broadcast_last_activity_summary">Conversations を使用するとき、連絡先に知らせましょう</string>
<string name="pref_broadcast_last_activity_summary">Conversations を使用するとき、連絡先に知らせましょう</string>
<string name="pref_privacy">プライバシー</string>
<string name="pref_theme_options">テーマ</string>
<string name="pref_theme_options_summary">カラーパレットの選択</string>
<string name="pref_theme_automatic">自動</string>
<string name="pref_theme_light">ライト</string>
<string name="pref_theme_dark">ダーク</string>
<string name="pref_theme_light"></string>
<string name="pref_theme_dark"></string>
<string name="pref_use_green_background">緑の背景</string>
<string name="pref_use_green_background_summary">受信したメッセージに緑の背景を使用します</string>
<string name="unable_to_connect_to_keychain">OpenKeychain に接続できません</string>
@ -586,28 +591,28 @@
<string name="show_error_message">エラーメッセージを表示</string>
<string name="error_message">エラーメッセージ</string>
<string name="data_saver_enabled">データセーバーを有効にしました</string>
<string name="data_saver_enabled_explained">お使いのオペレーティングシステムは、%1$s がバックグラウンドのときにインターネットにアクセスすることを制限しています。新しいメッセージの通知を受信するには、“データセーバー”がオンになっているとき、%1$s に無制限のアクセスを許可する必要があります。\n%1$s は可能なときにデータを保存するための努力をします。</string>
<string name="data_saver_enabled_explained">お使いのオペレーティングシステムは、%1$s がバックグラウンドのときにインターネットにアクセスすることを制限しています。新しいメッセージの通知を受信するには、“データセーバー”がオンならば、%1$s に無制限のアクセスを許可する必要があります。\n%1$s は可能なときにデータを保存するための努力をします。</string>
<string name="device_does_not_support_data_saver">お使いのデバイスは、%1$s のデータセーバーを無効にできません。</string>
<string name="error_unable_to_create_temporary_file">一時ファイルを作成できません</string>
<string name="this_device_has_been_verified">このデバイスは検証済です</string>
<string name="copy_fingerprint">フィンガープリントをコピー</string>
<string name="all_omemo_keys_have_been_verified">所有するすべての OMEMO 鍵を確認完了</string>
<string name="all_omemo_keys_have_been_verified">所有するすべての OMEMO 鍵を検証完了</string>
<string name="barcode_does_not_contain_fingerprints_for_this_conversation">バーコードに、この会話のフィンガープリントが含まれていません。</string>
<string name="verified_fingerprints">フィンガープリントを検証しました</string>
<string name="use_camera_icon_to_scan_barcode">カメラを使用して連絡先のバーコードをスキャンします</string>
<string name="please_wait_for_keys_to_be_fetched">鍵が取得されるのをお待ちください</string>
<string name="share_as_barcode">バーコードとして共有</string>
<string name="share_as_uri">XMPP URI として共有</string>
<string name="share_as_http">HTTP リンクとして共有</string>
<string name="share_as_barcode">バーコード共有</string>
<string name="share_as_uri">XMPP URI 共有</string>
<string name="share_as_http">HTTP リンク共有</string>
<string name="pref_blind_trust_before_verification">検証前に白紙信託する</string>
<string name="pref_blind_trust_before_verification_summary">認証されていない連絡先からの新規デバイスを信頼するが、認証されている連絡先からの新規デバイスについては手動での確認を求める。</string>
<string name="blindly_trusted_omemo_keys">OMEMO 鍵を盲目的に信用していた。つまり、他の人かもしれないし、誰かが盗聴しているかもしれない。</string>
<string name="not_trusted">信頼されていない</string>
<string name="invalid_barcode">不正な 2D バーコード</string>
<string name="pref_clean_cache_summary">キャッシュフォルダーをクリアします (カメラアプリで使用)</string>
<string name="pref_clean_cache">キャッシュをクリア</string>
<string name="pref_clean_private_storage">プライベートストレージをクリア</string>
<string name="pref_clean_private_storage_summary">ファイルが保存されているプライベートストレージをクリアします (サーバーから再ダウンロードできます)</string>
<string name="invalid_barcode">不正な二次元バーコード</string>
<string name="pref_clean_cache_summary">キャッシュフォルダを消去します (カメラアプリで使用)</string>
<string name="pref_clean_cache">キャッシュを消去</string>
<string name="pref_clean_private_storage">プライベートストレージを消去</string>
<string name="pref_clean_private_storage_summary">ファイルが保存されているプライベートストレージを消去します (サーバーから再ダウンロードできます)</string>
<string name="i_followed_this_link_from_a_trusted_source">信頼できるソースからこのリンクをたどりました</string>
<string name="verifying_omemo_keys_trusted_source">リンクをクリックした後、%1$s の OMEMO 鍵を検証しようとしています。 これは、%2$s がこのリンクを公開した、信頼できるソースからこのリンクをたどった場合にのみ安全です。</string>
<string name="verify_omemo_keys">OMEMO 鍵を検証</string>
@ -693,7 +698,7 @@
<string name="pref_omemo_setting_summary_default_off">新しい会話をするためには、OMEMOを明示的にオンにする必要があります。</string>
<string name="create_shortcut">ショートカットを作成</string>
<string name="pref_font_size">フォントの大きさ</string>
<string name="pref_font_size_summary">このアプリで使用される相対フォントサイズ</string>
<string name="pref_font_size_summary">このアプリで使用される相対的なフォントの大きさ</string>
<string name="default_on">デフォルトでオン</string>
<string name="default_off">デフォルトでオフ</string>
<string name="small"></string>
@ -702,10 +707,10 @@
<string name="not_encrypted_for_this_device">このデバイス向けにメッセージは暗号化されませんでした。</string>
<string name="omemo_decryption_failed">OMEMO メッセージの復号に失敗しました。</string>
<string name="undo">元に戻す</string>
<string name="location_disabled">場所の共有が無効</string>
<string name="location_disabled">位置の共有が無効</string>
<string name="action_fix_to_location">位置を固定</string>
<string name="action_unfix_from_location">位置を固定しない</string>
<string name="action_copy_location">場所をコピー</string>
<string name="action_copy_location">位置をコピー</string>
<string name="action_share_location">位置を共有</string>
<string name="title_activity_share_location">位置を共有</string>
<string name="title_activity_show_location">位置を表示</string>
@ -716,13 +721,13 @@
<string name="search_messages">メッセージを検索</string>
<string name="gif">GIF</string>
<string name="view_conversation">会話を表示</string>
<string name="pref_use_share_location_plugin">場所共有プラグイン</string>
<string name="pref_use_share_location_plugin_summary">場所共有プラグインの代わりに、組み込みの地図を使う</string>
<string name="pref_use_share_location_plugin">位置共有プラグイン</string>
<string name="pref_use_share_location_plugin_summary">位置共有プラグインの代わりに、組み込みの地図を使う</string>
<string name="copy_link">ウェブアドレスをコピー</string>
<string name="copy_jabber_id">XMPP アドレスをコピー</string>
<string name="p1_s3_filetransfer">S3 の HTTP ファイル共有</string>
<string name="pref_start_search">直接検索</string>
<string name="pref_start_search_summary">‘会話開始’画面でキーボードを開き、検索フィールドにカーソルを置きます</string>
<string name="pref_start_search_summary">‘会話開始’画面でキーボードを開き、検索フィールドにカーソルを置きます</string>
<string name="group_chat_avatar">グループチャットのアバター</string>
<string name="host_does_not_support_group_chat_avatars">ホストはグループチャットのアバターをサポートしていません</string>
<string name="only_the_owner_can_change_group_chat_avatar">所有者だけが、グループチャットのアバターを変更可能です</string>
@ -733,7 +738,7 @@
<string name="create_dialog_group_chat_name">グループチャット名</string>
<string name="conference_destroyed">このグループチャットは破棄されました</string>
<string name="foreground_service_channel_name">フォアグラウンドサービス</string>
<string name="foreground_service_channel_description">この通知カテゴリーは %1$s が実行されていることを表示する、永続的な通知を表示するために使用されます。</string>
<string name="foreground_service_channel_description">この通知カテゴリーは %1$s が実行ていることを表示する、永続的な通知を表示するために使用されます。</string>
<string name="notification_group_status_information">ステータス情報</string>
<string name="error_channel_name">接続の問題</string>
<string name="error_channel_description">この通知カテゴリーは、アカウントへの接続に問題があった場合に、通知を表示するために使用されます。</string>
@ -742,6 +747,7 @@
<string name="messages_channel_name">メッセージ</string>
<string name="incoming_calls_channel_name">着信</string>
<string name="ongoing_calls_channel_name">発信</string>
<string name="silent_messages_channel_name">サイレントメッセージ</string>
<string name="silent_messages_channel_description">この通知グループは、音を鳴らしてはいけない通知を表示するために使用します。例えば、他のデバイスでアクティブになっているときなどです (猶予期間)。</string>
<string name="delivery_failed_channel_name">配信に失敗</string>
<string name="pref_message_notification_settings">メッセージ通知設定</string>
@ -811,7 +817,7 @@
<string name="ebook">電子書籍</string>
<string name="video_original">原物 (非圧縮)</string>
<string name="open_with">…で開く</string>
<string name="set_profile_picture">Conversations プロフィール写真</string>
<string name="set_profile_picture">Conversations プロフィール画像</string>
<string name="choose_account">アカウントを選択</string>
<string name="restore_backup">バックアップを復元</string>
<string name="restore">復元</string>
@ -849,11 +855,11 @@
<string name="search_participants">参加者を検索</string>
<string name="file_too_large">ファイルが大きすぎます</string>
<string name="attach">添付</string>
<string name="discover_channels">談話室発見</string>
<string name="discover_channels">談話室発見</string>
<string name="search_channels">談話室を検索</string>
<string name="channel_discovery_opt_in_title">プライバシー侵害の可能性あり!</string>
<string name="channel_discover_opt_in_message"><![CDATA[談話室発見は、第三者のサービスである<a href="https://search.jabber.network">search.jabber.networkを利用します。<br><br>この機能を使うと、あなたののIPアドレスや検索キーワードがそのサービスに送信されます。詳しくは、<a href="https://search.jabber.network/privacy">プライバシーポリシー</a>をご覧ください。]]></string>
<string name="i_already_have_an_account">私は既にアカウントを持っています</string>
<string name="channel_discover_opt_in_message"><![CDATA[談話室発見は、第三者のサービスである<a href=\"https://search.jabber.network\">search.jabber.network</a>を利用します。<br><br>この機能を使うと、あなたののIPアドレスや検索キーワードがそのサービスに送信されます。詳しくは、<a href=\"https://search.jabber.network/privacy\">プライバシーポリシー</a>をご覧ください。]]></string>
<string name="i_already_have_an_account">既にアカウントを持っています</string>
<string name="add_existing_account">存在するアカウントを追加</string>
<string name="register_new_account">新しいアカウントを登録</string>
<string name="this_looks_like_a_domain">これはドメインアドレスのようです</string>
@ -873,7 +879,7 @@
<string name="jabber_network">jabber.network</string>
<string name="local_server">ローカルサーバー</string>
<string name="pref_channel_discovery_summary">ほとんどのユーザーは、公開されている XMPP エコシステム全体からより良い提案を得るために、jabber.networkを選択するはずです。</string>
<string name="pref_channel_discovery">談話室発見方法</string>
<string name="pref_channel_discovery">談話室発見方法</string>
<string name="please_enable_an_account">アカウントを有効にしてください</string>
<string name="make_call">通話をする</string>
<string name="rtp_state_incoming_call">通話着信</string>
@ -937,4 +943,7 @@
<string name="server_does_not_support_easy_onboarding_invites">サーバーは招待をサポートしていません</string>
<string name="no_active_accounts_support_this">この機能をサポートするアクティブなアカウントがありません</string>
<string name="backup_started_message">バックアップを開始しました。 バックアップが完了すると通知が届きます。</string>
<string name="unable_to_enable_video">映像を有効化できません。</string>
<string name="plain_text_document">プレーンテキスト文書</string>
</resources>

View File

@ -86,6 +86,7 @@
<string name="pref_accept_files">ഫയലുകൾ സ്വീകരിക്കൂ</string>
<string name="pref_attachments">അറ്റാച്ചുമെന്റുകൾ</string>
<string name="pref_notification_settings">അറിയിപ്പ്</string>
<string name="pref_vibrate">വൈബ്രേറ്റ് ചെയ്യൂ</string>
<string name="pref_led">LED അറിയിപ്പ്</string>
<string name="pref_ringtone">റിംഗ്‌ടോൺ</string>
<string name="pref_notification_sound">അറിയിപ്പ് ശബ്‌ദം</string>
@ -106,8 +107,11 @@
<string name="account_status_online">ഓൺലൈൻ</string>
<string name="account_status_offline">ഓഫ്‌ലൈൻ</string>
<string name="account_status_not_found">സെർവർ കണ്ടെത്തിയില്ല</string>
<string name="account_status_no_internet">കണക്റ്റിവിറ്റി ഇല്ല</string>
<string name="account_status_regis_fail">രജിസ്ട്രേഷൻ പരാജയപ്പെട്ടു</string>
<string name="account_status_regis_conflict">ഉപയോക്തൃനാമം ഇതിനകം നിലവിലുണ്ട്</string>
<string name="account_status_regis_success">രജിസ്ട്രേഷൻ പൂർത്തിയായി</string>
<string name="account_status_policy_violation">നയ ലംഘനം</string>
<string name="encryption_choice_unencrypted">സുരക്ഷിതമല്ലാത്ത</string>
<string name="encryption_choice_otr">OTR</string>
<string name="encryption_choice_pgp">OpenPGP</string>
@ -152,6 +156,7 @@
<string name="select">തിരഞ്ഞെടുക്കൂ</string>
<string name="contact_already_exists">കോൺ‌ടാക്റ്റ് ഇതിനകം നിലവിലുണ്ട്</string>
<string name="join">ചേരുക</string>
<string name="save_as_bookmark">അടയാളക്കുറിപ്പായി സംരക്ഷിക്കൂ</string>
<string name="destroy_room">ഗ്രൂപ്പ് ചാറ്റ് നശിപ്പിക്കൂ</string>
<string name="destroy_channel">ചാനൽ നശിപ്പിക്കൂ</string>
<string name="topic">വിഷയം</string>
@ -166,6 +171,7 @@
<string name="next">അടുത്തത്</string>
<string name="server_info_session_established">സെഷൻ സ്ഥാപിച്ചു</string>
<string name="skip">ഒഴിവാക്കൂ</string>
<string name="enable">പ്രാപ്തമാക്കൂ</string>
<string name="conference_requires_password">ഗ്രൂപ്പ് ചാറ്റിന് രഹസ്യവാക്ക് ആവശ്യമാണ്</string>
<string name="enter_password">രഹസ്യവാക്ക് നൽകുക</string>
<string name="request_now">ഇപ്പോൾ അഭ്യർത്ഥിക്കുക</string>
@ -175,6 +181,7 @@
<string name="title_activity_about_x">%s-നെ കുറിച്ച്</string>
<string name="title_pref_quiet_hours_start_time">ആരംഭ സമയം</string>
<string name="pref_expert_options_other">മറ്റുള്ളവ</string>
<string name="conference_banned">ഈ ഗ്രൂപ്പ് ചാറ്റിൽ നിന്ന് നിങ്ങളെ നിരോധിച്ചിരിക്കുന്നു</string>
<string name="conference_kicked">നിങ്ങളെ ഗ്രൂപ്പ് ചാറ്റിൽ നിന്ന് പുറത്താക്കി</string>
<string name="using_account">%s അക്കൗണ്ട് ഉപയോഗിക്കുന്നു</string>
<string name="check_x_filesize">%s-ന്റെ വലുപ്പം പരിശോധിക്കൂ</string>
@ -214,6 +221,7 @@
<string name="ban_from_channel">ചാനലിൽ നിന്ന് നിരോധിക്കൂ</string>
<string name="ban_now">ഇപ്പോൾ നിരോധിക്കൂ</string>
<string name="members_only">സ്വകാര്യ, അംഗങ്ങൾ മാത്രം</string>
<string name="you_are_not_participating">നിങ്ങൾ പങ്കെടുക്കുന്നില്ല</string>
<string name="reply">മറുപടി</string>
<string name="mark_as_read">വായിച്ചതായി കാണിക്കൂ</string>
<string name="pref_enter_is_send">എന്റെർ കീ അയയ്ക്കും</string>
@ -222,6 +230,10 @@
<string name="image">ചിത്രം</string>
<string name="sending_x_file">%s അയയ്ക്കുന്നു</string>
<string name="contact_is_typing">%s ടൈപ്പുചെയ്യുന്നു…</string>
<string name="contacts_are_typing">%s ടൈപ്പുചെയ്യുന്നു…</string>
<string name="send_location">ലൊക്കേഷൻ അയയ്‌ക്കുക</string>
<string name="show_location">ലൊക്കേഷൻ കാണിക്കൂ</string>
<string name="location">ലൊക്കേഷൻ</string>
<string name="dialog_manage_certs_negativebutton">റദ്ദാക്കൂ</string>
<string name="recently_used">സമീപകാലത്ത് ഉപയോഗിച്ചത്</string>
<string name="search_contacts">കോൺ‌ടാക്റ്റുകൾ തിരയുക</string>
@ -250,25 +262,40 @@
<string name="presence_online">ഓൺലൈൻ</string>
<string name="presence_xa">ലഭ്യമല്ല</string>
<string name="presence_dnd">തിരക്കിലാണ്</string>
<string name="choose_participants">പങ്കെടുക്കുന്നവരെ തിരഞ്ഞെടുക്കുക</string>
<string name="creating_conference">ഗ്രൂപ്പ് ചാറ്റ് സൃഷ്ടിക്കുന്നു…</string>
<string name="invite_again">വീണ്ടും ക്ഷണിക്കൂ</string>
<string name="pref_privacy">സ്വകാര്യത</string>
<string name="pref_theme_options">രൂപഭംഗി</string>
<string name="type_pc">കമ്പ്യൂട്ടർ</string>
<string name="type_phone">മൊബൈൽ ഫോൺ</string>
<string name="me">ഞാൻ</string>
<string name="allow">അനുവദിക്കൂ</string>
<plurals name="months">
<item quantity="one">%d മാസം</item>
<item quantity="other">%d മാസം</item>
</plurals>
<string name="block_entire_domain">മുഴുവൻ മേഖലയും തടയുക</string>
<string name="online_right_now">ഇപ്പോൾ സജീവം</string>
<string name="open_website">വെബ്സൈറ്റ് തുറക്കൂ</string>
<string name="today">ഇന്ന്</string>
<string name="yesterday">ഇന്നലെ</string>
<string name="copy_to_clipboard">ക്ലിപ്പ്ബോർഡിലേയ്ക്ക് പകർത്തുക</string>
<string name="message">സന്ദേശം</string>
<string name="once">ഒരിക്കൽ</string>
<string name="share">പങ്കിടുക</string>
<string name="search_messages">സന്ദേശങ്ങൾ തിരയുക</string>
<string name="gif">GIF</string>
<string name="nickname">വിളിപ്പേര്</string>
<string name="group_chat_name">പേര്</string>
<string name="create_dialog_group_chat_name">ഗ്രൂപ്പ് ചാറ്റിന്റെ പേര്</string>
<string name="notification_group_messages">സന്ദേശങ്ങൾ</string>
<string name="notification_group_calls">കോളുകൾ</string>
<string name="messages_channel_name">സന്ദേശങ്ങൾ</string>
<string name="silent_messages_channel_name">നിശബ്‌ദ സന്ദേശങ്ങൾ</string>
<string name="group_chat_members">പങ്കെടുക്കുന്നവർ</string>
<string name="cancelled">റദ്ദാക്കി</string>
<string name="invalid_country_code">രാജ്യ കോഡ് തെറ്റാണ്</string>
<string name="choose_a_country">ഒരു രാജ്യം തിരഞ്ഞെടുക്കൂ</string>
<string name="phone_number">ഫോൺ നമ്പർ</string>
<string name="verify_your_phone_number">നിങ്ങളുടെ ഫോൺ നമ്പർ ഉറപ്പാക്കൂ</string>
@ -283,8 +310,25 @@
<string name="enter_your_name">നിങ്ങളുടെ പേര് നൽകുക</string>
<string name="choose_account">അക്കൗണ്ട് തിരഞ്ഞെടുക്കൂ</string>
<string name="enter_jabber_id">XMPP വിലാസം നൽകുക</string>
<string name="create_group_chat">ഗ്രൂപ്പ് ചാറ്റ് സൃഷ്ടിക്കുക</string>
<string name="join_public_channel">പൊതു ചാനലിൽ ചേരുക</string>
<string name="xmpp_address">XMPP വിലാസം</string>
<string name="file_too_large">ഫയൽ വളരെ വലുതാണ്</string>
<string name="search_channels">ചാനലുകൾ തിരയുക</string>
<string name="add_existing_account">നിലവിലുള്ള അക്കൗണ്ട് ചേർക്കുക</string>
<string name="unable_to_perform_this_action">ഈ പ്രവർത്തനം നടത്താൻ കഴിഞ്ഞില്ല</string>
<string name="open_join_dialog">പൊതു ചാനലിൽ ചേരുക…</string>
<string name="jabber_network">jabber.network</string>
<string name="rtp_state_accepting_call">കോൾ സ്വീകരിക്കുന്നു</string>
<string name="rtp_state_ending_call">കോൾ അവസാനിപ്പിക്കുന്നു</string>
<string name="incoming_call">ഇൻകമിംഗ് കോൾ</string>
<string name="help">സഹായം</string>
<string name="microphone_unavailable">നിങ്ങളുടെ മൈക്രോഫോൺ ലഭ്യമല്ല</string>
<string name="gpx_track">GPX ട്രാക്ക്</string>
<string name="search_all_conversations">എല്ലാ സംഭാഷണങ്ങളും</string>
<string name="search_this_conversation">ഈ സംഭാഷണം</string>
<string name="your_avatar">നിങ്ങളുടെ അവതാർ</string>
<string name="more_options">കൂടുതൽ ഓപ്ഷനുകൾ</string>
<string name="invite_to_app">Conversations-ലേക്ക് ക്ഷണിക്കുക</string>
<string name="backup_started_message">ബാക്കപ്പ് ആരംഭിച്ചു. അത് പൂർത്തിയായിക്കഴിഞ്ഞാൽ നിങ്ങൾക്ക് ഒരു അറിയിപ്പ് ലഭിക്കും.</string>
</resources>

View File

@ -138,6 +138,8 @@
<string name="pref_never_send_crash_summary">Wysyłając nam ślady stosu pomagasz w rozwoju</string>
<string name="pref_confirm_messages">Potwierdzenia wiadomości</string>
<string name="pref_confirm_messages_summary">Zezwól na wysyłanie do osób z twojej listy kontaktów informacji o tym, kiedy otrzymałeś i przeczytałeś wiadomość od nich</string>
<string name="pref_prevent_screenshots">Zapobiegaj zrzutom ekranu</string>
<string name="pref_prevent_screenshots_summary">Ukryj zawartość aplikacji w podglądzie aplikacji oraz zablokuj zrzuty ekranu </string>
<string name="pref_ui_options">UI</string>
<string name="openpgp_error">OpenKeychain zgłosiło błąd.</string>
<string name="bad_key_for_encryption">Zły klucz szyfrowania.</string>
@ -156,6 +158,7 @@
<string name="error_file_not_found">Nie odnaleziono pliku</string>
<string name="error_io_exception">Ogólny błąd wejścia/wyjścia</string>
<string name="error_security_exception_during_image_copy">Aplikacja użyta do wyboru obrazu nie zezwoliła na odczyt pliku.\n\n<small>Wybierz obraz przy użyciu innego menedżera plików</small></string>
<string name="error_security_exception">Aplikacja której użyłeś do udostępnienia pliku nie dostarczyła odpowiednich uprawnień. </string>
<string name="account_status_unknown">Nieznany</string>
<string name="account_status_disabled">Tymczasowo wyłączono</string>
<string name="account_status_online">Połączono</string>
@ -170,6 +173,7 @@
<string name="account_status_regis_not_sup">Ten serwer nie wspiera rejestracji</string>
<string name="account_status_regis_invalid_token">Nieprawidłowy żeton rejestracji</string>
<string name="account_status_tls_error">Nie powiodła się negocjacja TLS</string>
<string name="account_status_tls_error_domain">Nie można zweryfikować tej domeny</string>
<string name="account_status_policy_violation">Naruszenie zasad</string>
<string name="account_status_incompatible_server">Serwer niekompatybilny</string>
<string name="account_status_stream_error">Błąd strumienia</string>
@ -418,6 +422,7 @@
<string name="audio">plik audio</string>
<string name="video">plik wideo</string>
<string name="image">obraz</string>
<string name="vector_graphic">grafika wektorowa</string>
<string name="pdf_document">Dokument PDF</string>
<string name="apk">Aplikacja Androida</string>
<string name="vcard">Kontakt</string>
@ -924,7 +929,7 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż
<string name="rtp_state_connected">Połączony</string>
<string name="rtp_state_accepting_call">Akceptowanie połączenia</string>
<string name="rtp_state_ending_call">Kończenie połączenia</string>
<string name="answer_call">Odbierz</string>
<string name="answer_call">Połącz</string>
<string name="dismiss_call">Odrzuć</string>
<string name="rtp_state_finding_device">Wyszukiwanie urządzeń</string>
<string name="rtp_state_ringing">Dzwonienie</string>
@ -933,6 +938,7 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż
<string name="rtp_state_connectivity_lost_error">Utracono połączenie</string>
<string name="rtp_state_retracted">Anulowane połączenie</string>
<string name="rtp_state_application_failure">Błąd aplikacji</string>
<string name="rtp_state_security_error">Problem z weryfikacją </string>
<string name="hang_up">Rozłącz</string>
<string name="ongoing_call">Połączenie wychodzące</string>
<string name="ongoing_video_call">Wideorozmowa wychodząca</string>
@ -987,4 +993,7 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż
<string name="server_does_not_support_easy_onboarding_invites">Serwer nie wspiera tworzenia zaproszeń</string>
<string name="no_active_accounts_support_this">Nie ma aktywnych kont wspierających tę funkcję</string>
<string name="backup_started_message">Tworzenie kopii zapasowej się rozpoczęło. Dostaniesz powiadomienie kiedy się zakończy. </string>
<string name="unable_to_enable_video">Nie można włączyć wideo. </string>
<string name="plain_text_document">Dokument zwykłego tekstu</string>
</resources>

View File

@ -132,6 +132,8 @@
<string name="pref_never_send_crash_summary">Ao enviar os stack traces você está colaborando com o desenvolvimento</string>
<string name="pref_confirm_messages">Confirmação de mensagens</string>
<string name="pref_confirm_messages_summary">Permite que seus contatos saibam quando você recebeu e leu as mensagens deles.</string>
<string name="pref_prevent_screenshots">Impedir capturas de tela</string>
<string name="pref_prevent_screenshots_summary">Esconde o conteúdo do app no alternador de apps e bloqueia capturas de tela</string>
<string name="pref_ui_options">IU</string>
<string name="openpgp_error">O OpenKeychain produziu um erro.</string>
<string name="bad_key_for_encryption">Chave ruim para a criptografia</string>
@ -414,6 +416,7 @@
<string name="audio">áudio</string>
<string name="video">vídeo</string>
<string name="image">imagem</string>
<string name="vector_graphic">gráfico vetorial</string>
<string name="pdf_document">Documento PDF</string>
<string name="apk">Aplicativo Android</string>
<string name="vcard">Contato</string>
@ -912,6 +915,7 @@
<string name="rtp_state_connectivity_lost_error">Conexão perdida</string>
<string name="rtp_state_retracted">Chamada rejeitada</string>
<string name="rtp_state_application_failure">Falha no aplicativo</string>
<string name="rtp_state_security_error">Problema de verificação</string>
<string name="hang_up">Desligar</string>
<string name="ongoing_call">Chamada em andamento</string>
<string name="ongoing_video_call">Chamada de vídeo em andamento</string>
@ -963,4 +967,6 @@
<string name="no_active_accounts_support_this">Nenhuma conta ativa suporta esse recurso</string>
<string name="backup_started_message">O backup foi iniciado. Você receberá uma notificação assim que ele for concluído.</string>
<string name="unable_to_enable_video">Não foi possível habilitar o vídeo.</string>
<string name="plain_text_document">Documento em texto puro</string>
</resources>

View File

@ -135,6 +135,8 @@
<string name="pref_never_send_crash_summary">Trimițând date despre erori ajutați la continuarea dezvoltării aplicației</string>
<string name="pref_confirm_messages">Confirmă mesajele</string>
<string name="pref_confirm_messages_summary">Contactele sunt notificate atunci când ați primit un mesaj și l-ați citit</string>
<string name="pref_prevent_screenshots">Previne captura ecranului</string>
<string name="pref_prevent_screenshots_summary">Ascunde conținutul în managerul de aplicații și blochează captura de ecran</string>
<string name="pref_ui_options">Opțiuni interfață</string>
<string name="openpgp_error">OpenKeychain a raportat o eroare.</string>
<string name="bad_key_for_encryption">Cheie invalidă pentru criptare.</string>
@ -153,6 +155,7 @@
<string name="error_file_not_found">Fișierul nu a fost găsit</string>
<string name="error_io_exception">Eroare I/O generala. Poate ați rămas fără spațiu liber?</string>
<string name="error_security_exception_during_image_copy">Aplicația folosită pentru selecția acestei imagini nu a oferit destule permisiuni pentru a putea citii fișierul.\n\n<small>Folosiți un alt manager de fișiere pentru a alege o imagine</small></string>
<string name="error_security_exception">Aplicația pe care ați folosit-o pentru a partaja acest fișier nu a furnizat suficiente permisiuni.</string>
<string name="account_status_unknown">Necunoscut</string>
<string name="account_status_disabled">Dezactivat temporar</string>
<string name="account_status_online">Conectat</string>
@ -167,6 +170,7 @@
<string name="account_status_regis_not_sup">Serverul nu permite înregistrarea</string>
<string name="account_status_regis_invalid_token">Simbol de înregistrare invalid</string>
<string name="account_status_tls_error">Negociere TLS eşuată</string>
<string name="account_status_tls_error_domain">Domeniul nu se poate verifica</string>
<string name="account_status_policy_violation">Încălcare condiții furnizare serviciu</string>
<string name="account_status_incompatible_server">Server incompatibil</string>
<string name="account_status_stream_error">Eroare de date</string>
@ -415,6 +419,7 @@
<string name="audio">audio</string>
<string name="video">video</string>
<string name="image">imagine</string>
<string name="vector_graphic">grafic vectorial</string>
<string name="pdf_document">document PDF</string>
<string name="apk">Aplicație Android</string>
<string name="vcard">Contact</string>
@ -921,6 +926,7 @@
<string name="rtp_state_connectivity_lost_error">Conexiune pierdută</string>
<string name="rtp_state_retracted">Apel anulat</string>
<string name="rtp_state_application_failure">Eroare de aplicație</string>
<string name="rtp_state_security_error">Problemă la verificare</string>
<string name="hang_up">Închide</string>
<string name="ongoing_call">Apel în curs</string>
<string name="ongoing_video_call">Apel video în curs</string>
@ -973,4 +979,7 @@
<string name="server_does_not_support_easy_onboarding_invites">Serverul nu suportă generarea de invitații</string>
<string name="no_active_accounts_support_this">Nici un cont activ nu suporta această caracteristică</string>
<string name="backup_started_message">Se creează copia de siguranță. Veți primi o notificare când acesta este completă.</string>
<string name="unable_to_enable_video">Nu s-a putut activa camera video.</string>
<string name="plain_text_document">Document text</string>
</resources>

View File

@ -156,6 +156,7 @@
<string name="error_file_not_found">Файл не найден</string>
<string name="error_io_exception">Общая ошибка ввода/вывода. Возможно, на устройстве недостаточно свободного места?</string>
<string name="error_security_exception_during_image_copy">У приложения, которым вы выбрали это изображение, недостаточно прав, чтобы прочитать этот файл.\n\n<small>Пожалуйста, используйте другой файловый менеджер, чтобы выбрать это изображение</small>.</string>
<string name="error_security_exception">Приложение, которое вы использовали для публикации этого файла, не предоставило достаточно разрешений.</string>
<string name="account_status_unknown">Неизвестен</string>
<string name="account_status_disabled">Временно отключён</string>
<string name="account_status_online">В сети</string>
@ -170,6 +171,7 @@
<string name="account_status_regis_not_sup">Сервер не поддерживает возможность регистрации</string>
<string name="account_status_regis_invalid_token">Неправильный токен регистрации</string>
<string name="account_status_tls_error">Не удалось согласовать TLS</string>
<string name="account_status_tls_error_domain">Домен не поддается проверке</string>
<string name="account_status_policy_violation">Нарушение правил</string>
<string name="account_status_incompatible_server">Несовместимый сервер</string>
<string name="account_status_stream_error">Ошибка потока</string>
@ -986,4 +988,5 @@
<string name="server_does_not_support_easy_onboarding_invites">Сервер не поддерживает создание приглашений</string>
<string name="no_active_accounts_support_this">Ни один активный аккаунт не поддерживает эту функцию</string>
<string name="backup_started_message">Резервное копирование было начато. Вы получите уведомление, как только оно будет завершено. </string>
<string name="unable_to_enable_video">Невозможно включить видео.</string>
</resources>

View File

@ -3,38 +3,69 @@
<string name="action_settings">Nastavenia</string>
<string name="action_add">Nová konverzácia</string>
<string name="action_accounts">Nastavenie účtov</string>
<string name="action_account">Nastaviť účet</string>
<string name="action_end_conversation">Zavrieť rozhovor</string>
<string name="action_contact_details">Detaily kontaktu</string>
<string name="action_muc_details">Detaily skupinového rozhovoru</string>
<string name="channel_details">Detaily kanála</string>
<string name="action_add_account">Pridať účet</string>
<string name="action_edit_contact">Upraviť meno</string>
<string name="action_add_phone_book">Pridať do kontaktov</string>
<string name="action_delete_contact">Vymazať zo zoznamu</string>
<string name="action_block_contact">Zablokovať kontakt</string>
<string name="action_unblock_contact">Odblokovať kontakt</string>
<string name="action_block_domain">Zablokovať doménu</string>
<string name="action_unblock_domain">Odblokovať doménu</string>
<string name="action_block_participant">Zablokovať účastníka</string>
<string name="action_unblock_participant">Odblokovať účastníka</string>
<string name="title_activity_manage_accounts">Nastavenie účtov</string>
<string name="title_activity_settings">Nastavenia</string>
<string name="title_activity_sharewith">Zdieľať s konverzáciou</string>
<string name="title_activity_start_conversation">Začať konverzáciu</string>
<string name="title_activity_choose_contact">Vybrať Kontakt</string>
<string name="title_activity_choose_contacts">Vyberte Kontakty</string>
<string name="title_activity_share_via_account">Zdieľať cez účet</string>
<string name="title_activity_block_list">Zablokovať zoznam</string>
<string name="just_now">práve teraz</string>
<string name="minute_ago">pred 1 minútou</string>
<string name="minutes_ago">pred %d minútami</string>
<plurals name="x_unread_conversations">
<item quantity="one">%dneprečítaný rozhovor</item>
<item quantity="few">%dneprečítaných rozhovorov</item>
<item quantity="many">%dneprečítaných rozhovorov</item>
<item quantity="other">%dneprečítaných rozhovorov</item>
</plurals>
<string name="sending">posielam...</string>
<string name="message_decrypting">Dešifrujem správu. Čakajte, prosím…</string>
<string name="pgp_message">OpenPGP šifrovaná správa</string>
<string name="nick_in_use">Prezývka už existuje</string>
<string name="invalid_muc_nick">Chybná prezývka</string>
<string name="admin">Administrátor</string>
<string name="owner">Vlastník</string>
<string name="moderator">Moderátor</string>
<string name="participant">Účastník</string>
<string name="visitor">Návštevník</string>
<string name="remove_contact_text">Chcete vymazať %sz vašich kontaktov? Rozhovory s týmto kontaktom nebudú zmazané.</string>
<string name="block_contact_text">Chceli by ste zablokovať prijímanie správ od %s? </string>
<string name="unblock_contact_text">Chceli by ste odblokovať %s a povoliť prijímanie správ?</string>
<string name="block_domain_text">Zablokovať všetky kontakty od %s?</string>
<string name="unblock_domain_text">Odblokovať všetky kontakty od %s?</string>
<string name="contact_blocked">Kontakt zablokovaný</string>
<string name="blocked">Zablokovaný</string>
<string name="remove_bookmark_text">Chcete vymazať %sako záložku? Rozhovory s touto záložkou nebudú zmazané.</string>
<string name="register_account">Registrovať nový účet na serveri</string>
<string name="change_password_on_server">Zmeniť heslo na serveri</string>
<string name="share_with">Zdieľať s</string>
<string name="start_conversation">Začať rozhovor</string>
<string name="invite_contact">Pozvať kontakt</string>
<string name="invite">Pozvať</string>
<string name="contacts">Kontakty</string>
<string name="contact">Kontakt</string>
<string name="cancel">Zrušiť</string>
@ -46,33 +77,70 @@
<string name="unblock">Odblokovať</string>
<string name="save">Uložiť</string>
<string name="ok">OK</string>
<string name="crash_report_title">%1$ssa zrútila</string>
<string name="crash_report_message">Pomocou vášho XMPP konta nám pošlite záznam o zlyhaní, ktorý nám pomôže vo vývoji %1$s.</string>
<string name="send_now">Poslať teraz</string>
<string name="send_never">Nepýtať sa znova</string>
<string name="problem_connecting_to_account">Nedá sa pripojiť k účtu</string>
<string name="problem_connecting_to_accounts">Nedá sa pripojiť k viacerým kontám</string>
<string name="touch_to_fix">Ťapnite na správu vášho účtu</string>
<string name="attach_file">Priložiť súbor</string>
<string name="not_in_roster">Pridať tento chýbajúci kontakt do vašich kontaktov?</string>
<string name="add_contact">Pridať kontakt</string>
<string name="send_failed">doručenie zlyhalo</string>
<string name="preparing_image">Pripravujem odoslanie obrázka</string>
<string name="preparing_images">Pripravujem odoslanie obrázkov</string>
<string name="sharing_files_please_wait">Zdieľam súbory. Prosím čakajte...</string>
<string name="action_clear_history">Vymazať históriu</string>
<string name="clear_conversation_history">Vymazať históriu konverzácií</string>
<string name="clear_histor_msg">Chcete vymazať všetky správy v tomto rozhovore?\n\n<b>Upozornenie:</b>Nebude to mať vplyv na správy uložené na ostatných zariadeniach alebo serveroch.</string>
<string name="delete_file_dialog">Zmazať súbor</string>
<string name="delete_file_dialog_msg">Ste si istý, že chcete tento súbor zmazať?\n\n<b>Upozornenie:</b>Nevymažú sa kópie súborov, ktoré sú uložené na ostatných zariadeniach alebo serveroch.</string>
<string name="also_end_conversation">Potom zavrieť tento rozhovor</string>
<string name="choose_presence">Zvoliť zariadenie</string>
<string name="send_unencrypted_message">Poslať nezašifrovanú správu</string>
<string name="send_message">Poslať správu</string>
<string name="send_message_to_x">Poslať správu na %s</string>
<string name="send_omemo_message">Poslať OMEMO šifrovanú správu</string>
<string name="send_omemo_x509_message">Poslať v\\OMEMO šifrovanú správu</string>
<string name="send_pgp_message">Poslať OpenPGP šifrovanú správu</string>
<string name="your_nick_has_been_changed">Používa sa nová prezývka</string>
<string name="send_unencrypted">Poslať nešifrované</string>
<string name="decryption_failed">Zašifrovanie zlyhalo. Možno nemáte správny privátny kľúč.</string>
<string name="openkeychain_required">OpenKeychain</string>
<string name="restart">Reštartovať</string>
<string name="install">Inštalovať</string>
<string name="openkeychain_not_installed">Prosím, nainštalujte OpenKeychain</string>
<string name="offering">ponúka…</string>
<string name="waiting">čakám…</string>
<string name="no_pgp_key">Nenašiel sa žiadny OpenPGP kľúč</string>
<string name="contact_has_no_pgp_key">Nepodarilo sa zašifrovať vašu správu, pretože váš kontakt nezverejňuje jeho verejný kľúč.\n\n<small>Požiadajte prosím váš kontakt, aby si nastavil OpenPGP.</small></string>
<string name="no_pgp_keys">Nenašli sa žiadne OpenPGP kľúče</string>
<string name="contacts_have_no_pgp_keys">Nemôžem zašifrovať Vašu správu, pretože Vaše kontakty neoznamujú ich verejné kľúče.\n\n<small>Poproste ich, aby si nastavili OpenPGP.</small></string>
<string name="pref_general">Všeobecné</string>
<string name="pref_accept_files">Prijať súbory</string>
<string name="pref_accept_files_summary">Automaticky prijať súbory menšie ako…</string>
<string name="pref_attachments">Prílohy</string>
<string name="pref_notification_settings">Oznámenie</string>
<string name="pref_vibrate">Vibrovať</string>
<string name="pref_vibrate_summary">Vibrovať, keď príde nová správa</string>
<string name="pref_led">LED notifikácia</string>
<string name="pref_led_summary">Blikať notifikačným svetlom, keď príde nová správa</string>
<string name="pref_ringtone">Zvonenie</string>
<string name="pref_notification_sound">Zvuk oznámenia</string>
<string name="pref_notification_sound_summary">Zvuk oznámenia nových správ</string>
<string name="pref_call_ringtone_summary">Zvonenie pre prichádzajúce hovory</string>
<string name="pref_notification_grace_period">Ochranná doba</string>
<string name="pref_advanced_options">Pokročilé</string>
<string name="pref_never_send_crash">Neodosielať detaily o zlyhaní aplikácie</string>
<string name="pref_never_send_crash_summary">Keď pošlete detaily o dôvode zlyhania, pomáhate vývoju</string>
<string name="pref_confirm_messages">Potvrdzovať správy</string>
<string name="pref_confirm_messages_summary">Dajte vedieť svojim kontaktom, keď prijmete a prečítate si správy</string>
<string name="pref_ui_options">Prostredie</string>
<string name="bad_key_for_encryption">Nesprávny kľúč na šifrovanie.</string>
<string name="accept">Prijať</string>
<string name="error">Došlo k chybe</string>
<string name="recording_error">Chyba</string>
<string name="your_account">Váš účet</string>
<string name="send_presence_updates">Zasielať zmeny stavu</string>
<string name="receive_presence_updates">Prijímať zmeny stavu</string>
@ -81,8 +149,10 @@
<string name="attach_take_picture">Odfotiť</string>
<string name="preemptively_grant">Aktívne povoliť vyžiadanie zmeny stavu</string>
<string name="error_not_an_image_file">Vybraný súbor nie je obrázok</string>
<string name="error_compressing_image">Nemohol som konvertovať obrázkový súbor</string>
<string name="error_file_not_found">Súbor sa nenašiel</string>
<string name="error_io_exception">Všeobecná I/O chyba. Možno už nie je voľné miesto?</string>
<string name="error_security_exception">Aplikácia, ktorú ste použili na zdieľanie tohto súboru neposkytla dostatočné povolenia.</string>
<string name="account_status_unknown">Neznámy</string>
<string name="account_status_disabled">Dočasne vypnutý</string>
<string name="account_status_online">Online</string>
@ -94,6 +164,10 @@
<string name="account_status_regis_fail">Registrácia zlyhala</string>
<string name="account_status_regis_conflict">Užívateľské meno už existuje</string>
<string name="account_status_regis_success">Registrácia ukončená</string>
<string name="account_status_regis_not_sup">Registrácia nie je podporovaná serverom.</string>
<string name="account_status_regis_invalid_token">Neplatný registračný token</string>
<string name="account_status_tls_error_domain">Doména sa nedá overiť</string>
<string name="account_status_policy_violation">Porušenie pravidiel</string>
<string name="account_status_incompatible_server">Nekompatibilný server</string>
<string name="encryption_choice_unencrypted">Nezašifrovaný</string>
<string name="encryption_choice_otr">OTR</string>
@ -106,8 +180,13 @@
<string name="mgmt_account_enable">Povoliť účet</string>
<string name="mgmt_account_are_you_sure">Ste si istý?</string>
<string name="attach_record_voice">Nahrať hlas</string>
<string name="account_settings_jabber_id">XMPP adresa</string>
<string name="block_jabber_id">Zablokovať adresu XMPP</string>
<string name="account_settings_example_jabber_id">meno@priklad.com</string>
<string name="password">Heslo</string>
<string name="invalid_jid">Toto nie je platná XMPP adresa</string>
<string name="error_out_of_memory">Nedostatok pamäte. Obrázok príliš veľký</string>
<string name="add_phone_book_text">Chcete pridať do vašich kontaktov %s?</string>
<string name="server_info_show_more">Informácie o serveri</string>
<string name="server_info_mam">XEP-0313: MAM</string>
<string name="server_info_carbon_messages">XEP-0280: Message Carbons</string>
@ -115,38 +194,65 @@
<string name="server_info_blocking">XEP-0191: Blocking Command</string>
<string name="server_info_roster_version">XEP-0237: Roster Versioning</string>
<string name="server_info_stream_management">XEP-0198: Stream Management</string>
<string name="server_info_external_service_discovery">XEP-0215: Zistenie externej služby</string>
<string name="server_info_pep">XEP-0163: PEP (Avatars / OMEMO)</string>
<string name="server_info_http_upload">XEP-0363: HTTP File Upload</string>
<string name="server_info_push">XEP-0357: Oznámenia</string>
<string name="server_info_available">dostupný</string>
<string name="server_info_unavailable">nedostupný</string>
<string name="missing_public_keys">Chýba oznámenie o verejnom kľúči</string>
<string name="last_seen_now">práve prihlásený</string>
<string name="last_seen_min">naposledy videný pred minútou</string>
<string name="last_seen_mins">naposledy prihlásený pred %d minútami</string>
<string name="last_seen_hour">naposledy videný pred hodinou</string>
<string name="last_seen_hours">naposledy prihlásený pred %d hodinami</string>
<string name="last_seen_day">naposledy videný včera</string>
<string name="last_seen_days">naposledy prihlásený pred %d dňami</string>
<string name="omemo_fingerprint">OMEMO identifikátor</string>
<string name="omemo_fingerprint_x509">v\\OMEMO odtlačok</string>
<string name="omemo_fingerprint_selected_message">OMEMO odtlačok (pôvod správy)</string>
<string name="omemo_fingerprint_x509_selected_message">v\\OMEMO odtlačok (pôvod správy)</string>
<string name="other_devices">Ostatné zariadenia</string>
<string name="trust_omemo_fingerprints">Dôverovať OMEMO identifikátoru</string>
<string name="fetching_keys">Načítavam kľúče...</string>
<string name="done">Dokončený</string>
<string name="decrypt">Dešifrovať</string>
<string name="bookmarks">Záložky</string>
<string name="search">Hľadať</string>
<string name="delete_contact">Zmazať kontakt</string>
<string name="view_contact_details">Zobraziť detaily kontaktu</string>
<string name="block_contact">Zablokovať kontakt</string>
<string name="unblock_contact">Odblokovať kontakt</string>
<string name="create">Vytvoriť</string>
<string name="select">Vybrať</string>
<string name="contact_already_exists">Kontakt už existuje</string>
<string name="join">Vstúpiť</string>
<string name="channel_full_jid_example">channel@conference.example.com/nick</string>
<string name="channel_bare_jid_example">channel@conference.example.com</string>
<string name="save_as_bookmark">Uložiť ako záložku</string>
<string name="delete_bookmark">Vymazať záložku</string>
<string name="destroy_room">Vymazať skupinový rozhovor</string>
<string name="destroy_channel">Vymazať kanál</string>
<string name="could_not_destroy_room">Nemohol som vymazať skupinový rozhovor</string>
<string name="could_not_destroy_channel">Nemohol som vymazať kanál</string>
<string name="action_edit_subject">Upraviť predmet skupinového rozhovoru</string>
<string name="topic">Téma</string>
<string name="joining_conference">Pripájam skupinový rozhovor...</string>
<string name="leave">Odísť</string>
<string name="contact_added_you">Kontakt pridaný do zoznamu</string>
<string name="add_back">Znova pridať</string>
<string name="contact_has_read_up_to_this_point">%s dočítal až potiaľ</string>
<string name="contacts_have_read_up_to_this_point">%sdočítal potiaľto</string>
<string name="contacts_and_n_more_have_read_up_to_this_point">%1$s+%2$dostatní dočítali potiaľto</string>
<string name="everyone_has_read_up_to_this_point">Každý dočítal potiaľto</string>
<string name="publish">Zverejniť</string>
<string name="touch_to_choose_picture">Ťuknite na avatar pre vybranie obrázka z galérie</string>
<string name="publishing">Zverejňujem…</string>
<string name="error_publish_avatar_server_reject">Server odmietol toto zverejnenie</string>
<string name="error_publish_avatar_converting">Nemôžem skonvertovať váš obrázok</string>
<string name="error_saving_avatar">Nepodarilo sa uložiť avatar na disk</string>
<string name="or_long_press_for_default">(Dlho podržať pre obnovenie pôvodného stavu)</string>
<string name="error_publish_avatar_no_server_support">Váš server nepodporuje zverejnenie avatarov</string>
<string name="private_message">súkromná správa</string>
<string name="private_message_to">pre %s</string>
<string name="send_private_message_to">Odoslať súkromnú správu %s</string>
@ -159,26 +265,50 @@
<string name="enter_password">Vložiť heslo</string>
<string name="request_now">Ihneď vyžiadať</string>
<string name="ignore">Ignorovať</string>
<string name="pref_security_settings">Bezpečnosť</string>
<string name="pref_allow_message_correction">Povoliť úpravu správy</string>
<string name="pref_allow_message_correction_summary">Povoliť vašim kontaktom spätne upraviť ich správy</string>
<string name="pref_expert_options">Nastavenia pre skúsených</string>
<string name="pref_expert_options_summary">S týmto narábajte veľmi opatrne, prosím</string>
<string name="title_activity_about_x">O %s</string>
<string name="title_pref_quiet_hours">Tichý režim</string>
<string name="title_pref_quiet_hours_start_time">Čas začiatku</string>
<string name="title_pref_quiet_hours_end_time">Čas konca</string>
<string name="title_pref_enable_quiet_hours">Povoliť tichý režim</string>
<string name="pref_quiet_hours_summary">Upozornenia budú počas tichého režimu stlmené</string>
<string name="pref_expert_options_other">Ďalší</string>
<string name="pref_autojoin">Synchronizovať so záložkami</string>
<string name="pref_autojoin_summary">Automaticky sa pripojiť k skupinovému rozhovoru, ak to hovorí záložka</string>
<string name="toast_message_omemo_fingerprint">OMEMO odtlačok skopírovaný do schránky</string>
<string name="conference_banned">Ste zakázaný na tomto skupinovom rozhovore</string>
<string name="conference_members_only">Skupinový rozhovor len pre členov</string>
<string name="conference_shutdown">Skupinový rozhovor bol zastavený</string>
<string name="conference_unknown_error">Už viac nie ste v tomto skupinovom rozhovore</string>
<string name="using_account">Používa sa účet %s</string>
<string name="hosted_on">Hostovaný na %s</string>
<string name="checking_x">Overiť %s na HTTP host</string>
<string name="not_connected_try_again">Nie ste pripojený. Skúste to neskôr</string>
<string name="check_x_filesize">Overiť %s veľkosť</string>
<string name="check_x_filesize_on_host">Skontrolujte %1$sveľkosť na %2$s</string>
<string name="message_options">Možnosti správy</string>
<string name="quote">Citovať</string>
<string name="paste_as_quote">Vložiť ako citát</string>
<string name="copy_original_url">Skopírovať originálny URL</string>
<string name="send_again">Poslať znova</string>
<string name="file_url">URL súbor</string>
<string name="url_copied_to_clipboard">URL skopírovaná do schránky</string>
<string name="jabber_id_copied_to_clipboard">XMPP adresa skopírovaná do schránky</string>
<string name="error_message_copied_to_clipboard">Správa o chybe skopírovaná do schránky</string>
<string name="web_address">web adresa</string>
<string name="scan_qr_code">Snímať 2D Bar kód</string>
<string name="show_qr_code">Ukázať 2D Bar kód</string>
<string name="show_block_list">Zobraziť zoznam blokovaných</string>
<string name="account_details">Detaily účtu</string>
<string name="confirm">Potvrdiť</string>
<string name="try_again">Skúste znova</string>
<string name="pref_keep_foreground_service_summary">Zamedzí operačnému systému ukončiť pripojenie </string>
<string name="pref_create_backup">Vytvoriť zálohu</string>
<string name="notification_restored_backup_title">Vaša záloha bola obnovená</string>
<string name="choose_file">Vybrať súbor</string>
<string name="receiving_x_file">Prijímam %1$s (%2$d%% ukončený)</string>
<string name="download_x_file">Stiahnuť %s</string>
@ -191,6 +321,9 @@
<string name="enable_notifications">Povoliť upozornenia</string>
<string name="account_image_description">Avatar účtu</string>
<string name="copy_omemo_clipboard_description">Skopírovať OMEMO identifikátor do schránky</string>
<string name="regenerate_omemo_key">Regenerovať OMEMO kľúč</string>
<string name="clear_other_devices">Vymazať zariadenia</string>
<string name="clear_other_devices_desc">Ste si istý, že chcete odstrániť všetky ostatné zariadenia z OMEMO oznámenia? Keď sa nabudúce vaše zariadenia pripoja, znova sa samé ohlásia, ale nemusia prijať správy odoslané medzitým. </string>
<string name="fetching_history_from_server">Načítať históriu zo serveru</string>
<string name="no_more_history_on_server">Na serveri nie je žiadna ďalšia história</string>
<string name="updating">Aktualizujem...</string>
@ -257,7 +390,48 @@
<string name="username_hint">Užívateľské meno</string>
<string name="invalid_username">Toto nie je platné užívateľské meno</string>
<string name="action_renew_certificate">Obnoviť certifikát</string>
<string name="error_fetching_omemo_key">Chyba pri načítaní OMEMO kľúča!</string>
<string name="verified_omemo_key_with_certificate">kľúč OMEMO overený certifikátom!</string>
<string name="pref_use_tor">Pripojiť cez Tor</string>
<string name="connected_accounts">%1$dz%2$dúčtov pripojených</string>
<string name="no_accounts">(Žiadne aktivované účty)</string>
<string name="presence_online">Online</string>
<string name="pref_delete_omemo_identities">Vymazať OMEMO identifikátory</string>
<string name="pref_delete_omemo_identities_summary">Re-generuje vaše kľúče OMEMO. Všetky vaše kontakty vás budú musieť znova overiť. Použite to ako poslednú možnosť.</string>
<string name="all_omemo_keys_have_been_verified">Overili ste všetky kľúče OMEMO vo vašom vlastníctve.</string>
<string name="verify_omemo_keys">Overiť kľúče OMEMO</string>
<string name="online_right_now">online práve teraz</string>
<string name="message_copied_to_clipboard">Správa skopírovaná do schránky</string>
<string name="pref_omemo_setting">OMEMO šifrovanie</string>
<string name="pref_omemo_setting_summary_always">OMEMO bude vždy používané pre individuálne a súkromné skupinové rozhovory.</string>
<string name="pref_omemo_setting_summary_default_on">OMEMO bude predvolene zapnuté pre všetky rozhovory.</string>
<string name="pref_font_size">Veľkosť písma</string>
<string name="omemo_decryption_failed">Nepodarilo sa dešifrovať OMEMO správu.</string>
<string name="title_activity_show_location">Zobraziť polohu</string>
<string name="notification_group_calls">Hovory</string>
<string name="incoming_calls_channel_name">Prichádzajúce hovory</string>
<string name="ongoing_calls_channel_name">Prebiehajúce hovory</string>
<string name="pref_incoming_call_notification_settings">Nastavenia oznámení prichádzajúcich hovorov</string>
<string name="restore_backup">Obnoviť zálohu</string>
<string name="attach">Priložiť</string>
<string name="rtp_state_incoming_call">Prichádzajúci hovor</string>
<string name="rtp_state_incoming_video_call">Prichádzajúci video hovor</string>
<string name="rtp_state_accepting_call">Prijímam hovor</string>
<string name="rtp_state_ending_call">Ukončujem hovor</string>
<string name="rtp_state_finding_device">Vyhľadávanie zariadení</string>
<string name="rtp_state_ringing">Zvoní</string>
<string name="rtp_state_connectivity_error">Nedá sa pripojiť hovor</string>
<string name="ongoing_call">Prebiehajúci hovor</string>
<string name="ongoing_video_call">Prebiehajúci video hovor</string>
<string name="incoming_call">Prichádzajúci hovor</string>
<string name="incoming_call_duration">Prichádzajúci hovor - %s</string>
<string name="missed_call_timestamp">Zmeškaný hovor - %s</string>
<string name="outgoing_call">Odchádzajúci hovor</string>
<string name="outgoing_call_duration">Odchádzajúci hovor - %s</string>
<string name="missed_call">Zmeškaný hovor</string>
<string name="audio_call">Hlasový hovor</string>
<string name="video_call">Video hovor</string>
<string name="only_one_call_at_a_time">Naraz môžete mať iba jeden hovor.</string>
<string name="return_to_ongoing_call">Vrátiť sa do prebiehajúceho hovoru</string>
<string name="encrypted_with_omemo">Zašifrované s OMEMO</string>
</resources>

View File

@ -111,7 +111,9 @@
<string name="offering">нудим…</string>
<string name="waiting">чекам…</string>
<string name="no_pgp_key">Нема ОпенПГП кључа</string>
<string name="contact_has_no_pgp_key">Није могуће шифровати вашу поруку јер контакт није објавио свој јавни кључ.\n\n<small>Замолите контакт да подеси ОпенПГП.</small></string>
<string name="no_pgp_keys">Нема ОпенПГП кључева</string>
<string name="contacts_have_no_pgp_keys">Није могуће шифровати вашу поруку јер контакти нису објавили своје јавне кључеве.\n\n<small>Замолите контакте да подесе ОпенПГП.</small></string>
<string name="pref_general">Опште</string>
<string name="pref_accept_files">Прихватај фајлове</string>
<string name="pref_accept_files_summary">Аутоматски прихватај фајлове мање од…</string>
@ -128,6 +130,7 @@
<string name="pref_notification_grace_period">Период одгоде</string>
<string name="pref_advanced_options">Напредно</string>
<string name="pref_never_send_crash">Никад не шаљи извештаје о паду</string>
<string name="pref_never_send_crash_summary">Слањем извештаја рада помажете развоју апликације.</string>
<string name="pref_confirm_messages">Потврди поруке</string>
<string name="pref_confirm_messages_summary">Обзнаните контактима када примите и прочитате њихове поруке</string>
<string name="pref_ui_options">Сучеље</string>
@ -147,6 +150,8 @@
<string name="error_compressing_image">Не могу преобратити датотеку фотографије</string>
<string name="error_file_not_found">Фајл није нађен</string>
<string name="error_io_exception">Општа У/И грешка. Можда вам је нестало простора у складишту?</string>
<string name="error_security_exception_during_image_copy">Апликација из које делите ову слику не даје дозволу довољну да се датотека учита.\n\n<small>Поделите слику другим претраживачем датотека</small>.</string>
<string name="error_security_exception">Апликација из које делите овај садржај не даје довољну дозволу.</string>
<string name="account_status_unknown">Непознато</string>
<string name="account_status_disabled">Привремено искључен</string>
<string name="account_status_online">На вези</string>
@ -179,6 +184,7 @@
<string name="openpgp_has_been_published">ОпенПГП кључ је објављен.</string>
<string name="mgmt_account_enable">Укључи налог</string>
<string name="mgmt_account_are_you_sure">Да ли сте сигурни?</string>
<string name="mgmt_account_delete_confirm_text">Брисањем налога бришете и целу историју ваших разговора.</string>
<string name="attach_record_voice">Сними глас</string>
<string name="account_settings_jabber_id">ИксМПП адреса</string>
<string name="block_jabber_id">Блокирај ИксМПП адресу</string>
@ -237,6 +243,8 @@
<string name="delete_bookmark">Обриши обележивач</string>
<string name="destroy_room">Уклони групно ћаскање</string>
<string name="destroy_channel">Уклони канал</string>
<string name="destroy_room_dialog">Да ли сигурно жеите да уклоните ово групно ћаскање?\n\n<b>Упозорење:</b> Групно ћаскање ће бити потпуно обрисано са сервера.</string>
<string name="destroy_channel_dialog">Да ли сигурно жеите да уклоните овај јавни канал?\n\n<b>Упозорење:</b> Канал ће бити потпуно обрисан са сервера.</string>
<string name="could_not_destroy_room">Не могу уклонити групно ћаскање</string>
<string name="could_not_destroy_channel">Не могу уклонити канал</string>
<string name="action_edit_subject">Уреди предмет групног ћаскања</string>
@ -269,8 +277,10 @@
<string name="enable">Укључи</string>
<string name="conference_requires_password">Групно ћаскање захтева лозинку</string>
<string name="enter_password">Унесите лозинку</string>
<string name="request_presence_updates">Најпре захтевајте ажурирање присутности од вашег контакта.\n\n<small>Ово ће омогућити да се одреди који клијент ваш контакт користи</small>.</string>
<string name="request_now">Захтевај одмах</string>
<string name="ignore">Занемари</string>
<string name="without_mutual_presence_updates"><b>Упозорење:</b> Слањем овога без обостраног ажурирања присутности може изазвати неочекиване проблеме.\n\n<small>Идите у „Детаљи контакта” да потврдите вашу претплату за присутност.</small></string>
<string name="pref_security_settings">Безбедност</string>
<string name="pref_allow_message_correction">Дозволи исправљање порука</string>
<string name="pref_allow_message_correction_summary">Дозвољава вашим контактима да ретроактивно уређују њихове поруке</string>
@ -349,6 +359,7 @@
<string name="copy_omemo_clipboard_description">Копирај ОМЕМО отисак на клипборд</string>
<string name="regenerate_omemo_key">Поново генериши ОМЕМО кључ</string>
<string name="clear_other_devices">Очисти уређаје</string>
<string name="error_no_keys_to_trust_presence">Нема употребљивих кључева за овај контакт.\nПроверите да ли сте одобрили узајамно ажурирање присутности.</string>
<string name="error_trustkeys_title">Нешто је пошло по злу</string>
<string name="fetching_history_from_server">Добављам историјат са сервера</string>
<string name="no_more_history_on_server">Нема више историјата на серверу</string>
@ -515,6 +526,7 @@
<string name="correct_message">Исправи поруку</string>
<string name="send_corrected_message">Пошаљи исправљену поруку</string>
<string name="this_account_is_disabled">Искључили сте овај налог</string>
<string name="security_error_invalid_file_access">Безбедносна грешка: неисправан приступ датотеци!</string>
<string name="no_application_to_share_uri">Нема апликације за дељење ресурса</string>
<string name="share_uri_with">Подели везу помоћу…</string>
<string name="agree_and_continue">Сложи се и настави</string>
@ -523,6 +535,7 @@
<string name="use_own_provider">Користићу сопствени провајдер</string>
<string name="pick_your_username">Одредите ваше корисничко име</string>
<string name="pref_manually_change_presence">Ручно мењај доступност</string>
<string name="pref_manually_change_presence_summary">Поставите присутност при измени ваше поруке стања.</string>
<string name="status_message">Порука стања</string>
<string name="presence_chat">Слободан за ћаскање</string>
<string name="presence_online">На вези</string>
@ -564,12 +577,17 @@
<string name="allow">Дозволи</string>
<string name="no_permission_to_access_x">Нема дозвола за приступ %s</string>
<string name="remote_server_not_found">Удаљени сервер није нађен</string>
<string name="remote_server_timeout">Удаљени сервер се не одазива</string>
<string name="unable_to_update_account">Не могу да ажурирам налог</string>
<string name="report_jid_as_spammer">Пријавите ову ИксМПП адресу због нежељених порука.</string>
<string name="pref_delete_omemo_identities">Обриши ОМЕМО идентитете</string>
<string name="delete_selected_keys">Обриши изабране кључеве</string>
<string name="error_publish_avatar_offline">Морате бити повезани да бисте објавили ваш аватар.</string>
<string name="show_error_message">Прикажи поруку грешке</string>
<string name="error_message">Порука грешке</string>
<string name="data_saver_enabled">Чувар протока укључен</string>
<string name="device_does_not_support_data_saver">Ваш уређај не подржава искључење „Уштеде података” за %1$s.</string>
<string name="error_unable_to_create_temporary_file">Не могу да направим привремену датотеку</string>
<string name="this_device_has_been_verified">Овај уређај је оверен.</string>
<string name="copy_fingerprint">Копирај отисак</string>
<string name="verified_fingerprints">Оверени отисци</string>

View File

@ -4,6 +4,7 @@
<string name="action_add">Ny konversation</string>
<string name="action_accounts">Kontoinställningar</string>
<string name="action_account">Hantera konto</string>
<string name="action_end_conversation">Stäng konversation</string>
<string name="action_contact_details">Kontaktdetaljer</string>
<string name="action_muc_details">Gruppchattdetaljer</string>
<string name="channel_details">Kanaldetaljer</string>
@ -28,6 +29,13 @@
<string name="just_now">just nu</string>
<string name="minute_ago">1 min sedan</string>
<string name="minutes_ago">%d min sedan</string>
<plurals name="x_unread_conversations">
<item quantity="one">%d oläst konversation</item>
<item quantity="other">%d olästa konversationer</item>
</plurals>
<string name="sending">skickar…</string>
<string name="message_decrypting">Avkrypterar meddelande. Vänta…</string>
<string name="pgp_message">OpenPGP-krypterat meddelande</string>
@ -105,12 +113,16 @@
<string name="pref_led">LED notifieringar</string>
<string name="pref_led_summary">Blinka med notifieringsljuset när ett meddelande tagits emot</string>
<string name="pref_ringtone">Meddelandesignal</string>
<string name="pref_notification_sound">Aviseringsljud</string>
<string name="pref_notification_sound_summary">Aviseringsljud för nya meddelande</string>
<string name="pref_call_ringtone_summary">Ringsignal för inkommande samtal</string>
<string name="pref_notification_grace_period">Notifieringsfrist</string>
<string name="pref_advanced_options">Avancerat</string>
<string name="pref_never_send_crash">Skicka aldrig krasch-rapporter</string>
<string name="pref_confirm_messages">Bekräfta meddelanden</string>
<string name="pref_confirm_messages_summary">Låt dina kontakter veta när du har mottagit och läst deras meddelanden</string>
<string name="pref_ui_options">Gränssnitt</string>
<string name="openpgp_error">OpenKeychain genererade ett fel.</string>
<string name="bad_key_for_encryption">Dålig krypterings-nyckel.</string>
<string name="accept">Acceptera</string>
<string name="error">Ett fel har inträffat</string>
@ -136,6 +148,7 @@
<string name="account_status_regis_fail">Registreringsfel</string>
<string name="account_status_regis_conflict">Användarnamn används redan</string>
<string name="account_status_regis_success">Registrering klar</string>
<string name="account_status_regis_not_sup">Registrering stöds ej av server</string>
<string name="account_status_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>
@ -173,9 +186,14 @@
<string name="server_info_unavailable">otillgänglig</string>
<string name="missing_public_keys">Annonsering om publik nyckel saknas</string>
<string name="last_seen_now">senast sedd just nu</string>
<string name="last_seen_min">senast sedd för en minut sedan</string>
<string name="last_seen_mins">senast sedd %d minuter sedan</string>
<string name="last_seen_hour">senast sedd för en timme sedan</string>
<string name="last_seen_hours">senast sedd %d timmar sedan</string>
<string name="last_seen_day">senast sedd för en dag sedan</string>
<string name="last_seen_days">senast sedd %d dagar sedan</string>
<string name="install_openkeychain">Krypterat meddelande. Installera OpenKeychain för att dekryptera meddelandet.</string>
<string name="openpgp_messages_found">Nytt OpenPGP krypterat meddelande hittades</string>
<string name="openpgp_key_id">OpenPGP-nyckel-ID</string>
<string name="omemo_fingerprint">OMEMO-fingeravtryck</string>
<string name="omemo_fingerprint_x509">v\\OMEMO-fingeravtryck</string>
@ -500,7 +518,7 @@
<string name="encrypting_message">Krypterar meddelande</string>
<string name="not_fetching_history_retention_period">Hämtar inte meddelanden på grund av inställningen för borttagning av gamla meddelanden.</string>
<string name="transcoding_video">Komprimerar video</string>
<string name="corresponding_conversations_closed">Motsvarande konversationer är stängda.</string>
<string name="corresponding_conversations_closed">Korresponderande konversationer är stängda.</string>
<string name="contact_blocked_past_tense">Kontakt blockerad.</string>
<string name="pref_notifications_from_strangers">Notifieringar från främlingar</string>
<string name="received_message_from_stranger">Mottagna meddelanden från främlingar</string>
@ -531,6 +549,7 @@
<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="search_messages">Söka i meddelanden</string>
<string name="gif">GIF</string>
<string name="copy_jabber_id">Kopiera XMPP-adress</string>
<string name="nickname">Smeknamn</string>
@ -557,7 +576,10 @@
<string name="ebook">e-bok</string>
<string name="open_with">Öppna med...</string>
<string name="choose_account">Välj konto</string>
<string name="restore_backup">Återställa säkerhetskopiering</string>
<string name="restore">Återställa</string>
<string name="enter_password_to_restore">Ange ditt lösenord till kontot %s för att återställa säkerhetskopian.</string>
<string name="unable_to_restore_backup">Det gick inte att återställa säkerhetskopian.</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>
@ -584,4 +606,7 @@
<string name="category_about">Om</string>
<string name="please_enable_an_account">Aktivera ett konto</string>
<string name="rtp_state_declined_or_busy">Upptagen</string>
<string name="add_to_favorites">Fäst flik till toppen</string>
<string name="remove_from_favorites">Ta bort flik från toppen</string>
<string name="more_options">Fler alternativ</string>
</resources>

View File

@ -132,6 +132,8 @@
<string name="pref_never_send_crash_summary">Yığın izi göndererek gelişime yardımcı oluyorsunuz.</string>
<string name="pref_confirm_messages">İletileri onayla</string>
<string name="pref_confirm_messages_summary">Onların iletilerini aldığınızda ve okuduğunuzda, kişilerinizin bunu bilmesini sağlayın</string>
<string name="pref_prevent_screenshots">Ekran görüntülerini engelle</string>
<string name="pref_prevent_screenshots_summary">Uygulama anahtarlayıcısında uygulama içeriklerini sakla ve ekran görüntülerini engelle</string>
<string name="pref_ui_options">Arabirim</string>
<string name="openpgp_error">OpenKeychain bir hata verdi.</string>
<string name="bad_key_for_encryption">Kötü anahar şifrelemesi.</string>
@ -150,6 +152,7 @@
<string name="error_file_not_found">Dosya bulunamadı</string>
<string name="error_io_exception">Genel G/Ç hatası. Depolama yeri kalmamış olabilir mi?</string>
<string name="error_security_exception_during_image_copy">Bu görüntüyü seçmekte kullandığınız uygulama, dosyanın okunması için yeterli izinleri sağlayamadı. Görüntüyü seçmek için farklı bir dosya yöneticisi kullan.</string>
<string name="error_security_exception">Bu dosyayı paylaşmakta kullandığınız uygulama yeterince yetki sağlamamaktadır. </string>
<string name="account_status_unknown">Bilinmeyen</string>
<string name="account_status_disabled">Geçici olarak devre dışı</string>
<string name="account_status_online">Çevrim içi</string>
@ -164,6 +167,7 @@
<string name="account_status_regis_not_sup">Hesap, sunucu tarafından desteklenmiyor.</string>
<string name="account_status_regis_invalid_token">Geçersiz hesak simgesi</string>
<string name="account_status_tls_error">TLS uzlaşması başarısız</string>
<string name="account_status_tls_error_domain">Alan adı doğrulanamıyor</string>
<string name="account_status_policy_violation">Politika ihlali</string>
<string name="account_status_incompatible_server">Sunucu uyuşmazlığı</string>
<string name="account_status_stream_error">Akış hatası</string>
@ -412,6 +416,7 @@
<string name="audio">ses</string>
<string name="video">video</string>
<string name="image">görüntü</string>
<string name="vector_graphic">Vektör grafik</string>
<string name="pdf_document">PDF belgesi</string>
<string name="apk">Android uygulaması</string>
<string name="vcard">Kişi</string>
@ -910,6 +915,7 @@
<string name="rtp_state_connectivity_lost_error">Bağlantı kesildi</string>
<string name="rtp_state_retracted">Geri çekilmiş arama</string>
<string name="rtp_state_application_failure">Uygulama hatası</string>
<string name="rtp_state_security_error">Doğrulama sorunu</string>
<string name="hang_up">Çağrıyı sonlandır</string>
<string name="ongoing_call">Devam eden arama</string>
<string name="ongoing_video_call">Deaam eden görüntülü arama</string>
@ -959,4 +965,8 @@
<string name="unable_to_parse_invite">Davet iletilemedi</string>
<string name="server_does_not_support_easy_onboarding_invites">Sunucu, davet oluşturulmasını desteklemiyor</string>
<string name="no_active_accounts_support_this">Bu özelliği destekleyen aktif bir hesap yok</string>
<string name="backup_started_message">Yedekleme başlatıldı. Tamamlandığı zaman bir bildirim alacaksınız.</string>
<string name="unable_to_enable_video">Video etkinleştirilemedi</string>
<string name="plain_text_document">Düz metin dosyası</string>
</resources>

View File

@ -504,7 +504,7 @@
<string name="shared_images_with_x">Đã chia sẻ các hình ảnh với %s</string>
<string name="shared_text_with_x">Đã chia sẻ văn bản với %s</string>
<string name="no_storage_permission">Cấp quyền truy cập bộ nhớ cho %1$s</string>
<string name="no_camera_permission">Cấp quyền truy cập camera cho %1$s</string>
<string name="no_camera_permission">Cấp quyền truy cập máy ảnh cho %1$s</string>
<string name="sync_with_contacts">Đồng bộ với danh bạ</string>
<string name="sync_with_contacts_long">%1$s muốn quyền truy cập sổ địa chỉ của bạn để nối nó với danh sách liên hệ XMPP của bạn.\nViệc này sẽ hiển thị họ tên và ảnh đại diện của các liên hệ của bạn.\n\n%1$s sẽ chỉ đọc sổ địa chỉ của bạn và nối nó một cách cục bộ mà không tải gì cả lên máy chủ của bạn.</string>
<string name="sync_with_contacts_quicksy"><![CDATA[Quicksy cần quyền truy cập vào số điện thoại của các liên hệ của bạn để đưa ra đề xuất về các liên hệ có thể có đã ở trên Quicksy.<br><br>Chúng tôi sẽ không lưu trữ bản sao của các số điện thoại đó.\n\nĐể biết thêm thông tin hãy đọc <a href="https://quicksy.im/#privacy">chính sách riêng tư</a> của chúng tôi.<br><br>Bây giờ bạn sẽ được hỏi cấp quyền truy cập danh bạ.]]></string>
@ -530,11 +530,424 @@
<string name="security_error_invalid_file_access">Lỗi bảo mật: Truy cập tệp không hợp lệ!</string>
<string name="no_application_to_share_uri">Không tìm thấy ứng dụng nào để chia sẻ URI</string>
<string name="share_uri_with">Chia sẻ URI với...</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy là một sự phát triển khác của ứng dụng khách XMPP Conversations có tự động khám phá liên hệ.<br><br>Bạn đăng ký bằng số điện thoại của bạn và Quicksy sẽ tự động—dựa trên những số điện thoại trong sổ địa chỉ của bạn—đề xuất các liên hệ có thể có cho bạn.<br><br>Bằng cách đăng ký, bạn đồng ý với <a href=\"https://quicksy.im/#privacy\">chính sách riêng tư</a> của chúng tôi.]]></string>
<string name="agree_and_continue">Đồng ý và tiếp tục</string>
<string name="magic_create_text">Một hướng dẫn đã được thiết lập cho việc tạo tài khoản trên conversations.im.¹\nKhi chọn conversations.im làm nhà cung cấp, bạn sẽ có thể giao tiếp với những người dùng của các nhà cung cấp khác bằng cách đưa cho họ địa chỉ XMPP đầy đủ của bạn. </string>
<string name="your_full_jid_will_be">Địa chỉ XMPP đầy đủ của bạn sẽ là: %s</string>
<string name="create_account">Tạo tài khoản</string>
<string name="use_own_provider">Dùng nhà cung cấp của tôi</string>
<string name="pick_your_username">Hãy chọn tên người dùng</string>
<string name="pref_manually_change_presence">Quản lý tính khả dụng thủ công</string>
<string name="pref_manually_change_presence_summary">Đặt tính khả dụng của bạn khi chỉnh sửa thông báo trạng thái của bạn.</string>
<string name="status_message">Thông báo trạng thái</string>
<string name="presence_chat">Rảnh để trò chuyện</string>
<string name="presence_online">Trực tuyến</string>
<string name="presence_away">Vắng mặt</string>
<string name="presence_xa">Không khả dụng</string>
<string name="presence_dnd">Bận</string>
<string name="secure_password_generated">Một mật khẩu bảo mật đã được tạo</string>
<string name="device_does_not_support_battery_op">Thiết bị của bạn không hỗ trợ tắt tối ưu hoá pin</string>
<string name="registration_please_wait">Đăng ký thất bại: Hãy thử lại sau</string>
<string name="registration_password_too_weak">Đăng ký thất bại: Mật khẩu quá yếu</string>
<string name="choose_participants">Chọn các thành viên</string>
<string name="creating_conference">Tạo nhóm chat...</string>
<string name="invite_again">Mời lại</string>
<string name="gp_disable">Tắt</string>
<string name="gp_short">Ngắn</string>
<string name="gp_medium">Vừa</string>
<string name="gp_long">Dài</string>
<string name="pref_broadcast_last_activity">Sử dụng truyền phát</string>
<string name="pref_broadcast_last_activity_summary">Cho các liên hệ của bạn biết bạn dùng Conversations</string>
<string name="pref_privacy">Riêng tư</string>
<string name="pref_theme_options">Chủ đề</string>
<string name="pref_theme_options_summary">Chọn bộ màu sáng</string>
<string name="pref_theme_automatic">Tự động</string>
<string name="pref_theme_light">Sáng</string>
<string name="pref_theme_dark">Tối</string>
<string name="pref_use_green_background">Nền xanh lá cây</string>
<string name="pref_use_green_background_summary">Dùng nền xanh lá cây cho tin nhắn nhận được</string>
<string name="unable_to_connect_to_keychain">Không thể kết nối với OpenKeychain</string>
<string name="this_device_is_no_longer_in_use">Thiết bị này không còn được dùng nữa</string>
<string name="type_pc">Máy tính</string>
<string name="type_phone">Điện thoại di động</string>
<string name="type_tablet">Máy tính bảng</string>
<string name="type_web">Trình duyệt web</string>
<string name="type_console">Bảng điều khiển</string>
<string name="payment_required">Yêu cầu thanh toán</string>
<string name="missing_internet_permission">Cho phép sử dụng Internet</string>
<string name="me">Tôi</string>
<string name="contact_asks_for_presence_subscription">Liên hệ yêu cầu đăng ký sự có mặt</string>
<string name="allow">Cho phép</string>
<string name="no_permission_to_access_x">Không có quyền truy cập%s</string>
<string name="remote_server_not_found">Không tìm thấy máy chủ trên mạng</string>
<string name="remote_server_timeout">Hết thời gian chờ cho máy chủ trên mạng</string>
<string name="unable_to_update_account">Không thể cập nhật tài khoản</string>
<string name="report_jid_as_spammer">Báo cáo địa chỉ XMPP này vì spam.</string>
<string name="pref_delete_omemo_identities">Xoá các danh tính OMEMO</string>
<string name="pref_delete_omemo_identities_summary">Tái tạo lại các mã khoá OMEMO của bạn. Tất cả các liên hệ của bạn sẽ phải xác minh lại bạn. Chỉ sử dụng việc này làm giải pháp cuối cùng.</string>
<string name="delete_selected_keys">Xoá các mã khoá đã chọn</string>
<string name="error_publish_avatar_offline">Bạn cần phải có kết nối để xuất bản ảnh đại diện của bạn.</string>
<string name="show_error_message">Hiện thông báo lỗi</string>
<string name="error_message">Thông báo lỗi</string>
<string name="data_saver_enabled">Trình tiết kiệm dữ liệu đang bật</string>
<string name="data_saver_enabled_explained">Hệ điều hành của bạn đang giới hạn %1$s truy cập Internet trong nền. Để nhận các thông báo tin nhắn mới, bạn nên cho phép %1$s truy cập không giới hạn khi \"Trình tiết kiệm dữ liệu\" đang bật.\n%1$s vẫn sẽ nỗ lực tiết kiệm dữ liệu khi có thể.</string>
<string name="device_does_not_support_data_saver">Thiết bị của bạn không hỗ trợ việc tắt Trình tiết kiệm dữ liệu cho %1$s.</string>
<string name="error_unable_to_create_temporary_file">Không thể tạo tệp tạm</string>
<string name="this_device_has_been_verified">Thiết bị này đã được xác thực</string>
<string name="copy_fingerprint">Sao chép mã vân tay</string>
<string name="all_omemo_keys_have_been_verified">Bạn đã xác minh tất cả mã khoá OMEMO mà bạn đang sở hữu</string>
<string name="barcode_does_not_contain_fingerprints_for_this_conversation">Mã vạch không chứa mã vân tay cho cuộc trò chuyện này.</string>
<string name="verified_fingerprints">Mã vân tay đã xác minh</string>
<string name="use_camera_icon_to_scan_barcode">Sử dụng máy ảnh để quét mã vạch của liên hệ</string>
<string name="please_wait_for_keys_to_be_fetched">Vui lòng đợi để lấy các mã khoá</string>
<string name="share_as_barcode">Chia sẻ dưới dạng mã vạch</string>
<string name="share_as_uri">Chia sẻ dưới dạng URI XMPP</string>
<string name="share_as_http">Chia sẻ dưới dạng liên kết HTTP</string>
<string name="pref_blind_trust_before_verification">Tin tưởng mù quáng trước khi xác minh</string>
<string name="pref_blind_trust_before_verification_summary">Tin tưởng các thiết bị mới từ các liên hệ chưa xác minh, nhưng hỏi xác nhận thủ công các thiết bị mới từ các liên hệ đã xác minh.</string>
<string name="blindly_trusted_omemo_keys">Các mã khoá OMEMO đã tin tưởng mù quáng, có nghĩa là họ có thể là một ai đó khác hoặc ai đó có thể đã can thiệp.</string>
<string name="not_trusted">Chưa tin tưởng</string>
<string name="invalid_barcode">Mã vạch 2D không hợp lệ</string>
<string name="pref_clean_cache_summary">Dọn dẹp thư mục bộ nhớ tạm (được ứng dụng máy ảnh sử dụng)</string>
<string name="pref_clean_cache">Dọn dẹp bộ nhớ tạm</string>
<string name="pref_clean_private_storage">Dọn dẹp bộ nhớ riêng</string>
<string name="pref_clean_private_storage_summary">Dọn dẹp bộ nhớ riêng nơi các tệp được giữ (Chúng có thể được tải xuống lại từ máy chủ)</string>
<string name="i_followed_this_link_from_a_trusted_source">Tôi đã đi theo liên kết này từ một nguồn được tin tưởng</string>
<string name="verifying_omemo_keys_trusted_source">Bạn sắp xác minh các mã khoá OMEMO của %1$s sau khi nhấn vào một liên kết. Việc này chỉ là bảo mật nếu bạn đã đi theo liên kết này từ một nguồn được tin tưởng, nơi chỉ có %2$s có thể đã xuất bản liên kết này.</string>
<string name="verify_omemo_keys">Xác minh các mã khoá OMEMO</string>
<string name="show_inactive_devices">Hiện không hoạt động</string>
<string name="hide_inactive_devices">Ẩn không hoạt động</string>
<string name="distrust_omemo_key">Huỷ tin tưởng thiết bị</string>
<string name="distrust_omemo_key_text">Bạn có chắc bạn muốn bỏ xác minh thiết bị này không?\nThiết bị này và các tin nhắn từ nỏ sẽ được đánh dấu là \"Chưa tin tưởng\".</string>
<plurals name="seconds">
<item quantity="other">%d giây</item>
</plurals>
<plurals name="minutes">
<item quantity="other">%d phút</item>
</plurals>
<plurals name="hours">
<item quantity="other">%d giờ</item>
</plurals>
<plurals name="days">
<item quantity="other">%d ngày</item>
</plurals>
<plurals name="weeks">
<item quantity="other">%d tuần</item>
</plurals>
<plurals name="months">
<item quantity="other">%d tháng</item>
</plurals>
<string name="pref_automatically_delete_messages">Tự động xoá tin nhắn</string>
<string name="pref_automatically_delete_messages_description">Tự động xoá các tin nhắn cũ hơn phạm vi thời gian được thiết lập khỏi thiết bị.</string>
<string name="encrypting_message">Đang mã hoá tin nhắn</string>
<string name="not_fetching_history_retention_period">Không lấy tin nhắn do khoảng thời gian giữ lại cục bộ.</string>
<string name="transcoding_video">Đang nén video</string>
<string name="corresponding_conversations_closed">Đã đóng các cuộc hội thoại tương ứng.</string>
<string name="contact_blocked_past_tense">Đã chặn liên hệ.</string>
<string name="pref_notifications_from_strangers">Thông báo từ người lạ</string>
<string name="pref_notifications_from_strangers_summary">Thông báo về các tin nhắn và cuộc gọi được nhận từ những người lạ.</string>
<string name="received_message_from_stranger">Đã nhận tin nhắn từ người lạ</string>
<string name="block_stranger">Chặn người lạ</string>
<string name="block_entire_domain">Chặn toàn bộ miền</string>
<string name="online_right_now">trực tuyến ngay lúc này</string>
<string name="retry_decryption">Thử giải mã lại</string>
<string name="session_failure">Lỗi phiên làm việc</string>
<string name="sasl_downgrade">Cơ chế SASL đã bị hạ cấp</string>
<string name="account_status_regis_web">Máy chủ yêu cầu đăng ký trên trang web</string>
<string name="open_website">Mở trang web</string>
<string name="application_found_to_open_website">Không tìm thấy ứng dụng nào để mở trang web</string>
<string name="pref_headsup_notifications">Thông báo gây chú ý</string>
<string name="pref_headsup_notifications_summary">Hiện thông báo gây chú ý</string>
<string name="today">Hôm nay</string>
<string name="yesterday">Hôm qua</string>
<string name="pref_validate_hostname">Xác thực tên máy chủ bằng DNSSEC</string>
<string name="pref_validate_hostname_summary">Các chứng chỉ máy chủ chứa tên miền được xác thực được coi là đã xác minh</string>
<string name="certificate_does_not_contain_jid">Chứng chỉ không chứa địa chỉ XMPP hợp lệ</string>
<string name="server_info_partial">một phần</string>
<string name="attach_record_video">Ghi video</string>
<string name="copy_to_clipboard">Sao chép vào bộ nhớ tạm</string>
<string name="message_copied_to_clipboard">Đã chép tin nhắn vào clipboard</string>
<string name="message">Tin nhắn</string>
<string name="private_messages_are_disabled">Tin nhắn riêng tư bị tắt</string>
<string name="huawei_protected_apps">Ứng dụng được bảo vệ</string>
<string name="huawei_protected_apps_summary">Để tiếp tục nhận các thông báo, kể cả khi màn hình đã tắt, bạn cần thêm Conversations vào danh sách các ứng dụng được bảo vệ.</string>
<string name="mtm_accept_cert">Chấp nhận chứng chỉ không xác định?</string>
<string name="mtm_trust_anchor">Chứng chỉ máy chủ này không được một người có quyền chứng chỉ đã biết ký.</string>
<string name="mtm_accept_servername">Chấp nhận tên máy chủ không khớp?</string>
<string name="mtm_hostname_mismatch">Máy chủ không thể xác thực với tư cách \&quot;%s\&quot;. Chứng chỉ chỉ hợp lệ cho: </string>
<string name="mtm_connect_anyway">Bạn có muốn vẫn kết nối không?</string>
<string name="mtm_cert_details">Chi tiết chứng chỉ:</string>
<string name="once">Một lần</string>
<string name="qr_code_scanner_needs_access_to_camera">Trình quét mã QR cần quyền truy cập máy ảnh</string>
<string name="pref_scroll_to_bottom">Cuộn xuống dưới cùng</string>
<string name="pref_scroll_to_bottom_summary">Cuộn xuống sau khi gửi một tin nhắn</string>
<string name="edit_status_message_title">Chỉnh sửa thông báo trạng thái</string>
<string name="edit_status_message">Chỉnh sửa thông báo trạng thái</string>
<string name="disable_encryption">Tắt mã hoá</string>
<string name="error_trustkey_general">%1$s không thể gửi tin nhắn được mã hoá đến %2$s. Điều này có thể là do liên hệ của bạn sử dụng một máy chủ hoặc ứng dụng khách lỗi thời không thể xử lý OMEMO.</string>
<string name="error_trustkey_device_list">Không thể lấy danh sách thiết bị</string>
<string name="error_trustkey_bundle">Không thể lấy mã khoá mã hoá</string>
<string name="error_trustkey_hint_mutual">Gợi ý: Trong một số trường hợp, điều này có thể được sửa bằng cách thêm lẫn nhau vào danh sách liên hệ của bạn.</string>
<string name="disable_encryption_message">Bạn có chắc bạn muốn tắt mã hoá OMEMO cho cuộc hội thoại này không?\nViệc này sẽ cho phép quản trị viên máy chủ đọc các tin nhắn của bạn, nhưng việc này có thể là cách duy nhất để giao tiếp với những người sử dụng các ứng dụng khách lỗi thời.</string>
<string name="disable_now">Tắt ngay</string>
<string name="draft">Bản nháp:</string>
<string name="pref_omemo_setting">Mã hoá OMEMO</string>
<string name="pref_omemo_setting_summary_always">OMEMO sẽ luôn được sử dụng cho các cuộc trò chuyện nhóm một đối một và riêng tư.</string>
<string name="pref_omemo_setting_summary_default_on">OMEMO sẽ được sử dụng theo mặc định cho các cuộc hội thoại mới.</string>
<string name="pref_omemo_setting_summary_default_off">OMEMO sẽ phải được bật một cách rõ ràng cho các cuộc hội thoại mới.</string>
<string name="create_shortcut">Tạo lối tắt</string>
<string name="pref_font_size">Cỡ chữ</string>
<string name="pref_font_size_summary">Cỡ chữ tương đối được sử dụng trong ứng dụng.</string>
<string name="default_on">Bật theo mặc định</string>
<string name="default_off">Tắt theo mặc định</string>
<string name="small">Nhỏ</string>
<string name="medium">Trung bình</string>
<string name="large">Lớn</string>
<string name="not_encrypted_for_this_device">Tin nhắn đã không được mã hoá cho thiết bị này.</string>
<string name="omemo_decryption_failed">Giải mã tin nhắn OMEMO thất bại.</string>
<string name="undo">hoàn tác</string>
<string name="location_disabled">Chia sẻ vị trí bị tắt</string>
<string name="action_fix_to_location">Cố định vị trí</string>
<string name="action_unfix_from_location">Bỏ cố định vị trí</string>
<string name="action_copy_location">Sao chép vị trí</string>
<string name="action_share_location">Chia sẻ vị trí</string>
<string name="action_directions">Hướng</string>
<string name="title_activity_share_location">Chia sẻ vị trí</string>
<string name="title_activity_show_location">Hiện vị trí</string>
<string name="share">Chia sẻ</string>
<string name="unable_to_start_recording">Không thể bắt đầu ghi lại</string>
<string name="please_wait">Vui lòng đợi...</string>
<string name="no_microphone_permission">Cấp quyền truy cập micro cho %1$s</string>
<string name="search_messages">Tìm kiếm tin nhắn</string>
<string name="gif">GIF</string>
<string name="view_conversation">Xem cuộc hội thoại</string>
<string name="pref_use_share_location_plugin">Chia sẻ plugin vị trí</string>
<string name="pref_use_share_location_plugin_summary">Sử dụng plugin chia sẻ vị trí thay vì bản đồ được tích hợp</string>
<string name="copy_link">Sao chép địa chỉ web</string>
<string name="copy_jabber_id">Sao chép địa chỉ XMPP</string>
<string name="p1_s3_filetransfer">Chia sẻ tệp HTTP cho S3</string>
<string name="pref_start_search">Tìm kiếm trực tiếp</string>
<string name="pref_start_search_summary">Tại màn hình \'Bắt đầu cuộc hội thoại\', mở bàn phím và đặt con trỏ trong trường tìm kiếm</string>
<string name="group_chat_avatar">Ảnh đại diện cuộc trò chuyện nhóm</string>
<string name="host_does_not_support_group_chat_avatars">Máy chủ không hỗ trợ ảnh đại diện cuộc trò chuyện nhóm</string>
<string name="only_the_owner_can_change_group_chat_avatar">Chỉ có chủ sở hữu mới có thể thay đổi ảnh đại diện cuộc trò chuyện nhóm</string>
<string name="contact_name">Tên liên hệ</string>
<string name="nickname">Biệt danh</string>
<string name="group_chat_name">Tên</string>
<string name="providing_a_name_is_optional">Việc cung cấp tên là không bắt buộc</string>
<string name="create_dialog_group_chat_name">Tên cuộc trò chuyện nhóm</string>
<string name="conference_destroyed">Cuộc trò chuyện nhóm này đã bị phá huỷ</string>
<string name="unable_to_save_recording">Không thể lưu bản ghi</string>
<string name="foreground_service_channel_name">Dịch vụ ở trước</string>
<string name="foreground_service_channel_description">Hạng mục thông báo này được sử dụng để hiển thị một thông báo vĩnh viễn chỉ ra rằng %1$s đang chạy.</string>
<string name="notification_group_status_information">Thông tin trạng thái</string>
<string name="error_channel_name">Vấn đề kết nối</string>
<string name="error_channel_description">Hạng mục thông báo này được sử dụng để hiển thị một thông báo trong trường hợp có vấn đề khi kết nối đến một tài khoản.</string>
<string name="notification_group_messages">Tin nhắn</string>
<string name="notification_group_calls">Cuộc gọi</string>
<string name="messages_channel_name">Tin nhắn</string>
<string name="incoming_calls_channel_name">Cuộc gọi đến</string>
<string name="ongoing_calls_channel_name">Cuộc gọi đang diễn ra</string>
<string name="silent_messages_channel_name">Tin nhắn im lặng</string>
<string name="silent_messages_channel_description">Nhóm thông báo này được sử dụng để hiển thị các thông báo không nên phát ra tiếng động. Ví dụ là khi đang hoạt động trên một thiết bị khác (thời gian ân hạn).</string>
<string name="delivery_failed_channel_name">Gửi đi thất bại</string>
<string name="pref_message_notification_settings">Cài đặt thông báo tin nhắn</string>
<string name="pref_incoming_call_notification_settings">Cài đặt thông báo cuộc gọi đến</string>
<string name="pref_more_notification_settings_summary">Sự quan trọng, âm thanh, rung</string>
<string name="video_compression_channel_name">Nén video</string>
<string name="view_media">Xem phương tiện</string>
<string name="group_chat_members">Thành viên</string>
<string name="media_browser">Trình duyệt phương tiện</string>
<string name="security_violation_not_attaching_file">Tệp đã bị bỏ vì vi phạm bảo mật.</string>
<string name="pref_video_compression">Chất lượng video</string>
<string name="pref_video_compression_summary">Chất lượng thấp hơn có nghĩa là tệp nhỏ hơn</string>
<string name="video_360p">Trung bình (360p)</string>
<string name="video_720p">Cao (720p)</string>
<string name="cancelled">đã huỷ</string>
<string name="already_drafting_message">Bạn đã đang tạo bản nháp một tin nhắn rồi.</string>
<string name="feature_not_implemented">Tính năng chưa được thêm</string>
<string name="invalid_country_code">Mã quốc gia không hợp lệ</string>
<string name="choose_a_country">Chọn quốc gia</string>
<string name="phone_number">số điện thoại</string>
<string name="verify_your_phone_number">Xác minh số điện thoại của bạn</string>
<string name="enter_country_code_and_phone_number">Quicksy sẽ gửi một tin nhắn SMS (có thể áp dụng phí nhà mạng) để xác minh số điện thoại của bạn. Hãy nhập mã quốc gia và số điện thoại của bạn:</string>
<string name="we_will_be_verifying"><![CDATA[Chúng tôi sẽ xác minh số điện thoại<br/><br/><b>%s</b><br/><br/>Điều này có ổn không, hay bạn muốn chỉnh sửa số điện thoại?]]></string>
<string name="not_a_valid_phone_number">%s không phải là số điện thoại hợp lệ.</string>
<string name="please_enter_your_phone_number">Vui lòng nhập số điện thoại của bạn.</string>
<string name="search_countries">Tìm kiếm quốc gia</string>
<string name="verify_x">Xác minh %s</string>
<string name="we_have_sent_you_an_sms_to_x"><![CDATA[Chúng tôi đã gửi một SMS cho bạn để <b>%s</b>.]]></string>
<string name="we_have_sent_you_another_sms">Chúng tôi đã gửi một SMS khác có mã 6 chữ số cho bạn.</string>
<string name="please_enter_pin_below">Vui lòng nhập mã PIN 6 chữ số ở dưới.</string>
<string name="resend_sms">Gửi lại SMS</string>
<string name="resend_sms_in">Gửi lại SMS (%s)</string>
<string name="wait_x">Vui lòng đợi (%s)</string>
<string name="back">quay lại</string>
<string name="possible_pin">Đã tự động dán mã PIN có thể có từ bộ nhớ tạm.</string>
<string name="please_enter_pin">Vui lòng nhập mã PIN 6 chữ số.</string>
<string name="abort_registration_procedure">Bạn có chắc bạn muốn huỷ quá trình đăng ký không?</string>
<string name="yes"></string>
<string name="no">Không</string>
<string name="verifying">Đang xác minh...</string>
<string name="requesting_sms">Đang yêu cầu SMS...</string>
<string name="incorrect_pin">Mã PIN bạn đã nhập không chính xác.</string>
<string name="pin_expired">Mã PIN chúng tôi gửi cho bạn đã hết hạn.</string>
<string name="unknown_api_error_network">Lỗi mạng không xác định.</string>
<string name="unknown_api_error_response">Phản hồi không xác định từ máy chủ.</string>
<string name="unable_to_connect_to_server">Không thể kết nối đến máy chủ.</string>
<string name="unable_to_establish_secure_connection">Không thể lập kết nối bảo mật.</string>
<string name="unable_to_find_server">Không thể tìm máy chủ.</string>
<string name="something_went_wrong_processing_your_request">Có gì đó sai đã xảy ra khi xử lý yêu cầu của bạn.</string>
<string name="invalid_user_input">Đầu vào người dùng không hợp lệ</string>
<string name="temporarily_unavailable">Tạm thời không có sẵn. Hãy thử lại sau.</string>
<string name="no_network_connection">Không có kết nối mạng.</string>
<string name="try_again_in_x">Vui lòng thử lại trong %s</string>
<string name="rate_limited">Bạn bị giới hạn tốc độ</string>
<string name="too_many_attempts">Quá nhiều lần thử</string>
<string name="the_app_is_out_of_date">Bạn đang sử dụng một phiên bản lỗi thời của ứng dụng này.</string>
<string name="update">Cập nhật</string>
<string name="logged_in_with_another_device">Số điện thoại này hiện đã được đăng nhập ở một thiết bị khác.</string>
<string name="enter_your_name_instructions">Vui lòng nhập tên của bạn để cho những người không có bạn trong sổ địa chỉ của họ biết bạn là ai.</string>
<string name="your_name">Tên của bạn</string>
<string name="enter_your_name">Nhập tên của bạn</string>
<string name="no_name_set_instructions">Sử dụng nút chỉnh sửa để đặt tên của bạn.</string>
<string name="reject_request">Từ chối yêu cầu</string>
<string name="install_orbot">Cài đặt Orbot</string>
<string name="start_orbot">Khởi động Orbot</string>
<string name="no_market_app_installed">Không có ứng dụng chợ nào được cài đặt.</string>
<string name="group_chat_will_make_your_jabber_id_public">Kênh này sẽ làm cho địa chỉ XMPP của bạn trở thành công khai</string>
<string name="ebook">sách điện tử</string>
<string name="video_original">Gốc (không nén)</string>
<string name="open_with">Mở bằng...</string>
<string name="set_profile_picture">Ảnh hồ sơ Conversations</string>
<string name="choose_account">Chọn tài khoản</string>
<string name="restore_backup">Khôi phục bản sao lưu</string>
<string name="restore">Khôi phục</string>
<string name="enter_password_to_restore">Nhập mật khẩu của bạn cho tài khoản %s để khôi phục bản sao lưu.</string>
<string name="restore_warning">Đừng sử dụng tính năng khôi phục bản sao lưu để cố gắng nhân bản (chạy đồng thời) một lượt cài đặt. Việc khôi phục một bản sao lưu chỉ dành cho việc di cư hoặc trong trường hợp bạn đã mất thiết bị gốc.</string>
<string name="unable_to_restore_backup">Không thể khôi phục bản sao lưu.</string>
<string name="unable_to_decrypt_backup">Không thể giải mã bản sao lưu. Mật khẩu có đúng không?</string>
<string name="backup_channel_name">Sao lưu &amp; khôi phục</string>
<string name="enter_jabber_id">Nhập địa chỉ XMPP</string>
<string name="create_group_chat">Tạo cuộc trò chuyện nhóm</string>
<string name="join_public_channel">Tham gia kênh công khai</string>
<string name="create_private_group_chat">Tạo cuộc trò chuyện nhóm riêng tư</string>
<string name="create_public_channel">Tạo kênh công khai</string>
<string name="create_dialog_channel_name">Tên kênh</string>
<string name="xmpp_address">Địa chỉ XMPP</string>
<string name="please_enter_name">Vui lòng cung cấp tên cho kênh</string>
<string name="please_enter_xmpp_address">Vui lòng cung cấp địa chỉ XMPP</string>
<string name="this_is_an_xmpp_address">Đây là một địa chỉ XMPP. Vui lòng cung cấp một cái tên.</string>
<string name="creating_channel">Đang tạo kênh công khai...</string>
<string name="channel_already_exists">Kênh này đã tồn tại</string>
<string name="joined_an_existing_channel">Bạn đã tham gia một kênh đang tồn tại</string>
<string name="unable_to_set_channel_configuration">Không thể lưu thiết lập kênh</string>
<string name="allow_participants_to_edit_subject">Cho phép bất kỳ ai chỉnh sửa chủ đề</string>
<string name="allow_participants_to_invite_others">Cho phép bất kỳ ai mời những người khác</string>
<string name="anyone_can_edit_subject">Bất kỳ ai cũng có thể chỉnh sửa chủ đề.</string>
<string name="owners_can_edit_subject">Chủ sở hữu có thể chỉnh sửa chủ đề.</string>
<string name="admins_can_edit_subject">Quản trị viên có thể chỉnh sửa chủ đề.</string>
<string name="owners_can_invite_others">Chủ sở hữu có thể mời những người khác.</string>
<string name="anyone_can_invite_others">Bất kỳ ai cũng có thể mời những người khác.</string>
<string name="jabber_ids_are_visible_to_admins">Các địa chỉ XMPP có thể được quản trị viên nhìn thấy.</string>
<string name="jabber_ids_are_visible_to_anyone">Các địa chỉ XMPP có thể được bất kỳ ai nhìn thấy.</string>
<string name="no_users_hint_channel">Kênh công khai này không có thành viên nào. Hãy mời các liên hệ của bạn hoặc sử dụng nút chia sẻ để phân phát địa chỉ XMPP của kênh.</string>
<string name="no_users_hint_group_chat">Cuộc trò chuyện nhóm riêng tư này không có thành viên nào.</string>
<string name="manage_permission">Quản lý đặc quyền</string>
<string name="search_participants">Tìm kiếm thành viên</string>
<string name="file_too_large">Tệp quá lớn</string>
<string name="attach">Đính kèm</string>
<string name="discover_channels">Khám phá các kênh</string>
<string name="search_channels">Tìm kiếm kênh</string>
<string name="channel_discovery_opt_in_title">Sự vi phạm tính riêng tư có thể có!</string>
<string name="channel_discover_opt_in_message"><![CDATA[Khám phá kênh sử dụng một dịch vụ bên thứ ba được gọi là <a href=\"https://search.jabber.network\">search.jabber.network</a>.<br><br>Việc sử dụng tính năng này sẽ truyền địa chỉ IP và câu từ tìm kiếm của bạn đến dịch vụ đó. Hãy xem <a href=\"https://search.jabber.network/privacy\">Chính sách riêng tư</a> của họ để biết thêm thông tin.]]></string>
<string name="i_already_have_an_account">Tôi đã có một tài khoản rồi</string>
<string name="add_existing_account">Thêm tài khoản đang tồn tại</string>
<string name="register_new_account">Đăng ký tài khoản mới</string>
<string name="this_looks_like_a_domain">Cái này trông giống một địa chỉ miền</string>
<string name="add_anway">Vẫn thêm</string>
<string name="this_looks_like_channel">Cái này trông giống một địa chỉ kênh</string>
<string name="share_backup_files">Chia sẻ tệp sao lưu</string>
<string name="conversations_backup">Bản sao lưu Conversations</string>
<string name="event">Sự kiện</string>
<string name="open_backup">Mở bản sao lưu</string>
<string name="not_a_backup_file">Tệp bạn đã chọn không phải là tệp sao lưu của Conversations</string>
<string name="account_already_setup">Tài khoản này đã được thiết lập rồi</string>
<string name="please_enter_password">Vui lòng nhập mật khẩu cho tài khoản này</string>
<string name="unable_to_perform_this_action">Không thể thực hiện hành động này</string>
<string name="open_join_dialog">Tham gia kênh công khai...</string>
<string name="sharing_application_not_grant_permission">Ứng dụng chia sẻ đã không cấp quyền truy cập tệp này.</string>
<string name="group_chats_and_channels"><![CDATA[Cuộc trò chuyện nhóm & Kênh]]></string>
<string name="jabber_network">jabber.network</string>
<string name="local_server">Máy chủ cục bộ</string>
<string name="pref_channel_discovery_summary">Đa số người dùng nên chọn \'jabber.network\' để có những đề xuất tốt hơn từ toàn thể hệ sinh thái XMPP.</string>
<string name="pref_channel_discovery">Phương pháp khám phá kênh</string>
<string name="backup">Sao lưu</string>
<string name="category_about">Giới thiệu</string>
<string name="please_enable_an_account">Vui lòng bật một tài khoản</string>
<string name="make_call">Tạo cuộc gọi</string>
<string name="rtp_state_incoming_call">Cuộc gọi đến</string>
<string name="rtp_state_incoming_video_call">Cuộc gọi video đến</string>
<string name="rtp_state_connecting">Đang kết nối</string>
<string name="rtp_state_connected">Đã kết nối</string>
<string name="rtp_state_accepting_call">Đang chấp nhận cuộc gọi</string>
<string name="rtp_state_ending_call">Đang kết thúc cuộc gọi</string>
<string name="answer_call">Trả lời</string>
<string name="dismiss_call">Từ chối</string>
<string name="rtp_state_finding_device">Đang khám phá các thiết bị</string>
<string name="rtp_state_ringing">Đang đổ chuông</string>
<string name="rtp_state_declined_or_busy">Bận</string>
<string name="rtp_state_connectivity_error">Không thể kết nối cuộc gọi</string>
<string name="rtp_state_connectivity_lost_error">Đã mất kết nối</string>
<string name="rtp_state_retracted">Cuộc gọi đã bị rút lại</string>
<string name="rtp_state_application_failure">Lỗi ứng dụng</string>
<string name="hang_up">Cúp máy</string>
<string name="ongoing_call">Cuộc gọi đang diễn ra</string>
<string name="ongoing_video_call">Cuộc gọi video đang diễn ra</string>
<string name="disable_tor_to_make_call">Tắt Tor để tạo cuộc gọi</string>
<string name="incoming_call">Cuộc gọi đến</string>
<string name="incoming_call_duration">Cuộc gọi đến · %s</string>
<string name="missed_call_timestamp">Cuộc gọi nhỡ · %s</string>
<string name="outgoing_call">Cuộc gọi đi</string>
<string name="outgoing_call_duration">Cuộc gọi đi · %s</string>
<string name="missed_call">Cuộc gọi nhỡ</string>
<string name="audio_call">Cuộc gọi âm thanh</string>
<string name="video_call">Cuộc gọi video</string>
<string name="help">Trợ giúp</string>
<string name="switch_to_conversation">Chuyển sang cuộc hội thoại</string>
<string name="microphone_unavailable">Micro của bạn không có sẵn</string>
<string name="only_one_call_at_a_time">Bạn chỉ có thể có một cuộc gọi trong một lúc.</string>
<string name="return_to_ongoing_call">Quay lại cuộc gọi đang diễn ra</string>
<string name="could_not_switch_camera">Không thể chuyển máy ảnh</string>
<string name="add_to_favorites">Ghim lên đầu</string>
<string name="remove_from_favorites">Bỏ ghim khỏi đầu</string>
<string name="gpx_track">Tuyến đường GPS</string>
<string name="could_not_correct_message">Không thể sửa tin nhắn</string>
<string name="search_all_conversations">Tất cả cuộc hội thoại</string>
<string name="search_this_conversation">Cuộc hội thoại này</string>
<string name="your_avatar">Ảnh đại diện của bạn</string>
<string name="avatar_for_x">Ảnh đại diện cho %s</string>
<string name="encrypted_with_omemo">Được mã hoá bằng OMEMO</string>
<string name="encrypted_with_openpgp">Được mã hoá bằng OpenPGP</string>
<string name="not_encrypted">Không được mã hoá</string>
<string name="exit">Thoát</string>
<string name="record_voice_mail">Ghi lại tin nhắn thoại</string>
<string name="play_audio">Phát âm thanh</string>
<string name="pause_audio">Tạm dừng âm thanh</string>
<string name="add_contact_or_create_or_join_group_chat">Thêm liên hệ, tạo hoặc tham gia cuộc trò chuyện nhóm, hoặc khám phá các kênh</string>
<plurals name="view_users">
<item quantity="other">Xem %1$d thành viên</item>
</plurals>
<plurals name="some_messages_could_not_be_delivered">
<item quantity="other">Một số tin nhắn không thể được gửi</item>
</plurals>
<string name="failed_deliveries">Gửi đi thất bại</string>
<string name="more_options">Thêm tuỳ chọn</string>
<string name="no_application_found">Không tìm thấy ứng dụng nào</string>
<string name="invite_to_app">Mời vào Conversations</string>
<string name="unable_to_parse_invite">Không thể xử lý lời mời</string>
<string name="server_does_not_support_easy_onboarding_invites">Máy chủ không hỗ trợ tạo lời mời</string>
<string name="no_active_accounts_support_this">Không có tài khoản đang hoạt động nào hỗ trợ tính năng này</string>
<string name="backup_started_message">Việc sao lưu đã được bắt đầu. Bạn sẽ nhận một thông báo khi việc đó đã hoàn tất.</string>
<string name="unable_to_enable_video">Không thể bật video.</string>
</resources>

View File

@ -129,6 +129,8 @@
<string name="pref_never_send_crash_summary">通过发送堆栈跟踪您可以帮助Conversations持续发展</string>
<string name="pref_confirm_messages">确认消息</string>
<string name="pref_confirm_messages_summary">让对方知道你收到并阅读了他们的消息</string>
<string name="pref_prevent_screenshots">防止截屏</string>
<string name="pref_prevent_screenshots_summary">在应用切换中隐藏应用程序内容并阻止截图</string>
<string name="pref_ui_options">用户界面</string>
<string name="openpgp_error">OpenKeychain报告一个错误。</string>
<string name="bad_key_for_encryption">错误的密钥</string>
@ -411,6 +413,7 @@
<string name="audio">音频</string>
<string name="video">视频</string>
<string name="image">图片</string>
<string name="vector_graphic">矢量图</string>
<string name="pdf_document">PDF文档</string>
<string name="apk">Android App</string>
<string name="vcard">联系人</string>
@ -901,6 +904,7 @@
<string name="rtp_state_connectivity_lost_error">连接丢失</string>
<string name="rtp_state_retracted">通话已撤销</string>
<string name="rtp_state_application_failure">程序错误</string>
<string name="rtp_state_security_error">验证问题</string>
<string name="hang_up">挂断</string>
<string name="ongoing_call">正在进行的通话</string>
<string name="ongoing_video_call">正在进行的视频通话</string>
@ -950,4 +954,6 @@
<string name="no_active_accounts_support_this">没有活跃帐户支持此功能</string>
<string name="backup_started_message">已启动备份。一旦完成,你会收到通知。</string>
<string name="unable_to_enable_video">无法启用视频</string>
<string name="plain_text_document">纯文本文档</string>
</resources>

View File

@ -62,6 +62,7 @@
<attr name="icon_alpha" format="float" />
<attr name="delete_icon_alpha" format="float" />
<attr name="media_preview_image" format="reference" />
<attr name="media_preview_document" format="reference" />
<attr name="media_preview_recording" format="reference" />
<attr name="media_preview_audio" format="reference" />

View File

@ -43,4 +43,5 @@
<bool name="start_searching">false</bool>
<string name="video_compression">360</string>
<string name="default_channel_discovery">JABBER_NETWORK</string>
<bool name="prevent_screenshots">false</bool>
</resources>

View File

@ -130,6 +130,8 @@
<string name="pref_never_send_crash_summary">By sending in stack traces you are helping the development</string>
<string name="pref_confirm_messages">Confirm Messages</string>
<string name="pref_confirm_messages_summary">Let your contacts know when you have received and read their messages</string>
<string name="pref_prevent_screenshots">Prevent Screenshots</string>
<string name="pref_prevent_screenshots_summary">Hide app contents in the app switcher and block screenshots</string>
<string name="pref_ui_options">UI</string>
<string name="openpgp_error">OpenKeychain produced an error.</string>
<string name="bad_key_for_encryption">Bad key for encryption.</string>
@ -412,6 +414,7 @@
<string name="audio">audio</string>
<string name="video">video</string>
<string name="image">image</string>
<string name="vector_graphic">vector graphic</string>
<string name="pdf_document">PDF document</string>
<string name="apk">Android App</string>
<string name="vcard">Contact</string>
@ -912,6 +915,7 @@
<string name="rtp_state_connectivity_lost_error">Connection lost</string>
<string name="rtp_state_retracted">Retracted call</string>
<string name="rtp_state_application_failure">App failure</string>
<string name="rtp_state_security_error">Verification problem</string>
<string name="hang_up">Hang up</string>
<string name="ongoing_call">Ongoing call</string>
<string name="ongoing_video_call">Ongoing video call</string>
@ -963,4 +967,6 @@
<string name="no_active_accounts_support_this">No active accounts support this feature</string>
<string name="backup_started_message">The backup has been started. Youll get a notification once it has been completed.</string>
<string name="unable_to_enable_video">Unable to enable video.</string>
<string name="plain_text_document">Plain text document</string>
</resources>

View File

@ -80,8 +80,8 @@
<item name="dialog_horizontal_padding">24dp</item>
<item name="dialog_vertical_padding">16dp</item>
<item name="media_preview_document" type="reference">@drawable/ic_description_black_48dp
</item>
<item name="media_preview_image" type="reference">@drawable/ic_image_black_48dp</item>
<item name="media_preview_document" type="reference">@drawable/ic_description_black_48dp</item>
<item name="media_preview_recording" type="reference">@drawable/ic_mic_black_48dp</item>
<item name="media_preview_audio" type="reference">@drawable/ic_headset_black_48dp</item>
<item name="media_preview_location" type="reference">@drawable/ic_room_black_48dp</item>
@ -236,8 +236,8 @@
<item name="dialog_horizontal_padding">24dp</item>
<item name="dialog_vertical_padding">16dp</item>
<item name="media_preview_document" type="reference">@drawable/ic_description_white_48dp
</item>
<item name="media_preview_image" type="reference">@drawable/ic_image_white_48dp</item>
<item name="media_preview_document" type="reference">@drawable/ic_description_white_48dp</item>
<item name="media_preview_recording" type="reference">@drawable/ic_mic_white_48dp</item>
<item name="media_preview_audio" type="reference">@drawable/ic_headset_white_48dp</item>
<item name="media_preview_location" type="reference">@drawable/ic_room_white_48dp</item>

View File

@ -32,6 +32,13 @@
android:key="last_activity"
android:summary="@string/pref_broadcast_last_activity_summary"
android:title="@string/pref_broadcast_last_activity" />
<CheckBoxPreference
android:defaultValue="@bool/prevent_screenshots"
android:key="prevent_screenshots"
android:summary="@string/pref_prevent_screenshots_summary"
android:title="@string/pref_prevent_screenshots" />
<ListPreference
android:defaultValue="@string/omemo_setting_default"
android:entries="@array/omemo_setting_entries"

View File

@ -5,9 +5,7 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import java.io.IOException;
import com.google.firebase.installations.FirebaseInstallations;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.utils.Compatibility;
@ -23,16 +21,10 @@ public class MaintenanceReceiver extends BroadcastReceiver {
}
private void renewInstanceToken(final Context context) {
new Thread(() -> {
try {
FirebaseInstanceId.getInstance().deleteInstanceId();
FirebaseInstallations.getInstance().delete().addOnSuccessListener(unused -> {
final Intent intent = new Intent(context, XmppConnectionService.class);
intent.setAction(XmppConnectionService.ACTION_FCM_TOKEN_REFRESH);
Compatibility.startService(context, intent);
} catch (IOException e) {
Log.d(Config.LOGTAG, "unable to renew instance token", e);
}
}).start();
});
}
}

Some files were not shown because too many files have changed in this diff Show More