Detailed changes
@@ -22,6 +22,11 @@ tasks:
echo y | android/cmdline-tools/tools/bin/sdkmanager "build-tools;29.0.2"
touch ~/.android/repositories.cfg
yes | android/cmdline-tools/tools/bin/sdkmanager --licenses
+- libwebrtc: |
+ cd cheogram-android
+ mkdir libs
+ cd libs
+ wget -qO libwebtrc.aar https://cloudflare-ipfs.com/ipfs/QmeqMiLxHi8AAjXobxr3QTfa1bSSLyAu86YviAqQnjxCjM/libwebrtc.aar
- build: |
cd cheogram-android
./gradlew assembleCheogramFreeSystemDebug
@@ -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
+
+
@@ -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"
@@ -1,5 +1,21 @@
# Changelog
+### Version 2.10.2
+
+* Fix crash when rendering some quotes
+* Fix crash in welcome screen
+
+### Version 2.10.1
+
+* Fix issue with some videos not being compressed
+* Fix rare crash when opening notification
+
+### 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
@@ -1,5 +1,3 @@
-import com.android.build.OutputFile
-
// Top-level build file where you can add configuration options common to all
// sub-projects/modules.
buildscript {
@@ -8,7 +6,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.1'
+ classpath 'com.android.tools.build:gradle:7.1.1'
}
}
@@ -35,23 +33,23 @@ configurations {
dependencies {
implementation 'androidx.viewpager:viewpager:1.0.0'
- playstoreImplementation('com.google.firebase:firebase-messaging:22.0.0') {
+ playstoreImplementation('com.google.firebase:firebase-messaging:23.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:18.0.1'
+ quicksyPlaystoreSystemImplementation 'com.google.android.gms:play-services-auth-api-phone:18.0.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.appcompat:appcompat:1.3.1'
+ 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'
- implementation 'com.google.android.material:material:1.3.0'
+ implementation 'com.google.android.material:material:1.4.0'
compatImplementation 'androidx.emoji:emoji-appcompat:1.1.0'
conversationsFreeCompatImplementation 'androidx.emoji:emoji-bundled:1.1.0'
quicksyFreeCompatImplementation 'androidx.emoji:emoji-bundled:1.1.0'
@@ -64,21 +62,22 @@ dependencies {
implementation 'org.whispersystems:signal-protocol-java:2.6.2'
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:1.0.1'
+ implementation 'com.otaliastudios:transcoder:0.10.4'
+
+ implementation 'org.jxmpp:jxmpp-jid:1.0.2'
implementation 'org.osmdroid:osmdroid-android:6.1.10'
implementation 'org.hsluv:hsluv:0.2'
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.leinardi.android:speed-dial:3.2.0"
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.squareup.okhttp3:okhttp:4.9.3"
implementation 'com.google.guava:guava:30.1.1-android'
- quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.12.18'
- implementation 'org.webrtc:google-webrtc:1.0.32006'
+ quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.12.36'
+ implementation fileTree(include: ['libwebrtc.aar'], dir: 'libs')
}
ext {
@@ -93,8 +92,8 @@ android {
defaultConfig {
minSdkVersion 24
targetSdkVersion 29
- versionCode 42015
- versionName "2.9.13"
+ versionCode 42024
+ versionName "2.10.3-beta"
archivesBaseName += "-$versionName"
applicationId "eu.siacs.conversations"
resValue "string", "applicationId", applicationId
@@ -105,19 +104,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
@@ -292,10 +285,25 @@ android {
}
}
-
packagingOptions {
- exclude 'META-INF/BCKEY.DSA'
- exclude 'META-INF/BCKEY.SF'
+ resources {
+ excludes += ['META-INF/BCKEY.DSA', 'META-INF/BCKEY.SF']
+ }
}
+ lint {
+ disable 'MissingTranslation', 'InvalidPackage', 'AppCompatResource'
+ }
+
+
+ android.applicationVariants.all { variant ->
+ variant.outputs.each { output ->
+ def baseAbiVersionCode = project.ext.abiCodes.get(output.getFilter(com.android.build.OutputFile.ABI))
+ if (baseAbiVersionCode != null) {
+ output.versionCodeOverride = (100 * project.android.defaultConfig.versionCode) + baseAbiVersionCode
+ } else {
+ output.versionCodeOverride = 100 * project.android.defaultConfig.versionCode
+ }
+ }
+ }
}
@@ -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>
@@ -0,0 +1,3 @@
+* Show black bars when remote video does not match aspect ratio of screen
+* Improve search performance
+* Add setting to prevent screenshots
@@ -0,0 +1,2 @@
+* Fix issue with some videos not being compressed
+* Fix rare crash when opening notification
@@ -0,0 +1,2 @@
+* Fix crash when rendering some quotes
+* Fix crash in welcome screen
@@ -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.7.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
@@ -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>;
}
@@ -29,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 {
@@ -54,6 +55,12 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
this.binding.list.setAdapter(this.backupFileAdapter);
this.backupFileAdapter.setOnItemClickedListener(this);
}
+
+ @Override
+ protected void onResume(){
+ super.onResume();
+ SettingsUtils.applyScreenshotPreventionSetting(this);
+ }
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
@@ -228,6 +228,7 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0) {
if (allGranted(grantResults)) {
switch (requestCode) {
@@ -106,7 +106,8 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
}
@Override
- public void onNewIntent(Intent intent) {
+ public void onNewIntent(final Intent intent) {
+ super.onNewIntent(intent);
if (intent != null) {
setIntent(intent);
}
@@ -201,6 +202,7 @@ public class WelcomeActivity extends XmppActivity implements XmppConnectionServi
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
UriHandlerActivity.onRequestPermissionResult(this, requestCode, grantResults);
if (grantResults.length > 0) {
if (allGranted(grantResults)) {
@@ -0,0 +1,11 @@
+package eu.siacs.conversations.utils;
+
+import android.content.Context;
+
+import eu.siacs.conversations.xmpp.Jid;
+
+public class PhoneNumberUtilWrapper {
+ public static String toFormattedPhoneNumber(Context context, Jid jid) {
+ throw new AssertionError("This method is not implemented in Conversations");
+ }
+}
@@ -3,4 +3,18 @@
<string name="pick_a_server">اختر مزود خدمة XMPP الخاص بك</string>
<string name="use_conversations.im">استخدِم conversations.im</string>
<string name="create_new_account">أنشئ حسابًا جديدًا</string>
- </resources>
+ <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>
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="pick_a_server">Изберете вашият XMPP доставчик</string>
+ <string name="pick_a_server">Изберете своя XMPP доставчик</string>
<string name="use_conversations.im">Използвайте conversations.im</string>
<string name="create_new_account">Създаване не нов профил</string>
- <string name="do_you_have_an_account">Имате ли вече XMPP профил? Това може да се случи, ако вече използвате друг клиент на XMPP или сте използвали преди това Conversations. Ако не, можете да създадете нов XMPP профил в момента.\nСъвет: Някои доставчици на имейл също предоставят XMPP профили.
+ <string name="do_you_have_an_account">Имате ли вече XMPP профил? Може да имате, ако вече използвате друг клиент на XMPP или сте използвали Conversations и преди. Ако не, можете да създадете нов XMPP профил сега.\nСъвет: някои доставчици на е-поща също предоставят XMPP профили.
</string>
- <string name="server_select_text">XMPP е мрежа за общуване чрез мигновени съобщения, която не е обвързана с конкретен доставчик. Можете да използвате клиента с всеки сървър, който работи с XMPP.\nЗа Ваше удобство, ние предоставяме лесен начин да си създадете профил в conversations.im¹ — сървър, пригоден да работи добре с Conversations.</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="server_select_text">XMPP е мрежа за общуване чрез мигновени съобщения, която не е обвързана с конкретен доставчик. Можете да използвате клиента с всеки сървър, който работи с XMPP.\nЗа Ваше удобство, обаче, ние предоставяме лесен начин да си създадете профил в conversations.im¹ — сървър, пригоден да работи най-добре с Conversations.</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="your_server_invitation">Вашата покана за сървъра</string>
<string name="improperly_formatted_provisioning">Неправилно форматиран код за достъп</string>
<string name="tap_share_button_send_invite">Докоснете бутона за споделяне, за да изпратите на контакта си покана за %1$s.</string>
@@ -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>
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="pick_a_server">Valitse XMPP-palveluntarjoaja</string>
+ <string name="use_conversations.im">Käytä conversations.im:ää</string>
+ <string name="create_new_account">Luo uusi tili</string>
+ <string name="do_you_have_an_account">Onko sinulla jo XMPP-tunnus? Jos käytät jo toista XMPP-sovellusta tai olet käyttänyt Conversationsia aiemmin, niin voi olla. Jos ei, voit tehdä uuden XMPP-tilin saman tien.\nVinkki: Jotkin sähköpostipalvelut tarjoavat myös XMPP-tilin.</string>
+ <string name="server_select_text">XMPP on tietystä palveluntarjoasta riippumaton pikaviestiverkosto. Voit käyttää tätä asiakasohjelmaa minkä tahansa haluamasi XMPP-palvelimen kanssa.\nHelppouden nimissä olemme kuitenkin helpottaneet tilin luomista conversations.im:iin.</string>
+ <string name="magic_create_text_on_x">Sinut on kutsuttu %1$s:iin. Opastamme sinua tilin luomisen kanssa.\nValitessasi palvelimen %1$s palveluntarjoajaksesi voit jutella muiden palveluntajoajien käyttäjien kanssa kertomalla heille koko XMPP-osoitteesi.</string>
+ <string name="magic_create_text_fixed">Sinut on kutsuttu palvelimelle %1$s. Käyttäjänimesi on valittu valmiiksi puolestasi. Opastamme sinua tilin luomisen kanssa.\nVoit jutella muiden palveluntarjoajien käyttäjien kanssa kertomalle heille koko XMPP-osoitteesi.</string>
+ <string name="your_server_invitation">Kutsusi palvelimelle</string>
+ <string name="improperly_formatted_provisioning">Virheellisesti muotoiltu koodi</string>
+ <string name="if_contact_is_nearby_use_qr">Jos henkilö on lähellä, hän voi myös hyväksyä kutsun lukemalla allaolevan koodin.</string>
+ <string name="share_invite_with">Jaa kutsu sovelluksella...</string>
+</resources>
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="pick_a_server">Scegli il tuo provider XMPP</string>
+ <string name="pick_a_server">Scegli il tuo fornitore XMPP</string>
<string name="use_conversations.im">Usa conversations.im</string>
- <string name="create_new_account">Crea un nuovo account</string>
- <string name="do_you_have_an_account">Possiedi già un account XMPP? Questo succede se stai già usando un diverso client XMPP o hai già usato prima Conversations. In caso negativo puoi creare un account XMPP adesso.
+ <string name="create_new_account">Crea un nuovo profilo</string>
+ <string name="do_you_have_an_account">Possiedi già un profilo XMPP? Questo succede se stai già usando un diverso client XMPP o hai già usato prima Conversations. In caso negativo puoi creare un profilo XMPP adesso.
Suggerimento: alcuni provider di email forniscono anche un account XMPP.</string>
- <string name="server_select_text">XMPP è una rete di instant messaging indipendente dal provider. Puoi usare questo client con qualsiasi server XMPP.
-In ogni caso per facilitare puoi creare facilmente un account su conversations.im, un provider pensato apposta per essere usato con Conversations.</string>
- <string name="magic_create_text_on_x">Sei stato invitato su %1$s. Ti guideremo nel procedimento per creare un account.\nQuando scegli %1$s come fornitore sarai in grado di comunicare con utenti di altri fornitori dando loro l\'indirizzo XMPP completo.</string>
- <string name="magic_create_text_fixed">Sei stato invitato su %1$s. È già stato scelto un nome utente per te. Ti guideremo nel procedimento per creare un account.\nSarai in grado di comunicare con utenti di altri fornitori dando loro l\'indirizzo XMPP completo.</string>
+ <string name="server_select_text">XMPP è una rete di messaggistica istantanea indipendente dal fornitore. Puoi usare questo client con qualsiasi server XMPP.
+In ogni caso per facilitare puoi creare facilmente un account su conversations.im, un fornitore pensato apposta per essere usato con Conversations.</string>
+ <string name="magic_create_text_on_x">Hai ricevuto un invito per %1$s. Ti guideremo nel procedimento per creare un profilo.\nQuando scegli %1$s come fornitore sarai in grado di comunicare con utenti di altri fornitori dando loro l\'indirizzo XMPP completo.</string>
+ <string name="magic_create_text_fixed">Hai ricevuto un invito per %1$s. È già stato scelto un nome utente per te. Ti guideremo nel procedimento per creare un profilo.\nSarai in grado di comunicare con utenti di altri fornitori dando loro l\'indirizzo XMPP completo.</string>
<string name="your_server_invitation">Il tuo invito al server</string>
<string name="improperly_formatted_provisioning">Codice di approvvigionamento formattato male</string>
<string name="tap_share_button_send_invite">Tocca il pulsante condividi per inviare al contatto un invito per %1$s.</string>
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="pick_a_server">XMPPプロバイダーを選択してください</string>
- <string name="use_conversations.im">conversations.imを利用する</string>
- <string name="create_new_account">アカウントを作成</string>
- <string name="do_you_have_an_account">XMPPアカウントをお持ちですか?既にほかのXMPPクライアントを利用しているか、Conversationsを利用したことがある場合はこちら。初めての方は、今すぐ新しいXMPPアカウントを作成できます。\nヒント: eメールのプロバイダーがXMPPアカウントも提供している場合があります。</string>
- <string name="server_select_text">XMPPは、プロバイダーに依存しないインスタントメッセージのプロトコルです。XMPPサーバーならどこでも、このクライアントを使用することができます。\nよろしければ、Conversationsに最適化されたプロバイダーconversations.im¹で簡単にアカウントを作成することもできます。</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="pick_a_server">XMPP プロバイダーを選択してください</string>
+ <string name="use_conversations.im">conversations.im を利用する</string>
+ <string name="create_new_account">新規アカウントを作成</string>
+ <string name="do_you_have_an_account">XMPP アカウントをお持ちですか?既にほかの XMPP クライアントを利用しているか、 Conversations を利用したことがある場合はこちら。初めての方は、今すぐ新規 XMPP アカウントを作成できます。\nヒント: e メールのプロバイダーが XMPP アカウントも提供している場合があります。</string>
+ <string name="server_select_text">XMPP は、プロバイダーに依存しないインスタントメッセージのプロトコルです。 XMPP サーバーならどこでも、このクライアントを使用することができます。\nよろしければ、 Conversations に最適化されたプロバイダー conversations.im¹ で簡単にアカウントを作成することもできます。</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="your_server_invitation">サーバーの招待</string>
<string name="improperly_formatted_provisioning">仮コードの書式が不正です</string>
<string name="tap_share_button_send_invite">共有ボタンを叩いて、連絡先の %1$s に招待を送信する。</string>
@@ -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>
@@ -1,5 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <string name="pick_a_server">Välj din XMPP-leverantör</string>
<string name="use_conversations.im">Använd conversations.im</string>
- <string name="create_new_account">Skapa nytt konto</string>
- </resources>
+ <string name="create_new_account">Skapa ett nytt konto</string>
+ <string name="do_you_have_an_account">Har du redan ett XMPP-konto? Detta kan vara fallet om du redan använder en annan XMPP-klient eller om du har använt Conversations tidigare. Om inte, kan du skapa ett nytt XMPP-konto på en gång.\nTips: Vissa e-postleverantörer tillhandahåller även XMPP-konton.</string>
+ <string name="your_server_invitation">Din serverinbjudan</string>
+ <string name="improperly_formatted_provisioning">Felaktigt formaterad provisioneringskod</string>
+ <string name="tap_share_button_send_invite">Tryck på dela-knappen för att skicka en inbjudan till din kontakt till %1$s.</string>
+ <string name="if_contact_is_nearby_use_qr">Om din kontakt är i närheten, kan de också skanna koden nedan för att acceptera din inbjudan.</string>
+ <string name="easy_invite_share_text">Gå med %1$s och chatta med mig: %2$s</string>
+ <string name="share_invite_with">Dela inbjudan med...</string>
+</resources>
@@ -39,8 +39,6 @@
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
- <uses-sdk tools:overrideLibrary="net.ypresto.androidtranscoder" />
-
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
@@ -54,7 +52,8 @@
<application
- android:allowBackup="false"
+ android:allowBackup="true"
+ android:fullBackupContent="@xml/backup_content"
android:appCategory="social"
android:hardwareAccelerated="true"
android:icon="@mipmap/new_launcher"
@@ -152,6 +151,14 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tel" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.SENDTO" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+
+ <data android:scheme="imto" />
+ <data android:host="xmpp" />
+ </intent-filter>
</activity>
<activity
android:name=".ui.StartConversationActivity"
@@ -195,6 +202,7 @@
android:launchMode="singleTop" />
<activity
android:name=".ui.EditAccountActivity"
+ android:exported="false"
android:launchMode="singleTop"
android:windowSoftInputMode="stateHidden|adjustResize" />
<activity
@@ -204,4 +204,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;
}
@@ -17,6 +17,21 @@ import eu.siacs.conversations.xmpp.Jid;
public class JabberIdContact extends AbstractPhoneContact {
+ private static final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
+ ContactsContract.Data.DISPLAY_NAME,
+ ContactsContract.Data.PHOTO_URI,
+ ContactsContract.Data.LOOKUP_KEY,
+ ContactsContract.CommonDataKinds.Im.DATA
+ };
+ private static final String SELECTION = ContactsContract.Data.MIMETYPE + "=? AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL + "=? or (" + ContactsContract.CommonDataKinds.Im.PROTOCOL + "=? and lower(" + ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL + ")=?))";
+
+ private static final String[] SELECTION_ARGS = {
+ ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE,
+ String.valueOf(ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER),
+ String.valueOf(ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM),
+ "xmpp"
+ };
+
private final Jid jid;
private JabberIdContact(Cursor cursor) throws IllegalArgumentException {
@@ -36,38 +51,26 @@ public class JabberIdContact extends AbstractPhoneContact {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
return Collections.emptyMap();
}
- final String[] PROJECTION = new String[]{ContactsContract.Data._ID,
- ContactsContract.Data.DISPLAY_NAME,
- ContactsContract.Data.PHOTO_URI,
- ContactsContract.Data.LOOKUP_KEY,
- ContactsContract.CommonDataKinds.Im.DATA};
-
- final String SELECTION = "(" + ContactsContract.Data.MIMETYPE + "=\""
- + ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
- + "\") AND (" + ContactsContract.CommonDataKinds.Im.PROTOCOL
- + "=\"" + ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER
- + "\")";
- final Cursor cursor;
- try {
- cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, null, null);
- } catch (Exception e) {
- return Collections.emptyMap();
- }
- final HashMap<Jid, JabberIdContact> contacts = new HashMap<>();
- while (cursor != null && cursor.moveToNext()) {
- try {
- final JabberIdContact contact = new JabberIdContact(cursor);
- final JabberIdContact preexisting = contacts.put(contact.getJid(), contact);
- if (preexisting == null || preexisting.rating() < contact.rating()) {
- contacts.put(contact.getJid(), contact);
+ try (final Cursor cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, SELECTION_ARGS, null)) {
+ if (cursor == null) {
+ return Collections.emptyMap();
+ }
+ final HashMap<Jid, JabberIdContact> contacts = new HashMap<>();
+ while (cursor.moveToNext()) {
+ try {
+ final JabberIdContact contact = new JabberIdContact(cursor);
+ final JabberIdContact preexisting = contacts.put(contact.getJid(), contact);
+ if (preexisting == null || preexisting.rating() < contact.rating()) {
+ contacts.put(contact.getJid(), contact);
+ }
+ } catch (final IllegalArgumentException e) {
+ Log.d(Config.LOGTAG, "unable to create jabber id contact");
}
- } catch (IllegalArgumentException e) {
- Log.d(Config.LOGTAG,"unable to create jabber id contact");
}
+ return contacts;
+ } catch (final Exception e) {
+ Log.d(Config.LOGTAG, "unable to query", e);
+ return Collections.emptyMap();
}
- if (cursor != null) {
- cursor.close();
- }
- return contacts;
}
}
@@ -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) {
@@ -28,6 +28,7 @@ import eu.siacs.conversations.persistance.DatabaseBackend;
import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.utils.JidHelper;
+import eu.siacs.conversations.utils.MessageUtils;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
@@ -258,9 +259,22 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public Message findMessageWithFileAndUuid(final String uuid) {
synchronized (this.messages) {
for (final Message message : this.messages) {
+ final Transferable transferable = message.getTransferable();
+ final boolean unInitiatedButKnownSize = MessageUtils.unInitiatedButKnownSize(message);
if (message.getUuid().equals(uuid)
&& message.getEncryption() != Message.ENCRYPTION_PGP
- && (message.isFileOrImage() || message.treatAsDownloadable())) {
+ && (message.isFileOrImage() || message.treatAsDownloadable() || unInitiatedButKnownSize || (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING))) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public Message findMessageWithUuid(final String uuid) {
+ synchronized (this.messages) {
+ for (final Message message : this.messages) {
+ if (message.getUuid().equals(uuid)) {
return message;
}
}
@@ -984,13 +984,28 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
}
if (conversation.getMode() == Conversation.MODE_MULTI) {
final Jid nextCounterpart = conversation.getNextCounterpart();
- if (nextCounterpart != null) {
- message.setCounterpart(nextCounterpart);
- message.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(nextCounterpart));
- message.setType(isFile ? Message.TYPE_PRIVATE_FILE : Message.TYPE_PRIVATE);
- return true;
- }
+ return configurePrivateMessage(conversation, message, nextCounterpart, isFile);
}
return false;
}
+
+ public static boolean configurePrivateMessage(final Message message, final Jid counterpart) {
+ final Conversation conversation;
+ if (message.conversation instanceof Conversation) {
+ conversation = (Conversation) message.conversation;
+ } else {
+ return false;
+ }
+ return configurePrivateMessage(conversation, message, counterpart, false);
+ }
+
+ private static boolean configurePrivateMessage(final Conversation conversation, final Message message, final Jid counterpart, final boolean isFile) {
+ if (counterpart == null) {
+ return false;
+ }
+ message.setCounterpart(counterpart);
+ message.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(counterpart));
+ message.setType(isFile ? Message.TYPE_PRIVATE_FILE : Message.TYPE_PRIVATE);
+ return true;
+ }
}
@@ -131,7 +131,6 @@ public class HttpDownloadConnection implements Transferable {
}
private void download(final boolean interactive) {
- Log.d(Config.LOGTAG,"download()",new Exception());
EXECUTOR.execute(new FileDownloader(interactive));
}
@@ -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 = 48;
+ private static final int DATABASE_VERSION = 49;
+
+ private static boolean requiresMessageIndexRebuild = false;
private static DatabaseBackend instance = null;
private static final String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -170,10 +174,11 @@ public class DatabaseBackend extends SQLiteOpenHelper {
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, 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 (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);
@@ -186,6 +191,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);
@@ -262,6 +278,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
@@ -518,16 +535,6 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL(CREATE_RESOLVER_RESULTS_TABLE);
}
- 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 < 42 && newVersion >= 42) {
- db.execSQL("DROP TRIGGER IF EXISTS after_message_delete");
- }
if (QuickConversationsService.isQuicksy() && oldVersion < 43 && newVersion >= 43) {
List<Account> accounts = getAccounts(db);
for (Account account : accounts) {
@@ -551,10 +558,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 46 && newVersion >= 46) {
final long start = SystemClock.elapsedRealtime();
db.rawQuery("PRAGMA secure_delete = FALSE", null).close();
- db.execSQL("update "+Message.TABLENAME+" set "+Message.EDITED+"=NULL");
+ db.execSQL("update " + Message.TABLENAME + " set " + Message.EDITED + "=NULL");
db.rawQuery("PRAGMA secure_delete=ON", null).close();
final long diff = SystemClock.elapsedRealtime() - start;
- Log.d(Config.LOGTAG,"deleted old edit information in "+diff+"ms");
+ Log.d(Config.LOGTAG, "deleted old edit information in " + diff + "ms");
}
if (oldVersion < 47 && newVersion >= 47) {
db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + Contact.PRESENCE_NAME + " TEXT");
@@ -562,6 +569,26 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 48 && newVersion >= 48) {
db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + Contact.RTP_CAPABILITY + " TEXT");
}
+ if (oldVersion < 49 && newVersion >= 49) {
+ db.beginTransaction();
+ db.execSQL("DROP TRIGGER IF EXISTS after_message_insert;");
+ db.execSQL("DROP TRIGGER IF EXISTS after_message_update;");
+ db.execSQL("DROP TRIGGER IF EXISTS after_message_delete;");
+ db.execSQL("DROP TABLE IF EXISTS messages_index;");
+ // a hack that should not be necessary, but
+ // there was at least one occurence when SQLite failed at this
+ db.execSQL("DROP TABLE IF EXISTS messages_index_docsize;");
+ db.execSQL("DROP TABLE IF EXISTS messages_index_segdir;");
+ db.execSQL("DROP TABLE IF EXISTS messages_index_segments;");
+ db.execSQL("DROP TABLE IF EXISTS messages_index_stat;");
+ db.execSQL(CREATE_MESSAGE_INDEX_TABLE);
+ db.execSQL(CREATE_MESSAGE_INSERT_TRIGGER);
+ db.execSQL(CREATE_MESSAGE_UPDATE_TRIGGER);
+ db.execSQL(CREATE_MESSAGE_DELETE_TRIGGER);
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ requiresMessageIndexRebuild = true;
+ }
}
private void canonicalizeJids(SQLiteDatabase db) {
@@ -776,7 +803,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
try {
list.add(0, Message.fromCursor(cursor, conversation));
} catch (Exception e) {
- Log.e(Config.LOGTAG,"unable to restore message");
+ Log.e(Config.LOGTAG, "unable to restore message");
}
}
cursor.close();
@@ -787,12 +814,12 @@ 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 {
selectionArgs = new String[]{FtsUtils.toMatchString(term), uuid};
- SQL.append(" AND "+Conversation.TABLENAME+'.'+Conversation.UUID+"=?");
+ SQL.append(" AND " + Conversation.TABLENAME + '.' + Conversation.UUID + "=?");
}
SQL.append(" ORDER BY " + Message.TIME_SENT + " DESC limit " + Config.MAX_SEARCH_RESULTS);
Log.d(Config.LOGTAG, "search term: " + FtsUtils.toMatchString(term));
@@ -856,7 +883,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
public List<FilePathInfo> getFilePathInfo() {
final SQLiteDatabase db = this.getReadableDatabase();
- final Cursor cursor = db.query(Message.TABLENAME, new String[]{Message.UUID, Message.RELATIVE_FILE_PATH, Message.DELETED}, "type in (1,2,5) and "+Message.RELATIVE_FILE_PATH+" is not null", null, null, null, null);
+ final Cursor cursor = db.query(Message.TABLENAME, new String[]{Message.UUID, Message.RELATIVE_FILE_PATH, Message.DELETED}, "type in (1,2,5) and " + Message.RELATIVE_FILE_PATH + " is not null", null, null, null, null);
final List<FilePathInfo> list = new ArrayList<>();
while (cursor != null && cursor.moveToNext()) {
list.add(new FilePathInfo(cursor.getString(0), cursor.getString(1), cursor.getInt(2) > 0));
@@ -869,7 +896,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
public List<FilePath> getRelativeFilePaths(String account, Jid jid, int limit) {
SQLiteDatabase db = this.getReadableDatabase();
- final String SQL = "select uuid,relativeFilePath from messages where type in (1,2,5) and deleted=0 and "+Message.RELATIVE_FILE_PATH+" is not null and conversationUuid=(select uuid from conversations where accountUuid=? and (contactJid=? or contactJid like ?)) order by timeSent desc";
+ final String SQL = "select uuid,relativeFilePath from messages where type in (1,2,5) and deleted=0 and " + Message.RELATIVE_FILE_PATH + " is not null and conversationUuid=(select uuid from conversations where accountUuid=? and (contactJid=? or contactJid like ?)) order by timeSent desc";
final String[] args = {account, jid.toString(), jid.toString() + "/%"};
Cursor cursor = db.rawQuery(SQL + (limit > 0 ? " limit " + limit : ""), args);
List<FilePath> filesPaths = new ArrayList<>();
@@ -894,7 +921,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
public boolean deleted;
private FilePathInfo(String uuid, String path, boolean deleted) {
- super(uuid,path);
+ super(uuid, path);
this.deleted = deleted;
}
@@ -1038,8 +1065,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();
@@ -1050,7 +1076,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();
@@ -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;
@@ -162,16 +164,16 @@ public class FileBackend {
return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + app + "/Backup/";
}
- private static Bitmap rotate(Bitmap bitmap, int degree) {
+ private static Bitmap rotate(final Bitmap bitmap, final int degree) {
if (degree == 0) {
return bitmap;
}
- int w = bitmap.getWidth();
- int h = bitmap.getHeight();
- Matrix mtx = new Matrix();
- mtx.postRotate(degree);
- Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
- if (bitmap != null && !bitmap.isRecycled()) {
+ final int w = bitmap.getWidth();
+ final int h = bitmap.getHeight();
+ final Matrix matrix = new Matrix();
+ matrix.postRotate(degree);
+ final Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
+ if (!bitmap.isRecycled()) {
bitmap.recycle();
}
return result;
@@ -627,20 +629,20 @@ 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) {
- try {
- os.write(buffer, 0, length);
- } catch (IOException e) {
- throw new FileWriterException();
- }
+ } 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 {
+ ByteStreams.copy(is, os);
+ } catch (IOException e) {
+ throw new FileWriterException();
}
try {
os.flush();
@@ -648,16 +650,17 @@ public class FileBackend {
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;
@@ -743,23 +746,28 @@ public class FileBackend {
final int imageMaxSize = mXmppConnectionService.getResources().getInteger(R.integer.auto_accept_filesize);
while (!targetSizeReached) {
os = new FileOutputStream(file);
+ Log.d(Config.LOGTAG, "compressing image with quality " + quality);
boolean success = scaledBitmap.compress(Config.IMAGE_FORMAT, quality, os);
if (!success) {
throw new FileCopyException(R.string.error_compressing_image);
}
os.flush();
- targetSizeReached = file.length() <= imageMaxSize || quality <= 50;
+ final long fileSize = file.length();
+ Log.d(Config.LOGTAG, "achieved file size of " + fileSize);
+ targetSizeReached = fileSize <= imageMaxSize || quality <= 50;
quality -= 5;
}
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 +780,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 +824,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;
- } finally {
- close(is);
+ }
+ }
+
+ 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;
}
}
@@ -1468,7 +1499,8 @@ public class FileBackend {
this.resId = resId;
}
- public @StringRes int getResId() {
+ public @StringRes
+ int getResId() {
return resId;
}
}
@@ -3,16 +3,19 @@ package eu.siacs.conversations.services;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
-import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
import android.util.Log;
-import net.ypresto.androidtranscoder.MediaTranscoder;
-import net.ypresto.androidtranscoder.format.MediaFormatStrategy;
+import androidx.annotation.NonNull;
+
+import com.otaliastudios.transcoder.Transcoder;
+import com.otaliastudios.transcoder.TranscoderListener;
+
+import org.jetbrains.annotations.NotNull;
import java.io.File;
-import java.io.FileDescriptor;
import java.io.FileNotFoundException;
+import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -23,161 +26,164 @@ import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.UiCallback;
-import eu.siacs.conversations.utils.Android360pFormatStrategy;
-import eu.siacs.conversations.utils.Android720pFormatStrategy;
import eu.siacs.conversations.utils.MimeUtils;
-
-public class AttachFileToConversationRunnable implements Runnable, MediaTranscoder.Listener {
-
- private final XmppConnectionService mXmppConnectionService;
- private final Message message;
- private final Uri uri;
- private final String type;
- private final UiCallback<Message> callback;
- private final boolean isVideoMessage;
- private final long originalFileSize;
- private int currentProgress = -1;
-
- AttachFileToConversationRunnable(XmppConnectionService xmppConnectionService, Uri uri, String type, Message message, UiCallback<Message> callback) {
- this.uri = uri;
- this.type = type;
- this.mXmppConnectionService = xmppConnectionService;
- this.message = message;
- this.callback = callback;
- final String mimeType = MimeUtils.guessMimeTypeFromUriAndMime(mXmppConnectionService, uri, type);
- final int autoAcceptFileSize = mXmppConnectionService.getResources().getInteger(R.integer.auto_accept_filesize);
- this.originalFileSize = FileBackend.getFileSize(mXmppConnectionService,uri);
- this.isVideoMessage = (mimeType != null && mimeType.startsWith("video/")) && originalFileSize > autoAcceptFileSize && !"uncompressed".equals(getVideoCompression());
- }
-
- boolean isVideoMessage() {
- return this.isVideoMessage;
- }
-
- private void processAsFile() {
- final String path = mXmppConnectionService.getFileBackend().getOriginalPath(uri);
- if (path != null && !FileBackend.isPathBlacklisted(path)) {
- message.setRelativeFilePath(path);
- mXmppConnectionService.getFileBackend().updateFileParams(message);
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- mXmppConnectionService.getPgpEngine().encrypt(message, callback);
- } else {
- mXmppConnectionService.sendMessage(message);
- callback.success(message);
- }
- } else {
- try {
- mXmppConnectionService.getFileBackend().copyFileToPrivateStorage(message, uri, type);
- mXmppConnectionService.getFileBackend().updateFileParams(message);
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- final PgpEngine pgpEngine = mXmppConnectionService.getPgpEngine();
- if (pgpEngine != null) {
- pgpEngine.encrypt(message, callback);
- } else if (callback != null) {
- callback.error(R.string.unable_to_connect_to_keychain, null);
- }
- } else {
- mXmppConnectionService.sendMessage(message);
- callback.success(message);
- }
- } catch (FileBackend.FileCopyException e) {
- callback.error(e.getResId(), message);
- }
- }
- }
-
- private void processAsVideo() throws FileNotFoundException {
- Log.d(Config.LOGTAG,"processing file as video");
- mXmppConnectionService.startForcingForegroundNotification();
- message.setRelativeFilePath(message.getUuid() + ".mp4");
- final DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message);
- final MediaFormatStrategy formatStrategy = "720".equals(getVideoCompression()) ? new Android720pFormatStrategy() : new Android360pFormatStrategy();
- file.getParentFile().mkdirs();
- final ParcelFileDescriptor parcelFileDescriptor = mXmppConnectionService.getContentResolver().openFileDescriptor(uri, "r");
- if (parcelFileDescriptor == null) {
- throw new FileNotFoundException("Parcel File Descriptor was null");
- }
- FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
- Future<Void> future = MediaTranscoder.getInstance().transcodeVideo(fileDescriptor, file.getAbsolutePath(), formatStrategy, this);
- try {
- future.get();
- } catch (InterruptedException e) {
- throw new AssertionError(e);
- } catch (ExecutionException e) {
- if (e.getCause() instanceof Error) {
- mXmppConnectionService.stopForcingForegroundNotification();
- processAsFile();
- } else {
- Log.d(Config.LOGTAG, "ignoring execution exception. Should get handled by onTranscodeFiled() instead", e);
- }
- }
- }
-
- @Override
- public void onTranscodeProgress(double progress) {
- final int p = (int) Math.round(progress * 100);
- if (p > currentProgress) {
- currentProgress = p;
- mXmppConnectionService.getNotificationService().updateFileAddingNotification(p,message);
- }
- }
-
- @Override
- public void onTranscodeCompleted() {
- mXmppConnectionService.stopForcingForegroundNotification();
- final File file = mXmppConnectionService.getFileBackend().getFile(message);
- long convertedFileSize = mXmppConnectionService.getFileBackend().getFile(message).getSize();
- Log.d(Config.LOGTAG,"originalFileSize="+originalFileSize+" convertedFileSize="+convertedFileSize);
- if (originalFileSize != 0 && convertedFileSize >= originalFileSize) {
- if (file.delete()) {
- Log.d(Config.LOGTAG,"original file size was smaller. deleting and processing as file");
- processAsFile();
- return;
- } else {
- Log.d(Config.LOGTAG,"unable to delete converted file");
- }
- }
- mXmppConnectionService.getFileBackend().updateFileParams(message);
- if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- mXmppConnectionService.getPgpEngine().encrypt(message, callback);
- } else {
- mXmppConnectionService.sendMessage(message);
- callback.success(message);
- }
- }
-
- @Override
- public void onTranscodeCanceled() {
- mXmppConnectionService.stopForcingForegroundNotification();
- processAsFile();
- }
-
- @Override
- public void onTranscodeFailed(Exception e) {
- mXmppConnectionService.stopForcingForegroundNotification();
- Log.d(Config.LOGTAG,"video transcoding failed",e);
- processAsFile();
- }
-
- @Override
- public void run() {
- if (this.isVideoMessage()) {
- try {
- processAsVideo();
- } catch (FileNotFoundException e) {
- processAsFile();
- }
- } else {
- processAsFile();
- }
- }
-
- private String getVideoCompression() {
- return getVideoCompression(mXmppConnectionService);
- }
-
- public static String getVideoCompression(final Context context) {
- final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
- return preferences.getString("video_compression", context.getResources().getString(R.string.video_compression));
- }
+import eu.siacs.conversations.utils.TranscoderStrategies;
+
+public class AttachFileToConversationRunnable implements Runnable, TranscoderListener {
+
+ private final XmppConnectionService mXmppConnectionService;
+ private final Message message;
+ private final Uri uri;
+ private final String type;
+ private final UiCallback<Message> callback;
+ private final boolean isVideoMessage;
+ private final long originalFileSize;
+ private int currentProgress = -1;
+
+ AttachFileToConversationRunnable(XmppConnectionService xmppConnectionService, Uri uri, String type, Message message, UiCallback<Message> callback) {
+ this.uri = uri;
+ this.type = type;
+ this.mXmppConnectionService = xmppConnectionService;
+ this.message = message;
+ this.callback = callback;
+ final String mimeType = MimeUtils.guessMimeTypeFromUriAndMime(mXmppConnectionService, uri, type);
+ final int autoAcceptFileSize = mXmppConnectionService.getResources().getInteger(R.integer.auto_accept_filesize);
+ this.originalFileSize = FileBackend.getFileSize(mXmppConnectionService, uri);
+ this.isVideoMessage = (mimeType != null && mimeType.startsWith("video/")) && originalFileSize > autoAcceptFileSize && !"uncompressed".equals(getVideoCompression());
+ }
+
+ boolean isVideoMessage() {
+ return this.isVideoMessage;
+ }
+
+ private void processAsFile() {
+ final String path = mXmppConnectionService.getFileBackend().getOriginalPath(uri);
+ if (path != null && !FileBackend.isPathBlacklisted(path)) {
+ message.setRelativeFilePath(path);
+ mXmppConnectionService.getFileBackend().updateFileParams(message);
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ mXmppConnectionService.getPgpEngine().encrypt(message, callback);
+ } else {
+ mXmppConnectionService.sendMessage(message);
+ callback.success(message);
+ }
+ } else {
+ try {
+ mXmppConnectionService.getFileBackend().copyFileToPrivateStorage(message, uri, type);
+ mXmppConnectionService.getFileBackend().updateFileParams(message);
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ final PgpEngine pgpEngine = mXmppConnectionService.getPgpEngine();
+ if (pgpEngine != null) {
+ pgpEngine.encrypt(message, callback);
+ } else if (callback != null) {
+ callback.error(R.string.unable_to_connect_to_keychain, null);
+ }
+ } else {
+ mXmppConnectionService.sendMessage(message);
+ callback.success(message);
+ }
+ } catch (FileBackend.FileCopyException e) {
+ callback.error(e.getResId(), message);
+ }
+ }
+ }
+
+ private void processAsVideo() throws FileNotFoundException {
+ Log.d(Config.LOGTAG, "processing file as video");
+ mXmppConnectionService.startForcingForegroundNotification();
+ message.setRelativeFilePath(message.getUuid() + ".mp4");
+ final DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message);
+ if (Objects.requireNonNull(file.getParentFile()).mkdirs()) {
+ Log.d(Config.LOGTAG, "created parent directory for video file");
+ }
+
+ final boolean highQuality = "720".equals(getVideoCompression());
+
+ final Future<Void> future = Transcoder.into(file.getAbsolutePath()).
+ addDataSource(mXmppConnectionService, uri)
+ .setVideoTrackStrategy(highQuality ? TranscoderStrategies.VIDEO_720P : TranscoderStrategies.VIDEO_360P)
+ .setAudioTrackStrategy(highQuality ? TranscoderStrategies.AUDIO_HQ : TranscoderStrategies.AUDIO_MQ)
+ .setListener(this)
+ .transcode();
+ try {
+ future.get();
+ } catch (InterruptedException e) {
+ throw new AssertionError(e);
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof Error) {
+ mXmppConnectionService.stopForcingForegroundNotification();
+ processAsFile();
+ } else {
+ Log.d(Config.LOGTAG, "ignoring execution exception. Should get handled by onTranscodeFiled() instead", e);
+ }
+ }
+ }
+
+ @Override
+ public void onTranscodeProgress(double progress) {
+ final int p = (int) Math.round(progress * 100);
+ if (p > currentProgress) {
+ currentProgress = p;
+ mXmppConnectionService.getNotificationService().updateFileAddingNotification(p, message);
+ }
+ }
+
+ @Override
+ public void onTranscodeCompleted(int successCode) {
+ mXmppConnectionService.stopForcingForegroundNotification();
+ final File file = mXmppConnectionService.getFileBackend().getFile(message);
+ long convertedFileSize = mXmppConnectionService.getFileBackend().getFile(message).getSize();
+ Log.d(Config.LOGTAG, "originalFileSize=" + originalFileSize + " convertedFileSize=" + convertedFileSize);
+ if (originalFileSize != 0 && convertedFileSize >= originalFileSize) {
+ if (file.delete()) {
+ Log.d(Config.LOGTAG, "original file size was smaller. deleting and processing as file");
+ processAsFile();
+ return;
+ } else {
+ Log.d(Config.LOGTAG, "unable to delete converted file");
+ }
+ }
+ mXmppConnectionService.getFileBackend().updateFileParams(message);
+ if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
+ mXmppConnectionService.getPgpEngine().encrypt(message, callback);
+ } else {
+ mXmppConnectionService.sendMessage(message);
+ callback.success(message);
+ }
+ }
+
+ @Override
+ public void onTranscodeCanceled() {
+ mXmppConnectionService.stopForcingForegroundNotification();
+ processAsFile();
+ }
+
+ @Override
+ public void onTranscodeFailed(@NonNull @NotNull Throwable exception) {
+ mXmppConnectionService.stopForcingForegroundNotification();
+ Log.d(Config.LOGTAG, "video transcoding failed", exception);
+ processAsFile();
+ }
+
+ @Override
+ public void run() {
+ if (this.isVideoMessage()) {
+ try {
+ processAsVideo();
+ } catch (FileNotFoundException e) {
+ processAsFile();
+ }
+ } else {
+ processAsFile();
+ }
+ }
+
+ private String getVideoCompression() {
+ return getVideoCompression(mXmppConnectionService);
+ }
+
+ public static String getVideoCompression(final Context context) {
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ return preferences.getString("video_compression", context.getResources().getString(R.string.video_compression));
+ }
}
@@ -35,6 +35,7 @@ import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.IconCompat;
import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
import java.io.File;
import java.io.IOException;
@@ -407,7 +408,7 @@ public class NotificationService {
currentInterruptionFilter = 1; //INTERRUPTION_FILTER_ALL
}
if (currentInterruptionFilter != 1) {
- Log.d(Config.LOGTAG,"do not ring or vibrate because interruption filter has been set to "+currentInterruptionFilter);
+ Log.d(Config.LOGTAG, "do not ring or vibrate because interruption filter has been set to " + currentInterruptionFilter);
return;
}
final ScheduledFuture<?> currentVibrationFuture = this.vibrationFuture;
@@ -424,13 +425,13 @@ public class NotificationService {
final Resources resources = mXmppConnectionService.getResources();
final String ringtonePreference = preferences.getString("call_ringtone", resources.getString(R.string.incoming_call_ringtone));
if (Strings.isNullOrEmpty(ringtonePreference)) {
- Log.d(Config.LOGTAG,"ringtone has been set to none");
+ Log.d(Config.LOGTAG, "ringtone has been set to none");
return;
}
final Uri uri = Uri.parse(ringtonePreference);
this.currentlyPlayingRingtone = RingtoneManager.getRingtone(mXmppConnectionService, uri);
if (this.currentlyPlayingRingtone == null) {
- Log.d(Config.LOGTAG,"unable to find ringtone for uri "+uri);
+ Log.d(Config.LOGTAG, "unable to find ringtone for uri " + uri);
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@@ -487,14 +488,23 @@ public class NotificationService {
notify(INCOMING_CALL_NOTIFICATION_ID, notification);
}
- public Notification getOngoingCallNotification(final AbstractJingleConnection.Id id, final Set<Media> media) {
+ public Notification getOngoingCallNotification(final XmppConnectionService.OngoingCall ongoingCall) {
+ final AbstractJingleConnection.Id id = ongoingCall.id;
final NotificationCompat.Builder builder = new NotificationCompat.Builder(mXmppConnectionService, "ongoing_calls");
- if (media.contains(Media.VIDEO)) {
+ if (ongoingCall.media.contains(Media.VIDEO)) {
builder.setSmallIcon(R.drawable.ic_videocam_white_24dp);
- builder.setContentTitle(mXmppConnectionService.getString(R.string.ongoing_video_call));
+ if (ongoingCall.reconnecting) {
+ builder.setContentTitle(mXmppConnectionService.getString(R.string.reconnecting_video_call));
+ } else {
+ builder.setContentTitle(mXmppConnectionService.getString(R.string.ongoing_video_call));
+ }
} else {
builder.setSmallIcon(R.drawable.ic_call_white_24dp);
- builder.setContentTitle(mXmppConnectionService.getString(R.string.ongoing_call));
+ if (ongoingCall.reconnecting) {
+ builder.setContentTitle(mXmppConnectionService.getString(R.string.reconnecting_call));
+ } else {
+ builder.setContentTitle(mXmppConnectionService.getString(R.string.ongoing_call));
+ }
}
builder.setContentText(id.account.getRoster().getContact(id.with).getDisplayName());
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
@@ -790,17 +800,18 @@ public class NotificationService {
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ)
.setShowsUserInterface(false)
.build();
- String replyLabel = mXmppConnectionService.getString(R.string.reply);
- NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
+ final String replyLabel = mXmppConnectionService.getString(R.string.reply);
+ final String lastMessageUuid = Iterables.getLast(messages).getUuid();
+ final NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
R.drawable.ic_send_text_offline,
replyLabel,
- createReplyIntent(conversation, false))
+ createReplyIntent(conversation, lastMessageUuid, false))
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
.setShowsUserInterface(false)
.addRemoteInput(remoteInput).build();
- NotificationCompat.Action wearReplyAction = new NotificationCompat.Action.Builder(R.drawable.ic_wear_reply,
+ final NotificationCompat.Action wearReplyAction = new NotificationCompat.Action.Builder(R.drawable.ic_wear_reply,
replyLabel,
- createReplyIntent(conversation, true)).addRemoteInput(remoteInput).build();
+ createReplyIntent(conversation, lastMessageUuid, true)).addRemoteInput(remoteInput).build();
mBuilder.extend(new NotificationCompat.WearableExtender().addAction(wearReplyAction));
int addedActionsCount = 1;
mBuilder.addAction(markReadAction);
@@ -1066,13 +1077,14 @@ public class NotificationService {
return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
}
- private PendingIntent createReplyIntent(Conversation conversation, boolean dismissAfterReply) {
+ private PendingIntent createReplyIntent(final Conversation conversation, final String lastMessageUuid, final boolean dismissAfterReply) {
final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
intent.setAction(XmppConnectionService.ACTION_REPLY_TO_CONVERSATION);
intent.putExtra("uuid", conversation.getUuid());
intent.putExtra("dismiss_notification", dismissAfterReply);
+ intent.putExtra("last_message_uuid", lastMessageUuid);
final int id = generateRequestCode(conversation, dismissAfterReply ? 12 : 14);
- return PendingIntent.getService(mXmppConnectionService, id, intent, 0);
+ return PendingIntent.getService(mXmppConnectionService, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
private PendingIntent createReadPendingIntent(Conversation conversation) {
@@ -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,14 +566,14 @@ 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);
}
}
- public void attachImageToConversation(final Conversation conversation, final Uri uri, final UiCallback<Message> callback) {
- final String mimeType = MimeUtils.guessMimeTypeFromUri(this, uri);
+ public void attachImageToConversation(final Conversation conversation, final Uri uri, final String type, final UiCallback<Message> callback) {
+ final String mimeType = MimeUtils.guessMimeTypeFromUriAndMime(this, uri, type);
final String compressPictures = getCompressPicturesPreference();
if ("never".equals(compressPictures)
@@ -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) {
@@ -724,6 +726,7 @@ public class XmppConnectionService extends Service {
}
final CharSequence body = remoteInput.getCharSequence("text_reply");
final boolean dismissNotification = intent.getBooleanExtra("dismiss_notification", false);
+ final String lastMessageUuid = intent.getStringExtra("last_message_uuid");
if (body == null || body.length() <= 0) {
break;
}
@@ -732,7 +735,7 @@ public class XmppConnectionService extends Service {
restoredFromDatabaseLatch.await();
final Conversation c = findConversationByUuid(uuid);
if (c != null) {
- directReply(c, body.toString(), dismissNotification);
+ directReply(c, body.toString(), lastMessageUuid, dismissNotification);
}
} catch (InterruptedException e) {
Log.d(Config.LOGTAG, "unable to process direct reply");
@@ -930,8 +933,12 @@ public class XmppConnectionService extends Service {
}
}
- private void directReply(Conversation conversation, String body, final boolean dismissAfterReply) {
- Message message = new Message(conversation, body, conversation.getNextEncryption());
+ private void directReply(final Conversation conversation, final String body, final String lastMessageUuid, final boolean dismissAfterReply) {
+ final Message inReplyTo = lastMessageUuid == null ? null : conversation.findMessageWithUuid(lastMessageUuid);
+ final Message message = new Message(conversation, body, conversation.getNextEncryption());
+ if (inReplyTo != null && inReplyTo.isPrivateMessage()) {
+ Message.configurePrivateMessage(message, inReplyTo.getCounterpart());
+ }
message.markUnread();
if (message.getEncryption() == Message.ENCRYPTION_PGP) {
getPgpEngine().encrypt(message, new UiCallback<Message>() {
@@ -1147,11 +1154,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 +1179,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 +1274,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() {
@@ -1291,8 +1298,8 @@ public class XmppConnectionService extends Service {
toggleForegroundService(false);
}
- public void setOngoingCall(AbstractJingleConnection.Id id, Set<Media> media) {
- ongoingCall.set(new OngoingCall(id, media));
+ public void setOngoingCall(AbstractJingleConnection.Id id, Set<Media> media, final boolean reconnecting) {
+ ongoingCall.set(new OngoingCall(id, media, reconnecting));
toggleForegroundService(false);
}
@@ -1308,7 +1315,7 @@ public class XmppConnectionService extends Service {
final Notification notification;
final int id;
if (ongoing != null) {
- notification = this.mNotificationService.getOngoingCallNotification(ongoing.id, ongoing.media);
+ notification = this.mNotificationService.getOngoingCallNotification(ongoing);
id = NotificationService.ONGOING_CALL_NOTIFICATION_ID;
startForeground(id, notification);
mNotificationService.cancel(NotificationService.FOREGROUND_NOTIFICATION_ID);
@@ -1885,7 +1892,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 +1935,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 +3347,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) {
- conference.getMucOptions().changeAffiliation(jid, affiliation);
- getAvatarService().clear(conference);
+ 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 +3929,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);
}
@@ -4864,12 +4869,14 @@ public class XmppConnectionService extends Service {
}
public static class OngoingCall {
- private final AbstractJingleConnection.Id id;
- private final Set<Media> media;
+ public final AbstractJingleConnection.Id id;
+ public final Set<Media> media;
+ public final boolean reconnecting;
- public OngoingCall(AbstractJingleConnection.Id id, Set<Media> media) {
+ public OngoingCall(AbstractJingleConnection.Id id, Set<Media> media, final boolean reconnecting) {
this.id = id;
this.media = media;
+ this.reconnecting = reconnecting;
}
@Override
@@ -4877,12 +4884,12 @@ public class XmppConnectionService extends Service {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
OngoingCall that = (OngoingCall) o;
- return Objects.equal(id, that.id);
+ return reconnecting == that.reconnecting && Objects.equal(id, that.id) && Objects.equal(media, that.media);
}
@Override
public int hashCode() {
- return Objects.hashCode(id);
+ return Objects.hashCode(id, media, reconnecting);
}
}
}
@@ -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);
@@ -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);
}
@@ -45,6 +45,7 @@ import eu.siacs.conversations.databinding.ActivityContactDetailsBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.services.AbstractQuickConversationsService;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
import eu.siacs.conversations.ui.adapter.MediaAdapter;
@@ -58,6 +59,7 @@ import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.Emoticons;
import eu.siacs.conversations.utils.IrregularUnicodeDetector;
+import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xml.Namespace;
@@ -131,15 +133,31 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
}
private void showAddToPhoneBookDialog() {
+ final Jid jid = contact.getJid();
+ final boolean quicksyContact = AbstractQuickConversationsService.isQuicksy()
+ && Config.QUICKSY_DOMAIN.equals(jid.getDomain())
+ && jid.getLocal() != null;
+ final String value;
+ if (quicksyContact) {
+ value = PhoneNumberUtilWrapper.toFormattedPhoneNumber(this, jid);
+ } else {
+ value = jid.toEscapedString();
+ }
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.action_add_phone_book));
- builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid().toEscapedString()));
+ builder.setMessage(getString(R.string.add_phone_book_text, value));
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setPositiveButton(getString(R.string.add), (dialog, which) -> {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
intent.setType(Contacts.CONTENT_ITEM_TYPE);
- intent.putExtra(Intents.Insert.IM_HANDLE, contact.getJid().toEscapedString());
- intent.putExtra(Intents.Insert.IM_PROTOCOL, CommonDataKinds.Im.PROTOCOL_JABBER);
+ if (quicksyContact) {
+ intent.putExtra(Intents.Insert.PHONE, value);
+ } else {
+ intent.putExtra(Intents.Insert.IM_HANDLE, value);
+ intent.putExtra(Intents.Insert.IM_PROTOCOL, CommonDataKinds.Im.PROTOCOL_JABBER);
+ //TODO for modern use we want PROTOCOL_CUSTOM and an extra field with a value of 'XMPP'
+ // however we don’t have such a field and thus have to use the legacy PROTOCOL_JABBER
+ }
intent.putExtra("finishActivityOnSaveCompleted", true);
try {
startActivityForResult(intent, 0);
@@ -233,6 +251,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (requestCode == REQUEST_SYNC_CONTACTS && xmppConnectionServiceBound) {
@@ -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);
+ }
}
@@ -135,6 +135,8 @@ import static eu.siacs.conversations.utils.PermissionUtils.allGranted;
import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
import static eu.siacs.conversations.utils.PermissionUtils.writeGranted;
+import org.jetbrains.annotations.NotNull;
+
public class ConversationFragment extends XmppFragment implements EditMessage.KeyboardListener, MessageAdapter.OnContactPictureLongClicked, MessageAdapter.OnContactPictureClicked {
@@ -186,10 +188,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() {
@@ -689,14 +688,14 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
toggleInputMethod();
}
- private void attachImageToConversation(Conversation conversation, Uri uri) {
+ private void attachImageToConversation(Conversation conversation, Uri uri, String type) {
if (conversation == null) {
return;
}
final Toast prepareFileToast = Toast.makeText(getActivity(), getText(R.string.preparing_image), Toast.LENGTH_LONG);
prepareFileToast.show();
activity.delegateUriPermissionsToService(uri);
- activity.xmppConnectionService.attachImageToConversation(conversation, uri,
+ activity.xmppConnectionService.attachImageToConversation(conversation, uri, type,
new UiCallback<Message>() {
@Override
@@ -857,9 +856,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
toggleInputMethod();
break;
case ATTACHMENT_CHOICE_LOCATION:
- double latitude = data.getDoubleExtra("latitude", 0);
- double longitude = data.getDoubleExtra("longitude", 0);
- Uri geo = Uri.parse("geo:" + latitude + "," + longitude);
+ final double latitude = data.getDoubleExtra("latitude", 0);
+ final double longitude = data.getDoubleExtra("longitude", 0);
+ final int accuracy = data.getIntExtra("accuracy", 0);
+ final Uri geo;
+ if (accuracy > 0) {
+ geo = Uri.parse(String.format("geo:%s,%s;u=%s", latitude, longitude, accuracy));
+ } else {
+ geo = Uri.parse(String.format("geo:%s,%s", latitude, longitude));
+ }
mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), geo, Attachment.Type.LOCATION));
toggleInputMethod();
break;
@@ -890,7 +895,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
attachLocationToConversation(conversation, attachment.getUri());
} else if (attachment.getType() == Attachment.Type.IMAGE) {
Log.d(Config.LOGTAG, "ConversationsActivity.commitAttachments() - attaching image to conversations. CHOOSE_IMAGE");
- attachImageToConversation(conversation, attachment.getUri());
+ attachImageToConversation(conversation, attachment.getUri(), attachment.getMime());
} else {
Log.d(Config.LOGTAG, "ConversationsActivity.commitAttachments() - attaching file to conversations. CHOOSE_FILE/RECORD_VOICE/RECORD_VIDEO");
attachFileToConversation(conversation, attachment.getUri(), attachment.getMime());
@@ -1272,10 +1277,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);
@@ -1586,6 +1588,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
if (writeGranted(grantResults, permissions)) {
if (activity != null && activity.xmppConnectionService != null) {
+ activity.xmppConnectionService.getBitmapCache().evictAll();
activity.xmppConnectionService.restartFileObserver();
}
refresh();
@@ -1624,9 +1627,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
@SuppressLint("InflateParams")
protected void clearHistoryDialog(final Conversation conversation) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ final AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
builder.setTitle(getString(R.string.clear_conversation_history));
- final View dialogView = getActivity().getLayoutInflater().inflate(R.layout.dialog_clear_history, null);
+ final View dialogView = requireActivity().getLayoutInflater().inflate(R.layout.dialog_clear_history, null);
final CheckBox endConversationCheckBox = dialogView.findViewById(R.id.end_conversation_checkbox);
builder.setView(dialogView);
builder.setNegativeButton(getString(R.string.cancel), null);
@@ -1644,7 +1647,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
protected void muteConversationDialog(final Conversation conversation) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ final AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
builder.setTitle(R.string.disable_notifications);
final int[] durations = getResources().getIntArray(R.array.mute_options_durations);
final CharSequence[] labels = new CharSequence[durations.length];
@@ -1660,13 +1663,13 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (durations[which] == -1) {
till = Long.MAX_VALUE;
} else {
- till = System.currentTimeMillis() + (durations[which] * 1000);
+ till = System.currentTimeMillis() + (durations[which] * 1000L);
}
conversation.setMutedTill(till);
activity.xmppConnectionService.updateConversation(conversation);
activity.onConversationsListItemUpdated();
refresh();
- getActivity().invalidateOptionsMenu();
+ requireActivity().invalidateOptionsMenu();
});
builder.create().show();
}
@@ -1698,7 +1701,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
this.activity.xmppConnectionService.updateConversation(conversation);
this.activity.onConversationsListItemUpdated();
refresh();
- getActivity().invalidateOptionsMenu();
+ requireActivity().invalidateOptionsMenu();
}
@@ -1708,9 +1711,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
switch (attachmentChoice) {
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
intent.setAction(Intent.ACTION_GET_CONTENT);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
- }
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setType("image/*");
chooser = true;
break;
@@ -1728,9 +1729,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
case ATTACHMENT_CHOICE_CHOOSE_FILE:
chooser = true;
intent.setType("*/*");
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
- }
+ intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setAction(Intent.ACTION_GET_CONTENT);
break;
@@ -1813,7 +1812,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
private void showErrorMessage(final Message message) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
builder.setTitle(R.string.error_message);
final String errorMessage = message.getErrorMessage();
final String[] errorMessageParts = errorMessage == null ? new String[0] : errorMessage.split("\\u001f");
@@ -1834,7 +1833,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
private void deleteFile(final Message message) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.delete_file_dialog);
builder.setMessage(R.string.delete_file_dialog_msg);
@@ -1968,7 +1967,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
@Override
- public void onSaveInstanceState(Bundle outState) {
+ public void onSaveInstanceState(@NotNull Bundle outState) {
super.onSaveInstanceState(outState);
if (conversation != null) {
outState.putString(STATE_CONVERSATION_UUID, conversation.getUuid());
@@ -2193,13 +2192,14 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
final boolean asQuote = extras.getBoolean(ConversationsActivity.EXTRA_AS_QUOTE);
final boolean pm = extras.getBoolean(ConversationsActivity.EXTRA_IS_PRIVATE_MESSAGE, false);
final boolean doNotAppend = extras.getBoolean(ConversationsActivity.EXTRA_DO_NOT_APPEND, false);
+ final String type = extras.getString(ConversationsActivity.EXTRA_TYPE);
final List<Uri> uris = extractUris(extras);
if (uris != null && uris.size() > 0) {
if (uris.size() == 1 && "geo".equals(uris.get(0).getScheme())) {
mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), uris.get(0), Attachment.Type.LOCATION));
} else {
final List<Uri> cleanedUris = cleanUris(new ArrayList<>(uris));
- mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), cleanedUris));
+ mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), cleanedUris, type));
}
toggleInputMethod();
return;
@@ -3058,4 +3058,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
activity.switchToAccount(message.getConversation().getAccount(), fingerprint);
}
+
+ private Activity requireActivity() {
+ final Activity activity = getActivity();
+ if (activity == null) {
+ throw new IllegalStateException("Activity not attached");
+ }
+ return activity;
+ }
}
@@ -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";
@@ -96,6 +99,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
public static final String EXTRA_DO_NOT_APPEND = "do_not_append";
public static final String EXTRA_POST_INIT_ACTION = "post_init_action";
public static final String POST_ACTION_RECORD_VOICE = "record_voice";
+ public static final String EXTRA_TYPE = "type";
private static final List<String> VIEW_AND_SHARE_ACTIONS = Arrays.asList(
ACTION_VIEW_CONVERSATION,
@@ -278,6 +282,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
UriHandlerActivity.onRequestPermissionResult(this, requestCode, grantResults);
if (grantResults.length > 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
@@ -425,16 +430,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();
+ executePendingTransactions(fragmentManager);
+ 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 {
@@ -456,6 +463,14 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
}
+ private static void executePendingTransactions(final FragmentManager fragmentManager) {
+ try {
+ fragmentManager.executePendingTransactions();
+ } catch (final Exception e) {
+ Log.e(Config.LOGTAG,"unable to execute pending fragment transactions");
+ }
+ }
+
public boolean onXmppUriClicked(Uri uri) {
XmppUri xmppUri = new XmppUri(uri);
if (xmppUri.isValidJid() && !xmppUri.hasFingerprints()) {
@@ -524,6 +539,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
@Override
protected void onStart() {
+ super.onStart();
final int theme = findTheme();
if (this.mTheme != theme) {
this.mSkipBackgroundBinding = true;
@@ -532,7 +548,6 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
this.mSkipBackgroundBinding = false;
}
mRedirectInProcess.set(false);
- super.onStart();
}
@Override
@@ -562,17 +577,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 +599,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 +617,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 (mainFragment instanceof ConversationFragment) {
- final Conversation conversation = ((ConversationFragment) mainFragment).getConversation();
- if (conversation != null) {
- actionBar.setTitle(EmojiWrapper.transform(conversation.getName()));
- actionBar.setDisplayHomeAsUpEnabled(true);
- return;
- }
+ 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);
}
- actionBar.setTitle(R.string.app_name);
- actionBar.setDisplayHomeAsUpEnabled(false);
}
}
@@ -621,17 +657,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);
@@ -23,11 +23,14 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
+import android.widget.CheckBox;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AlertDialog.Builder;
@@ -693,12 +696,18 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
} catch (final IllegalArgumentException | NullPointerException ignored) {
this.jidToEdit = null;
}
- if (jidToEdit != null && intent.getData() != null && intent.getBooleanExtra("scanned", false)) {
- final XmppUri uri = new XmppUri(intent.getData());
- if (xmppConnectionServiceBound) {
- processFingerprintVerification(uri, false);
+ final Uri data = intent.getData();
+ final XmppUri xmppUri = data == null ? null : new XmppUri(data);
+ final boolean scanned = intent.getBooleanExtra("scanned", false);
+ if (jidToEdit != null && xmppUri != null && xmppUri.hasFingerprints()) {
+ if (scanned) {
+ if (xmppConnectionServiceBound) {
+ processFingerprintVerification(xmppUri, false);
+ } else {
+ this.pendingUri = xmppUri;
+ }
} else {
- this.pendingUri = uri;
+ displayVerificationWarningDialog(xmppUri);
}
}
boolean init = intent.getBooleanExtra("init", false);
@@ -735,6 +744,28 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
}
}
+ private void displayVerificationWarningDialog(final XmppUri xmppUri) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.verify_omemo_keys);
+ View view = getLayoutInflater().inflate(R.layout.dialog_verify_fingerprints, null);
+ final CheckBox isTrustedSource = view.findViewById(R.id.trusted_source);
+ TextView warning = view.findViewById(R.id.warning);
+ warning.setText(R.string.verifying_omemo_keys_trusted_source_account);
+ builder.setView(view);
+ builder.setPositiveButton(R.string.continue_btn, (dialog, which) -> {
+ if (isTrustedSource.isChecked()) {
+ processFingerprintVerification(xmppUri, false);
+ } else {
+ finish();
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, (dialog, which) -> finish());
+ AlertDialog dialog = builder.create();
+ dialog.setCanceledOnTouchOutside(false);
+ dialog.setOnCancelListener(d -> finish());
+ dialog.show();
+ }
+
@Override
public void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
@@ -749,7 +780,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
}
@Override
- public void onSaveInstanceState(final Bundle savedInstanceState) {
+ public void onSaveInstanceState(@NonNull final Bundle savedInstanceState) {
if (mAccount != null) {
savedInstanceState.putString("account", mAccount.getJid().asBareJid().toEscapedString());
savedInstanceState.putBoolean("initMode", mInitMode);
@@ -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);
@@ -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);
@@ -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();
@@ -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;
@@ -57,6 +60,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.widget.DialpadView;
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;
@@ -67,10 +71,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";
@@ -97,7 +98,17 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
);
private static final List<RtpEndUserState> STATES_SHOWING_SWITCH_TO_CHAT = Arrays.asList(
RtpEndUserState.CONNECTING,
- RtpEndUserState.CONNECTED
+ RtpEndUserState.CONNECTED,
+ RtpEndUserState.RECONNECTING
+ );
+ private static final List<RtpEndUserState> STATES_CONSIDERED_CONNECTED = Arrays.asList(
+ RtpEndUserState.CONNECTED,
+ RtpEndUserState.RECONNECTING
+ );
+ private static final List<RtpEndUserState> STATES_SHOWING_PIP_PLACEHOLDER = Arrays.asList(
+ RtpEndUserState.ACCEPTING_CALL,
+ RtpEndUserState.CONNECTING,
+ RtpEndUserState.RECONNECTING
);
private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session";
private static final int REQUEST_ACCEPT_CALL = 0x1111;
@@ -141,13 +152,13 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session);
setSupportActionBar(binding.toolbar);
- ((DialpadView)findViewById(R.id.dialpad)).setClickConsumer(tag -> {
+ binding.dialpad.setClickConsumer(tag -> {
requireRtpConnection().applyDtmfTone(tag);
});
if (savedInstanceState != null) {
- int dialpad_visibility = savedInstanceState.getInt("dialpad_visibility");
- findViewById(R.id.dialpad).setVisibility(dialpad_visibility);
+ boolean dialpadVisible = savedInstanceState.getBoolean("dialpad_visible");
+ binding.dialpad.setVisibility(dialpadVisible ? View.VISIBLE : View.GONE);
}
}
@@ -197,9 +208,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private boolean isAudioOnlyConversation() {
final JingleRtpConnection connection =
this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
- return connection != null &&
- connection.getEndUserState() == RtpEndUserState.CONNECTED &&
- !connection.isVideoEnabled();
+
+ return connection != null && !connection.getMedia().contains(Media.VIDEO);
}
private void switchToConversation() {
@@ -482,12 +492,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();
@@ -535,7 +547,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private boolean isConnected() {
final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
- return connection != null && connection.getEndUserState() == RtpEndUserState.CONNECTED;
+ return connection != null && STATES_CONSIDERED_CONNECTED.contains(connection.getEndUserState());
}
private boolean switchToPictureInPicture() {
@@ -551,9 +563,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) {
@@ -562,6 +577,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);
@@ -656,8 +682,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
surfaceViewRenderer.setVisibility(View.VISIBLE);
try {
surfaceViewRenderer.init(requireRtpConnection().getEglBaseContext(), null);
- } catch (IllegalStateException e) {
- Log.d(Config.LOGTAG, "SurfaceViewRenderer was already initialized");
+ } catch (final IllegalStateException e) {
+ //Log.d(Config.LOGTAG, "SurfaceViewRenderer was already initialized");
}
surfaceViewRenderer.setEnableHardwareScaler(true);
}
@@ -682,6 +708,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
case CONNECTED:
setTitle(R.string.rtp_state_connected);
break;
+ case RECONNECTING:
+ setTitle(R.string.rtp_state_reconnecting);
+ break;
case ACCEPTING_CALL:
setTitle(R.string.rtp_state_accepting_call);
break;
@@ -824,7 +853,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@SuppressLint("RestrictedApi")
private void updateInCallButtonConfiguration(final RtpEndUserState state, final Set<Media> media) {
- if (state == RtpEndUserState.CONNECTED && !isPictureInPicture()) {
+ if (STATES_CONSIDERED_CONNECTED.contains(state) && !isPictureInPicture()) {
Preconditions.checkArgument(media.size() > 0, "Media must not be empty");
if (media.contains(Media.VIDEO)) {
final JingleRtpConnection rtpConnection = requireRtpConnection();
@@ -952,14 +981,11 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
this.binding.duration.setVisibility(View.GONE);
return;
}
- final long rtpConnectionStarted = connection.getRtpConnectionStarted();
- final long rtpConnectionEnded = connection.getRtpConnectionEnded();
- if (rtpConnectionStarted != 0) {
- final long ended = rtpConnectionEnded == 0 ? SystemClock.elapsedRealtime() : rtpConnectionEnded;
- this.binding.duration.setText(TimeFrameUtils.formatTimePassed(rtpConnectionStarted, ended, false));
- this.binding.duration.setVisibility(View.VISIBLE);
- } else {
+ if (connection.zeroDuration()) {
this.binding.duration.setVisibility(View.GONE);
+ } else {
+ this.binding.duration.setText(TimeFrameUtils.formatElapsedTime(connection.getCallDuration(), false));
+ this.binding.duration.setVisibility(View.VISIBLE);
}
}
@@ -991,7 +1017,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
return;
}
- if (isPictureInPicture() && (state == RtpEndUserState.CONNECTING || state == RtpEndUserState.ACCEPTING_CALL)) {
+ if (isPictureInPicture() && STATES_SHOWING_PIP_PLACEHOLDER.contains(state)) {
binding.localVideo.setVisibility(View.GONE);
binding.remoteVideoWrapper.setVisibility(View.GONE);
binding.appBarLayout.setVisibility(View.GONE);
@@ -1024,6 +1050,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
binding.remoteVideoWrapper.setVisibility(View.VISIBLE);
} else {
+ binding.appBarLayout.setVisibility(View.VISIBLE);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
binding.remoteVideoWrapper.setVisibility(View.GONE);
}
@@ -1200,8 +1227,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@Override
protected void onSaveInstanceState(@NonNull @NotNull Bundle outState) {
super.onSaveInstanceState(outState);
- int visibility = findViewById(R.id.dialpad).getVisibility();
- outState.putInt("dialpad_visibility", visibility);
+ outState.putBoolean("dialpad_visible", binding.dialpad.getVisibility() == View.VISIBLE);
}
private void updateRtpSessionProposalState(final Account account, final Jid with, final RtpEndUserState state) {
@@ -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();
}
@@ -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
@@ -13,10 +13,13 @@ import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import com.google.android.material.snackbar.Snackbar;
+import com.google.common.math.DoubleMath;
import org.osmdroid.api.IGeoPoint;
import org.osmdroid.util.GeoPoint;
+import java.math.RoundingMode;
+
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityShareLocationBinding;
@@ -28,213 +31,213 @@ import eu.siacs.conversations.utils.ThemeHelper;
public class ShareLocationActivity extends LocationActivity implements LocationListener {
- private Snackbar snackBar;
- private ActivityShareLocationBinding binding;
- private boolean marker_fixed_to_loc = false;
- private static final String KEY_FIXED_TO_LOC = "fixed_to_loc";
- private Boolean noAskAgain = false;
-
- @Override
- protected void onSaveInstanceState(@NonNull final Bundle outState) {
- super.onSaveInstanceState(outState);
-
- outState.putBoolean(KEY_FIXED_TO_LOC, marker_fixed_to_loc);
- }
-
- @Override
- protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
-
- if (savedInstanceState.containsKey(KEY_FIXED_TO_LOC)) {
- this.marker_fixed_to_loc = savedInstanceState.getBoolean(KEY_FIXED_TO_LOC);
- }
- }
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- this.binding = DataBindingUtil.setContentView(this,R.layout.activity_share_location);
- setSupportActionBar(binding.toolbar);
- configureActionBar(getSupportActionBar());
- setupMapView(binding.map, LocationProvider.getGeoPoint(this));
-
- this.binding.cancelButton.setOnClickListener(view -> {
- setResult(RESULT_CANCELED);
- finish();
- });
-
- this.snackBar = Snackbar.make(this.binding.snackbarCoordinator, R.string.location_disabled, Snackbar.LENGTH_INDEFINITE);
- this.snackBar.setAction(R.string.enable, view -> {
- if (isLocationEnabledAndAllowed()) {
- updateUi();
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hasLocationPermissions()) {
- requestPermissions(REQUEST_CODE_SNACKBAR_PRESSED);
- } else if (!isLocationEnabled()) {
- startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
- }
- });
- ThemeHelper.fix(this.snackBar);
-
- this.binding.shareButton.setOnClickListener(view -> {
- final Intent result = new Intent();
-
- if (marker_fixed_to_loc && myLoc != null) {
- result.putExtra("latitude", myLoc.getLatitude());
- result.putExtra("longitude", myLoc.getLongitude());
- result.putExtra("altitude", myLoc.getAltitude());
- result.putExtra("accuracy", (int) myLoc.getAccuracy());
- } else {
- final IGeoPoint markerPoint = this.binding.map.getMapCenter();
- result.putExtra("latitude", markerPoint.getLatitude());
- result.putExtra("longitude", markerPoint.getLongitude());
- }
-
- setResult(RESULT_OK, result);
- finish();
- });
-
- this.marker_fixed_to_loc = isLocationEnabledAndAllowed();
-
- this.binding.fab.setOnClickListener(view -> {
- if (!marker_fixed_to_loc) {
- if (!isLocationEnabled()) {
- startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- requestPermissions(REQUEST_CODE_FAB_PRESSED);
- }
- }
- toggleFixedLocation();
- });
- }
-
- @Override
- public void onRequestPermissionsResult(final int requestCode,
- @NonNull final String[] permissions,
- @NonNull final int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
-
- if (grantResults.length > 0 &&
- grantResults[0] != PackageManager.PERMISSION_GRANTED &&
- Build.VERSION.SDK_INT >= 23 &&
- permissions.length > 0 &&
- (
- Manifest.permission.LOCATION_HARDWARE.equals(permissions[0]) ||
- Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[0]) ||
- Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[0])
- ) &&
- !shouldShowRequestPermissionRationale(permissions[0])) {
- noAskAgain = true;
- }
-
- if (!noAskAgain && requestCode == REQUEST_CODE_SNACKBAR_PRESSED && !isLocationEnabled() && hasLocationPermissions()) {
- startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
- }
- updateUi();
- }
-
- @Override
- protected void gotoLoc(final boolean setZoomLevel) {
- if (this.myLoc != null && mapController != null) {
- if (setZoomLevel) {
- mapController.setZoom(Config.Map.FINAL_ZOOM_LEVEL);
- }
- mapController.animateTo(new GeoPoint(this.myLoc));
- }
- }
-
- @Override
- protected void setMyLoc(final Location location) {
- this.myLoc = location;
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- @Override
- protected void updateLocationMarkers() {
- super.updateLocationMarkers();
- if (this.myLoc != null) {
- this.binding.map.getOverlays().add(new MyLocation(this, null, this.myLoc));
- if (this.marker_fixed_to_loc) {
- this.binding.map.getOverlays().add(new Marker(marker_icon, new GeoPoint(this.myLoc)));
- } else {
- this.binding.map.getOverlays().add(new Marker(marker_icon));
- }
- } else {
- this.binding.map.getOverlays().add(new Marker(marker_icon));
- }
- }
-
- @Override
- public void onLocationChanged(final Location location) {
- if (this.myLoc == null) {
- this.marker_fixed_to_loc = true;
- }
- updateUi();
- if (LocationHelper.isBetterLocation(location, this.myLoc)) {
- final Location oldLoc = this.myLoc;
- this.myLoc = location;
-
- // Don't jump back to the users location if they're not moving (more or less).
- if (oldLoc == null || (this.marker_fixed_to_loc && this.myLoc.distanceTo(oldLoc) > 1)) {
- gotoLoc();
- }
-
- updateLocationMarkers();
- }
- }
-
- @Override
- public void onStatusChanged(final String provider, final int status, final Bundle extras) {
-
- }
-
- @Override
- public void onProviderEnabled(final String provider) {
-
- }
-
- @Override
- public void onProviderDisabled(final String provider) {
-
- }
-
- private boolean isLocationEnabledAndAllowed() {
- return this.hasLocationFeature && (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || this.hasLocationPermissions()) && this.isLocationEnabled();
- }
-
- private void toggleFixedLocation() {
- this.marker_fixed_to_loc = isLocationEnabledAndAllowed() && !this.marker_fixed_to_loc;
- if (this.marker_fixed_to_loc) {
- gotoLoc(false);
- }
- updateLocationMarkers();
- updateUi();
- }
-
- @Override
- protected void updateUi() {
- if (!hasLocationFeature || noAskAgain || isLocationEnabledAndAllowed()) {
- this.snackBar.dismiss();
- } else {
- this.snackBar.show();
- }
-
- if (isLocationEnabledAndAllowed()) {
- this.binding.fab.setVisibility(View.VISIBLE);
- runOnUiThread(() -> {
- this.binding.fab.setImageResource(marker_fixed_to_loc ? R.drawable.ic_gps_fixed_white_24dp :
- R.drawable.ic_gps_not_fixed_white_24dp);
- this.binding.fab.setContentDescription(getResources().getString(
- marker_fixed_to_loc ? R.string.action_unfix_from_location : R.string.action_fix_to_location
- ));
- this.binding.fab.invalidate();
- });
- } else {
- this.binding.fab.setVisibility(View.GONE);
- }
- }
+ private Snackbar snackBar;
+ private ActivityShareLocationBinding binding;
+ private boolean marker_fixed_to_loc = false;
+ private static final String KEY_FIXED_TO_LOC = "fixed_to_loc";
+ private Boolean noAskAgain = false;
+
+ @Override
+ protected void onSaveInstanceState(@NonNull final Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putBoolean(KEY_FIXED_TO_LOC, marker_fixed_to_loc);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+
+ if (savedInstanceState.containsKey(KEY_FIXED_TO_LOC)) {
+ this.marker_fixed_to_loc = savedInstanceState.getBoolean(KEY_FIXED_TO_LOC);
+ }
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ this.binding = DataBindingUtil.setContentView(this, R.layout.activity_share_location);
+ setSupportActionBar(binding.toolbar);
+ configureActionBar(getSupportActionBar());
+ setupMapView(binding.map, LocationProvider.getGeoPoint(this));
+
+ this.binding.cancelButton.setOnClickListener(view -> {
+ setResult(RESULT_CANCELED);
+ finish();
+ });
+
+ this.snackBar = Snackbar.make(this.binding.snackbarCoordinator, R.string.location_disabled, Snackbar.LENGTH_INDEFINITE);
+ this.snackBar.setAction(R.string.enable, view -> {
+ if (isLocationEnabledAndAllowed()) {
+ updateUi();
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hasLocationPermissions()) {
+ requestPermissions(REQUEST_CODE_SNACKBAR_PRESSED);
+ } else if (!isLocationEnabled()) {
+ startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+ }
+ });
+ ThemeHelper.fix(this.snackBar);
+
+ this.binding.shareButton.setOnClickListener(this::shareLocation);
+
+ this.marker_fixed_to_loc = isLocationEnabledAndAllowed();
+
+ this.binding.fab.setOnClickListener(view -> {
+ if (!marker_fixed_to_loc) {
+ if (!isLocationEnabled()) {
+ startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ requestPermissions(REQUEST_CODE_FAB_PRESSED);
+ }
+ }
+ toggleFixedLocation();
+ });
+ }
+
+ private void shareLocation(final View view) {
+ final Intent result = new Intent();
+ if (marker_fixed_to_loc && myLoc != null) {
+ result.putExtra("latitude", myLoc.getLatitude());
+ result.putExtra("longitude", myLoc.getLongitude());
+ result.putExtra("altitude", myLoc.getAltitude());
+ result.putExtra("accuracy", DoubleMath.roundToInt(myLoc.getAccuracy(), RoundingMode.HALF_UP));
+ } else {
+ final IGeoPoint markerPoint = this.binding.map.getMapCenter();
+ result.putExtra("latitude", markerPoint.getLatitude());
+ result.putExtra("longitude", markerPoint.getLongitude());
+ }
+ setResult(RESULT_OK, result);
+ finish();
+ }
+
+ @Override
+ public void onRequestPermissionsResult(final int requestCode,
+ @NonNull final String[] permissions,
+ @NonNull final int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+
+ if (grantResults.length > 0 &&
+ grantResults[0] != PackageManager.PERMISSION_GRANTED &&
+ Build.VERSION.SDK_INT >= 23 &&
+ permissions.length > 0 &&
+ (
+ Manifest.permission.LOCATION_HARDWARE.equals(permissions[0]) ||
+ Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[0]) ||
+ Manifest.permission.ACCESS_COARSE_LOCATION.equals(permissions[0])
+ ) &&
+ !shouldShowRequestPermissionRationale(permissions[0])) {
+ noAskAgain = true;
+ }
+
+ if (!noAskAgain && requestCode == REQUEST_CODE_SNACKBAR_PRESSED && !isLocationEnabled() && hasLocationPermissions()) {
+ startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+ }
+ updateUi();
+ }
+
+ @Override
+ protected void gotoLoc(final boolean setZoomLevel) {
+ if (this.myLoc != null && mapController != null) {
+ if (setZoomLevel) {
+ mapController.setZoom(Config.Map.FINAL_ZOOM_LEVEL);
+ }
+ mapController.animateTo(new GeoPoint(this.myLoc));
+ }
+ }
+
+ @Override
+ protected void setMyLoc(final Location location) {
+ this.myLoc = location;
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ protected void updateLocationMarkers() {
+ super.updateLocationMarkers();
+ if (this.myLoc != null) {
+ this.binding.map.getOverlays().add(new MyLocation(this, null, this.myLoc));
+ if (this.marker_fixed_to_loc) {
+ this.binding.map.getOverlays().add(new Marker(marker_icon, new GeoPoint(this.myLoc)));
+ } else {
+ this.binding.map.getOverlays().add(new Marker(marker_icon));
+ }
+ } else {
+ this.binding.map.getOverlays().add(new Marker(marker_icon));
+ }
+ }
+
+ @Override
+ public void onLocationChanged(final Location location) {
+ if (this.myLoc == null) {
+ this.marker_fixed_to_loc = true;
+ }
+ updateUi();
+ if (LocationHelper.isBetterLocation(location, this.myLoc)) {
+ final Location oldLoc = this.myLoc;
+ this.myLoc = location;
+
+ // Don't jump back to the users location if they're not moving (more or less).
+ if (oldLoc == null || (this.marker_fixed_to_loc && this.myLoc.distanceTo(oldLoc) > 1)) {
+ gotoLoc();
+ }
+
+ updateLocationMarkers();
+ }
+ }
+
+ @Override
+ public void onStatusChanged(final String provider, final int status, final Bundle extras) {
+
+ }
+
+ @Override
+ public void onProviderEnabled(final String provider) {
+
+ }
+
+ @Override
+ public void onProviderDisabled(final String provider) {
+
+ }
+
+ private boolean isLocationEnabledAndAllowed() {
+ return this.hasLocationFeature && (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || this.hasLocationPermissions()) && this.isLocationEnabled();
+ }
+
+ private void toggleFixedLocation() {
+ this.marker_fixed_to_loc = isLocationEnabledAndAllowed() && !this.marker_fixed_to_loc;
+ if (this.marker_fixed_to_loc) {
+ gotoLoc(false);
+ }
+ updateLocationMarkers();
+ updateUi();
+ }
+
+ @Override
+ protected void updateUi() {
+ if (!hasLocationFeature || noAskAgain || isLocationEnabledAndAllowed()) {
+ this.snackBar.dismiss();
+ } else {
+ this.snackBar.show();
+ }
+
+ if (isLocationEnabledAndAllowed()) {
+ this.binding.fab.setVisibility(View.VISIBLE);
+ runOnUiThread(() -> {
+ this.binding.fab.setImageResource(marker_fixed_to_loc ? R.drawable.ic_gps_fixed_white_24dp :
+ R.drawable.ic_gps_not_fixed_white_24dp);
+ this.binding.fab.setContentDescription(getResources().getString(
+ marker_fixed_to_loc ? R.string.action_unfix_from_location : R.string.action_fix_to_location
+ ));
+ this.binding.fab.invalidate();
+ });
+ } else {
+ this.binding.fab.setVisibility(View.GONE);
+ }
+ }
}
@@ -33,7 +33,8 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
refreshUi();
}
- private class Share {
+ private static class Share {
+ public String type;
ArrayList<Uri> uris = new ArrayList<>();
public String account;
public String contact;
@@ -65,6 +66,7 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (requestCode == REQUEST_STORAGE_PERMISSION) {
@@ -139,6 +141,7 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
} else if (type != null && uri != null) {
this.share.uris.clear();
this.share.uris.add(uri);
+ this.share.type = type;
} else {
this.share.text = text;
this.share.asQuote = asQuote;
@@ -193,6 +196,9 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, share.uris);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (share.type != null) {
+ intent.putExtra(ConversationsActivity.EXTRA_TYPE, share.type);
+ }
} else if (share.text != null) {
intent.setAction(ConversationsActivity.ACTION_VIEW_CONVERSATION);
intent.putExtra(Intent.EXTRA_TEXT, share.text);
@@ -37,11 +37,14 @@ import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.MenuRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.PopupMenu;
+import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@@ -51,6 +54,8 @@ import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.textfield.TextInputLayout;
+import com.leinardi.android.speeddial.SpeedDialActionItem;
+import com.leinardi.android.speeddial.SpeedDialView;
import java.util.ArrayList;
import java.util.Collections;
@@ -266,8 +271,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
setSupportActionBar(binding.toolbar);
configureActionBar(getSupportActionBar());
- binding.speedDial.inflate(R.menu.start_conversation_fab_submenu);
-
+ inflateFab(binding.speedDial, R.menu.start_conversation_fab_submenu);
binding.tabLayout.setupWithViewPager(binding.startConversationViewPager);
binding.startConversationViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
@@ -338,6 +342,21 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
});
}
+ private void inflateFab(final SpeedDialView speedDialView, final @MenuRes int menuRes) {
+ speedDialView.clearActionItems();
+ final PopupMenu popupMenu = new PopupMenu(this, new View(this));
+ popupMenu.inflate(menuRes);
+ final Menu menu = popupMenu.getMenu();
+ for (int i = 0; i < menu.size(); i++) {
+ final MenuItem menuItem = menu.getItem(i);
+ final SpeedDialActionItem actionItem = new SpeedDialActionItem.Builder(menuItem.getItemId(), menuItem.getIcon())
+ .setLabel(menuItem.getTitle() != null ? menuItem.getTitle().toString() : null)
+ .setFabImageTintColor(ContextCompat.getColor(this, R.color.white))
+ .create();
+ speedDialView.addActionItem(actionItem);
+ }
+ }
+
public static boolean isValidJid(String input) {
try {
Jid jid = Jid.ofEscaped(input);
@@ -7,24 +7,39 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
import android.widget.Toast;
+import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
+import androidx.databinding.DataBindingUtil;
import com.google.common.base.Strings;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
+import eu.siacs.conversations.databinding.ActivityUriHandlerBinding;
+import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.persistance.DatabaseBackend;
import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.utils.ProvisioningUtils;
import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.Jid;
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.HttpUrl;
+import okhttp3.Request;
+import okhttp3.Response;
public class UriHandlerActivity extends AppCompatActivity {
@@ -34,7 +49,9 @@ public class UriHandlerActivity extends AppCompatActivity {
private static final int REQUEST_CAMERA_PERMISSIONS_TO_SCAN = 0x6789;
private static final int REQUEST_CAMERA_PERMISSIONS_TO_SCAN_AND_PROVISION = 0x6790;
private static final Pattern V_CARD_XMPP_PATTERN = Pattern.compile("\nIMPP([^:]*):(xmpp:.+)\n");
- private boolean handled = false;
+ private static final Pattern LINK_HEADER_PATTERN = Pattern.compile("<(.*?)>");
+ private ActivityUriHandlerBinding binding;
+ private Call call;
public static void scan(final Activity activity) {
scan(activity, false);
@@ -77,9 +94,7 @@ public class UriHandlerActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- this.handled = savedInstanceState != null && savedInstanceState.getBoolean("handled", false);
- getLayoutInflater().inflate(R.layout.toolbar, findViewById(android.R.id.content));
- setSupportActionBar(findViewById(R.id.toolbar));
+ this.binding = DataBindingUtil.setContentView(this, R.layout.activity_uri_handler);
}
@Override
@@ -88,23 +103,17 @@ public class UriHandlerActivity extends AppCompatActivity {
handleIntent(getIntent());
}
- @Override
- public void onSaveInstanceState(Bundle savedInstanceState) {
- savedInstanceState.putBoolean("handled", this.handled);
- super.onSaveInstanceState(savedInstanceState);
- }
-
@Override
public void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
- private void handleUri(Uri uri) {
- handleUri(uri, false);
+ private boolean handleUri(final Uri uri) {
+ return handleUri(uri, false);
}
- private void handleUri(Uri uri, final boolean scanned) {
+ private boolean handleUri(final Uri uri, final boolean scanned) {
final Intent intent;
final XmppUri xmppUri = new XmppUri(uri);
final List<Jid> accounts = DatabaseBackend.getInstance(this).getAccountJids(true);
@@ -114,19 +123,22 @@ public class UriHandlerActivity extends AppCompatActivity {
final Jid jid = xmppUri.getJid();
if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
if (jid.getEscapedLocal() != null && accounts.contains(jid.asBareJid())) {
- Toast.makeText(this, R.string.account_already_exists, Toast.LENGTH_LONG).show();
- return;
+ showError(R.string.account_already_exists);
+ return false;
}
intent = SignupUtils.getTokenRegistrationIntent(this, jid, preAuth);
startActivity(intent);
- return;
+ return true;
}
if (accounts.size() == 0 && xmppUri.isAction(XmppUri.ACTION_ROSTER) && "y".equals(xmppUri.getParameter(XmppUri.PARAMETER_IBR))) {
intent = SignupUtils.getTokenRegistrationIntent(this, jid.getDomain(), preAuth);
intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
startActivity(intent);
- return;
+ return true;
}
+ } else if (xmppUri.isAction(XmppUri.ACTION_REGISTER)) {
+ showError(R.string.account_registrations_are_not_supported);
+ return false;
}
if (accounts.size() == 0) {
@@ -134,26 +146,19 @@ public class UriHandlerActivity extends AppCompatActivity {
intent = SignupUtils.getSignUpIntent(this);
intent.putExtra(StartConversationActivity.EXTRA_INVITE_URI, xmppUri.toString());
startActivity(intent);
+ return true;
} else {
- Toast.makeText(this, R.string.invalid_jid, Toast.LENGTH_SHORT).show();
+ showError(R.string.invalid_jid);
+ return false;
}
-
- return;
}
if (xmppUri.isAction(XmppUri.ACTION_MESSAGE)) {
-
final Jid jid = xmppUri.getJid();
final String body = xmppUri.getBody();
if (jid != null) {
- Class clazz;
- try {
- clazz = Class.forName("eu.siacs.conversations.ui.ShareViaAccountActivity");
- } catch (ClassNotFoundException e) {
- clazz = null;
-
- }
+ final Class<?> clazz = findShareViaAccountClass();
if (clazz != null) {
intent = new Intent(this, clazz);
intent.putExtra("contact", jid.toEscapedString());
@@ -164,7 +169,6 @@ public class UriHandlerActivity extends AppCompatActivity {
intent.setData(uri);
intent.putExtra("account", accounts.get(0).toEscapedString());
}
-
} else {
intent = new Intent(this, ShareWithActivity.class);
intent.setAction(Intent.ACTION_SEND);
@@ -184,38 +188,96 @@ public class UriHandlerActivity extends AppCompatActivity {
intent.putExtra("scanned", scanned);
intent.setData(uri);
} else {
- Toast.makeText(this, R.string.invalid_jid, Toast.LENGTH_SHORT).show();
- return;
+ showError(R.string.invalid_jid);
+ return false;
}
-
startActivity(intent);
+ return true;
}
- private void handleIntent(Intent data) {
- if (handled) {
- return;
- }
- if (data == null || data.getAction() == null) {
- finish();
- return;
+ private void checkForLinkHeader(final HttpUrl url) {
+ Log.d(Config.LOGTAG, "checking for link header on " + url);
+ this.call = HttpConnectionManager.OK_HTTP_CLIENT.newCall(new Request.Builder()
+ .url(url)
+ .head()
+ .build());
+ this.call.enqueue(new Callback() {
+ @Override
+ public void onFailure(@NotNull Call call, @NotNull IOException e) {
+ Log.d(Config.LOGTAG, "unable to check HTTP url", e);
+ showError(R.string.no_xmpp_adddress_found);
+ }
+
+ @Override
+ public void onResponse(@NotNull Call call, @NotNull Response response) {
+ if (response.isSuccessful()) {
+ final String linkHeader = response.header("Link");
+ if (linkHeader != null && processLinkHeader(linkHeader)) {
+ return;
+ }
+ }
+ showError(R.string.no_xmpp_adddress_found);
+ }
+ });
+
+ }
+
+ private boolean processLinkHeader(final String header) {
+ final Matcher matcher = LINK_HEADER_PATTERN.matcher(header);
+ if (matcher.find()) {
+ final String group = matcher.group();
+ final String link = group.substring(1, group.length() - 1);
+ if (handleUri(Uri.parse(link))) {
+ finish();
+ return true;
+ }
}
+ return false;
+ }
+
+ private void showError(@StringRes int error) {
+ this.binding.progress.setVisibility(View.INVISIBLE);
+ this.binding.error.setText(error);
+ this.binding.error.setVisibility(View.VISIBLE);
+ }
- handled = true;
+ private static Class<?> findShareViaAccountClass() {
+ try {
+ return Class.forName("eu.siacs.conversations.ui.ShareViaAccountActivity");
+ } catch (final ClassNotFoundException e) {
+ return null;
+ }
+ }
- switch (data.getAction()) {
+ private void handleIntent(final Intent data) {
+ final String action = data == null ? null : data.getAction();
+ if (action == null) {
+ return;
+ }
+ switch (action) {
+ case Intent.ACTION_MAIN:
+ binding.progress.setVisibility(call != null && !call.isCanceled() ? View.VISIBLE : View.INVISIBLE);
+ break;
case Intent.ACTION_VIEW:
case Intent.ACTION_SENDTO:
case Intent.ACTION_DIAL:
case Intent.ACTION_CALL:
- handleUri(data.getData());
+ if (handleUri(data.getData())) {
+ finish();
+ }
break;
case ACTION_SCAN_QR_CODE:
- Intent intent = new Intent(this, ScanActivity.class);
- startActivityForResult(intent, REQUEST_SCAN_QR_CODE);
- return;
+ Log.d(Config.LOGTAG, "scan. allow=" + allowProvisioning());
+ setIntent(createMainIntent());
+ startActivityForResult(new Intent(this, ScanActivity.class), REQUEST_SCAN_QR_CODE);
+ break;
}
+ }
- finish();
+ private Intent createMainIntent() {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.putExtra(EXTRA_ALLOW_PROVISIONING, allowProvisioning());
+ return intent;
}
private boolean allowProvisioning() {
@@ -227,6 +289,7 @@ public class UriHandlerActivity extends AppCompatActivity {
public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
super.onActivityResult(requestCode, requestCode, intent);
if (requestCode == REQUEST_SCAN_QR_CODE && resultCode == RESULT_OK) {
+ final boolean allowProvisioning = allowProvisioning();
final String result = intent.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
if (Strings.isNullOrEmpty(result)) {
finish();
@@ -235,22 +298,38 @@ public class UriHandlerActivity extends AppCompatActivity {
if (result.startsWith("BEGIN:VCARD\n")) {
final Matcher matcher = V_CARD_XMPP_PATTERN.matcher(result);
if (matcher.find()) {
- handleUri(Uri.parse(matcher.group(2)), true);
+ if (handleUri(Uri.parse(matcher.group(2)), true)) {
+ finish();
+ }
+ } else {
+ showError(R.string.no_xmpp_adddress_found);
}
- finish();
return;
- } else if (QuickConversationsService.isConversations() && looksLikeJsonObject(result) && allowProvisioning()) {
+ } else if (QuickConversationsService.isConversations() && looksLikeJsonObject(result) && allowProvisioning) {
ProvisioningUtils.provision(this, result);
finish();
return;
}
- handleUri(Uri.parse(result), true);
+ final Uri uri = Uri.parse(result.trim());
+ if (allowProvisioning && "https".equalsIgnoreCase(uri.getScheme()) && !XmppUri.INVITE_DOMAIN.equalsIgnoreCase(uri.getHost())) {
+ final HttpUrl httpUrl = HttpUrl.parse(uri.toString());
+ if (httpUrl != null) {
+ checkForLinkHeader(httpUrl);
+ } else {
+ finish();
+ }
+ } else if (handleUri(uri, true)) {
+ finish();
+ } else {
+ setIntent(new Intent(Intent.ACTION_VIEW, uri));
+ }
+ } else {
+ finish();
}
- finish();
}
private static boolean looksLikeJsonObject(final String input) {
- final String trimmed = Strings.emptyToNull(input).trim();
+ final String trimmed = Strings.nullToEmpty(input).trim();
return trimmed.charAt(0) == '{' && trimmed.charAt(trimmed.length() - 1) == '}';
}
-}
+}
@@ -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() {
@@ -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();
}
}
@@ -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;
@@ -359,48 +360,51 @@ public class MessageAdapter extends ArrayAdapter<Message> {
*/
private boolean handleTextQuotes(SpannableStringBuilder body, boolean darkBackground) {
boolean startsWithQuote = false;
- char previous = '\n';
- int lineStart = -1;
- int lineTextStart = -1;
- int quoteStart = -1;
- for (int i = 0; i <= body.length(); i++) {
- 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)) {
- // Line start with quote
- lineStart = i;
- if (quoteStart == -1) quoteStart = i;
- if (i == 0) startsWithQuote = true;
- } else if (quoteStart >= 0) {
- // Line start without quote, apply spans there
- applyQuoteSpan(body, quoteStart, i - 1, darkBackground);
- quoteStart = -1;
+ int quoteDepth = 1;
+ while (QuoteHelper.bodyContainsQuoteStart(body) && quoteDepth <= Config.QUOTE_MAX_DEPTH) {
+ char previous = '\n';
+ int lineStart = -1;
+ int lineTextStart = -1;
+ int quoteStart = -1;
+ for (int i = 0; i <= body.length(); i++) {
+ char current = body.length() > i ? body.charAt(i) : '\n';
+ if (lineStart == -1) {
+ if (previous == '\n') {
+ if (i < body.length() && QuoteHelper.isPositionQuoteStart(body, i)) {
+ // Line start with quote
+ lineStart = i;
+ if (quoteStart == -1) quoteStart = i;
+ if (i == 0) startsWithQuote = true;
+ } else if (quoteStart >= 0) {
+ // Line start without quote, apply spans there
+ applyQuoteSpan(body, quoteStart, i - 1, darkBackground);
+ quoteStart = -1;
+ }
}
- }
- } else {
- // Remove extra spaces between > and first character in the line
- // > character will be removed too
- if (current != ' ' && lineTextStart == -1) {
- lineTextStart = i;
- }
- if (current == '\n') {
- body.delete(lineStart, lineTextStart);
- i -= lineTextStart - lineStart;
- if (i == lineStart) {
- // Avoid empty lines because span over empty line can be hidden
- body.insert(i++, " ");
+ } else {
+ // Remove extra spaces between > and first character in the line
+ // > character will be removed too
+ if (current != ' ' && lineTextStart == -1) {
+ lineTextStart = i;
+ }
+ if (current == '\n') {
+ body.delete(lineStart, lineTextStart);
+ i -= lineTextStart - lineStart;
+ if (i == lineStart) {
+ // Avoid empty lines because span over empty line can be hidden
+ body.insert(i++, " ");
+ }
+ lineStart = -1;
+ lineTextStart = -1;
}
- lineStart = -1;
- lineTextStart = -1;
}
+ previous = current;
}
- previous = current;
- }
- if (quoteStart >= 0) {
- // Apply spans to finishing open quote
- applyQuoteSpan(body, quoteStart, body.length(), darkBackground);
+ if (quoteStart >= 0) {
+ // Apply spans to finishing open quote
+ applyQuoteSpan(body, quoteStart, body.length(), darkBackground);
+ }
+ quoteDepth++;
}
return startsWithQuote;
}
@@ -800,12 +804,12 @@ public class MessageAdapter extends ArrayAdapter<Message> {
} else if (message.treatAsDownloadable()) {
try {
final URI uri = new URI(message.getBody());
- displayDownloadableMessage(viewHolder,
- message,
- activity.getString(R.string.check_x_filesize_on_host,
- UIHelper.getFileDescriptionString(activity, message),
- uri.getHost()),
- darkBackground);
+ displayDownloadableMessage(viewHolder,
+ message,
+ activity.getString(R.string.check_x_filesize_on_host,
+ UIHelper.getFileDescriptionString(activity, message),
+ uri.getHost()),
+ darkBackground);
} catch (Exception e) {
displayDownloadableMessage(viewHolder,
message,
@@ -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;
+ }
+ }
+}
@@ -136,10 +136,10 @@ public class Attachment implements Parcelable {
return Collections.singletonList(new Attachment(uri, type, mime));
}
- public static List<Attachment> of(final Context context, List<Uri> uris) {
- List<Attachment> attachments = new ArrayList<>();
- for (Uri uri : uris) {
- final String mime = MimeUtils.guessMimeTypeFromUri(context, uri);
+ public static List<Attachment> of(final Context context, List<Uri> uris, final String type) {
+ final List<Attachment> attachments = new ArrayList<>();
+ for (final Uri uri : uris) {
+ final String mime = MimeUtils.guessMimeTypeFromUriAndMime(context, uri, type);
attachments.add(new Attachment(uri, mime != null && isImage(mime) ? Type.IMAGE : Type.FILE, mime));
}
return attachments;
@@ -0,0 +1,106 @@
+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)
+ && isPositionPrecededByPreQuote(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;
+ }
+}
@@ -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;
+ }
+
+}
@@ -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);
+ }
+ }
+}
@@ -19,15 +19,17 @@ package eu.siacs.conversations.ui.widget;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.databinding.DataBindingUtil;
+import eu.siacs.conversations.databinding.DialpadBinding;
import eu.siacs.conversations.R;
-import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
public class DialpadView extends ConstraintLayout implements View.OnClickListener {
- protected java.util.function.Consumer<String> clickConsumer = null;
+ protected Consumer<String> clickConsumer = null;
public DialpadView(Context context) {
super(context);
@@ -44,28 +46,18 @@ public class DialpadView extends ConstraintLayout implements View.OnClickListene
init();
}
- public void setClickConsumer(java.util.function.Consumer<String> clickConsumer) {
+ public void setClickConsumer(Consumer<String> clickConsumer) {
this.clickConsumer = clickConsumer;
}
private void init() {
- inflate(getContext(), R.layout.dialpad, this);
- initViews();
- }
-
- private void initViews() {
- findViewById(R.id.dialpad_1_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_2_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_3_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_4_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_5_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_6_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_7_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_8_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_9_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_0_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_asterisk_holder).setOnClickListener(this);
- findViewById(R.id.dialpad_pound_holder).setOnClickListener(this);
+ DialpadBinding binding = DataBindingUtil.inflate(
+ LayoutInflater.from(getContext()),
+ R.layout.dialpad,
+ this,
+ true
+ );
+ binding.setDialpadView(this);
}
@Override
@@ -73,4 +65,8 @@ public class DialpadView extends ConstraintLayout implements View.OnClickListene
clickConsumer.accept(v.getTag().toString());
}
+ // Based on java.util.function.Consumer to avoid Android 24 dependency
+ public interface Consumer<T> {
+ void accept(T t);
+ }
}
@@ -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();
@@ -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);
+ }
+}
@@ -1,76 +0,0 @@
-package eu.siacs.conversations.utils;
-
-import android.media.MediaCodecInfo;
-import android.media.MediaFormat;
-import android.os.Build;
-import android.util.Log;
-
-import androidx.annotation.RequiresApi;
-
-import net.ypresto.androidtranscoder.format.MediaFormatExtraConstants;
-import net.ypresto.androidtranscoder.format.MediaFormatStrategy;
-import net.ypresto.androidtranscoder.format.OutputFormatUnavailableException;
-
-import eu.siacs.conversations.Config;
-
-public class Android360pFormatStrategy implements MediaFormatStrategy {
-
- private static final int LONGER_LENGTH = 640;
- private static final int SHORTER_LENGTH = 360;
- private static final int DEFAULT_VIDEO_BITRATE = 1000 * 1000;
- private static final int DEFAULT_AUDIO_BITRATE = 128 * 1000;
- private final int mVideoBitrate;
- private final int mAudioBitrate;
- private final int mAudioChannels;
-
- public Android360pFormatStrategy() {
- mVideoBitrate = DEFAULT_VIDEO_BITRATE;
- mAudioBitrate = DEFAULT_AUDIO_BITRATE;
- mAudioChannels = 2;
- }
-
- @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
- @Override
- public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
- int width = inputFormat.getInteger(MediaFormat.KEY_WIDTH);
- int height = inputFormat.getInteger(MediaFormat.KEY_HEIGHT);
- int longer, shorter, outWidth, outHeight;
- if (width >= height) {
- longer = width;
- shorter = height;
- outWidth = LONGER_LENGTH;
- outHeight = SHORTER_LENGTH;
- } else {
- shorter = width;
- longer = height;
- outWidth = SHORTER_LENGTH;
- outHeight = LONGER_LENGTH;
- }
- if (longer * 9 != shorter * 16) {
- throw new OutputFormatUnavailableException("This video is not 16:9, and is not able to transcode. (" + width + "x" + height + ")");
- }
- if (shorter <= SHORTER_LENGTH) {
- Log.d(Config.LOGTAG, "This video is less or equal to 360p, pass-through. (" + width + "x" + height + ")");
- return null;
- }
- MediaFormat format = MediaFormat.createVideoFormat("video/avc", outWidth, outHeight);
- format.setInteger(MediaFormat.KEY_BIT_RATE, mVideoBitrate);
- format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
- format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 3);
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- format.setInteger(MediaFormat.KEY_PROFILE ,MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline);
- format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel13);
- }
- return format;
- }
-
- @Override
- public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
- final MediaFormat format = MediaFormat.createAudioFormat(MediaFormatExtraConstants.MIMETYPE_AUDIO_AAC, inputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), mAudioChannels);
- format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
- format.setInteger(MediaFormat.KEY_BIT_RATE, mAudioBitrate);
- return format;
- }
-
-}
@@ -1,76 +0,0 @@
-package eu.siacs.conversations.utils;
-
-import android.media.MediaCodecInfo;
-import android.media.MediaFormat;
-import android.os.Build;
-import android.util.Log;
-
-import androidx.annotation.RequiresApi;
-
-import net.ypresto.androidtranscoder.format.MediaFormatExtraConstants;
-import net.ypresto.androidtranscoder.format.MediaFormatStrategy;
-import net.ypresto.androidtranscoder.format.OutputFormatUnavailableException;
-
-import eu.siacs.conversations.Config;
-
-public class Android720pFormatStrategy implements MediaFormatStrategy {
-
- private static final int LONGER_LENGTH = 1280;
- private static final int SHORTER_LENGTH = 720;
- private static final int DEFAULT_VIDEO_BITRATE = 2000 * 1000;
- private static final int DEFAULT_AUDIO_BITRATE = 192 * 1000;
- private final int mVideoBitrate;
- private final int mAudioBitrate;
- private final int mAudioChannels;
-
- public Android720pFormatStrategy() {
- mVideoBitrate = DEFAULT_VIDEO_BITRATE;
- mAudioBitrate = DEFAULT_AUDIO_BITRATE;
- mAudioChannels = 2;
- }
-
- @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
- @Override
- public MediaFormat createVideoOutputFormat(MediaFormat inputFormat) {
- int width = inputFormat.getInteger(MediaFormat.KEY_WIDTH);
- int height = inputFormat.getInteger(MediaFormat.KEY_HEIGHT);
- int longer, shorter, outWidth, outHeight;
- if (width >= height) {
- longer = width;
- shorter = height;
- outWidth = LONGER_LENGTH;
- outHeight = SHORTER_LENGTH;
- } else {
- shorter = width;
- longer = height;
- outWidth = SHORTER_LENGTH;
- outHeight = LONGER_LENGTH;
- }
- if (longer * 9 != shorter * 16) {
- throw new OutputFormatUnavailableException("This video is not 16:9, and is not able to transcode. (" + width + "x" + height + ")");
- }
- if (shorter <= SHORTER_LENGTH) {
- Log.d(Config.LOGTAG, "This video is less or equal to 720p, pass-through. (" + width + "x" + height + ")");
- return null;
- }
- MediaFormat format = MediaFormat.createVideoFormat("video/avc", outWidth, outHeight);
- format.setInteger(MediaFormat.KEY_BIT_RATE, mVideoBitrate);
- format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
- format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 3);
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- format.setInteger(MediaFormat.KEY_PROFILE ,MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline);
- format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel13);
- }
- return format;
- }
-
- @Override
- public MediaFormat createAudioOutputFormat(MediaFormat inputFormat) {
- final MediaFormat format = MediaFormat.createAudioFormat(MediaFormatExtraConstants.MIMETYPE_AUDIO_AAC, inputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), mAudioChannels);
- format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
- format.setInteger(MediaFormat.KEY_BIT_RATE, mAudioBitrate);
- return format;
- }
-
-}
@@ -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;
- }
- }
-}
@@ -4,6 +4,8 @@ import android.content.Context;
import android.telephony.TelephonyManager;
import android.util.Log;
+import androidx.core.content.ContextCompat;
+
import org.osmdroid.util.GeoPoint;
import java.io.BufferedReader;
@@ -16,11 +18,14 @@ import eu.siacs.conversations.R;
public class LocationProvider {
- public static final GeoPoint FALLBACK = new GeoPoint(0.0,0.0);
+ public static final GeoPoint FALLBACK = new GeoPoint(0.0, 0.0);
- public static String getUserCountry(Context context) {
+ public static String getUserCountry(final Context context) {
try {
- final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ final TelephonyManager tm = ContextCompat.getSystemService(context, TelephonyManager.class);
+ if (tm == null) {
+ return getUserCountryFallback();
+ }
final String simCountry = tm.getSimCountryIso();
if (simCountry != null && simCountry.length() == 2) { // SIM country code is available
return simCountry.toUpperCase(Locale.US);
@@ -30,40 +35,41 @@ public class LocationProvider {
return networkCountry.toUpperCase(Locale.US);
}
}
- } catch (Exception e) {
- // fallthrough
+ return getUserCountryFallback();
+ } catch (final Exception e) {
+ return getUserCountryFallback();
}
- Locale locale = Locale.getDefault();
+ }
+
+ private static String getUserCountryFallback() {
+ final Locale locale = Locale.getDefault();
return locale.getCountry();
}
- public static GeoPoint getGeoPoint(Context context) {
+ public static GeoPoint getGeoPoint(final Context context) {
return getGeoPoint(context, getUserCountry(context));
}
- public static synchronized GeoPoint getGeoPoint(Context context, String country) {
- try {
- BufferedReader reader = new BufferedReader(new InputStreamReader(context.getResources().openRawResource(R.raw.countries)));
+ public static synchronized GeoPoint getGeoPoint(final Context context, final String country) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(context.getResources().openRawResource(R.raw.countries)))) {
String line;
- while((line = reader.readLine()) != null) {
- String[] parts = line.split("\\s+",4);
+ while ((line = reader.readLine()) != null) {
+ final String[] parts = line.split("\\s+", 4);
if (parts.length == 4) {
if (country.equalsIgnoreCase(parts[0])) {
try {
return new GeoPoint(Double.parseDouble(parts[1]), Double.parseDouble(parts[2]));
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
return FALLBACK;
}
}
- } else {
- Log.d(Config.LOGTAG,"unable to parse line="+line);
}
}
- } catch (IOException e) {
- Log.d(Config.LOGTAG,e.getMessage());
+ } catch (final IOException e) {
+ Log.d(Config.LOGTAG, "unable to parse country->geo map", e);
}
return FALLBACK;
}
-}
+}
@@ -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) {
@@ -21,6 +21,8 @@ import android.net.Uri;
import android.provider.OpenableColumns;
import android.util.Log;
+import com.google.common.base.Strings;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -247,6 +249,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");
@@ -273,6 +276,8 @@ public final class MimeUtils {
add("image/ico", "ico");
add("image/ief", "ief");
add("image/heic", "heic");
+ add("image/heif", "heif");
+ add("image/avif", "avif");
// add ".jpg" first so it will be the default for guessExtensionFromMimeType
add("image/jpeg", "jpg");
add("image/jpeg", "jpeg");
@@ -567,6 +572,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;
}
@@ -584,22 +591,33 @@ public final class MimeUtils {
}
public static String extractRelevantExtension(final String path, final boolean ignoreCryptoExtension) {
- if (path == null || path.isEmpty()) {
+ if (Strings.isNullOrEmpty(path)) {
return null;
}
- String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase();
- int dotPosition = filename.lastIndexOf(".");
+ final String filenameQueryAnchor = path.substring(path.lastIndexOf('/') + 1);
+ final String filenameQuery = cutBefore(filenameQueryAnchor, '#');
+ final String filename = cutBefore(filenameQuery, '?');
+ final int dotPosition = filename.lastIndexOf('.');
- if (dotPosition != -1) {
- String extension = filename.substring(dotPosition + 1);
- // we want the real file extension, not the crypto one
- if (ignoreCryptoExtension && Transferable.VALID_CRYPTO_EXTENSIONS.contains(extension)) {
- return extractRelevantExtension(filename.substring(0, dotPosition));
- } else {
- return extension;
- }
+ if (dotPosition == -1) {
+ return null;
+ }
+ final String extension = filename.substring(dotPosition + 1);
+ // we want the real file extension, not the crypto one
+ if (ignoreCryptoExtension && Transferable.VALID_CRYPTO_EXTENSIONS.contains(extension)) {
+ return extractRelevantExtension(filename.substring(0, dotPosition));
+ } else {
+ return extension;
+ }
+ }
+
+ private static String cutBefore(final String input, final char c) {
+ final int position = input.indexOf(c);
+ if (position > 0) {
+ return input.substring(0, position);
+ } else {
+ return input;
}
- return null;
}
}
@@ -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.
@@ -477,4 +510,4 @@ public class Patterns {
* Do not create this static utility class.
*/
private Patterns() {}
-}
+}
@@ -1,5 +1,7 @@
package eu.siacs.conversations.utils;
+import com.google.common.io.ByteStreams;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -12,76 +14,108 @@ import eu.siacs.conversations.Config;
public class SocksSocketFactory {
- private static final byte[] LOCALHOST = new byte[]{127,0,0,1};
+ private static final byte[] LOCALHOST = new byte[]{127, 0, 0, 1};
+
+ public static void createSocksConnection(final Socket socket, final String destination, final int port) throws IOException {
+ //TODO use different Socks Addr Type if destination is IP or IPv6
+ final InputStream proxyIs = socket.getInputStream();
+ final OutputStream proxyOs = socket.getOutputStream();
+ proxyOs.write(new byte[]{0x05, 0x01, 0x00});
+ proxyOs.flush();
+ final byte[] handshake = new byte[2];
+ ByteStreams.readFully(proxyIs, handshake);
+ if (handshake[0] != 0x05 || handshake[1] != 0x00) {
+ throw new SocksConnectionException("Socks 5 handshake failed");
+ }
+ final byte[] dest = destination.getBytes();
+ final ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
+ request.put(new byte[]{0x05, 0x01, 0x00, 0x03});
+ request.put((byte) dest.length);
+ request.put(dest);
+ request.putShort((short) port);
+ proxyOs.write(request.array());
+ proxyOs.flush();
+ final byte[] response = new byte[4];
+ ByteStreams.readFully(proxyIs, response);
+ final byte ver = response[0];
+ if (ver != 0x05) {
+ throw new IOException(String.format("Unknown Socks version %02X ", ver));
+ }
+ final byte status = response[1];
+ final byte bndAddrType = response[3];
+ final byte[] bndDestination = readDestination(bndAddrType, proxyIs);
+ final byte[] bndPort = new byte[2];
+ if (bndAddrType == 0x03) {
+ final String receivedDestination = new String(bndDestination);
+ if (!receivedDestination.equalsIgnoreCase(destination)) {
+ throw new IOException(String.format("Destination mismatch. Received %s Expected %s", receivedDestination, destination));
+ }
+ }
+ ByteStreams.readFully(proxyIs, bndPort);
+ if (status != 0x00) {
+ if (status == 0x04) {
+ throw new HostNotFoundException("Host unreachable");
+ }
+ if (status == 0x05) {
+ throw new HostNotFoundException("Connection refused");
+ }
+ throw new IOException(String.format("Unknown status code %02X ", status));
+ }
+ }
- public static void createSocksConnection(final Socket socket, final String destination, final int port) throws IOException {
- final InputStream proxyIs = socket.getInputStream();
- final OutputStream proxyOs = socket.getOutputStream();
- proxyOs.write(new byte[]{0x05, 0x01, 0x00});
- proxyOs.flush();
- final byte[] handshake = new byte[2];
- proxyIs.read(handshake);
- if (handshake[0] != 0x05 || handshake[1] != 0x00) {
- throw new SocksConnectionException("Socks 5 handshake failed");
- }
- final byte[] dest = destination.getBytes();
- final ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
- request.put(new byte[]{0x05, 0x01, 0x00, 0x03});
- request.put((byte) dest.length);
- request.put(dest);
- request.putShort((short) port);
- proxyOs.write(request.array());
- proxyOs.flush();
- final byte[] response = new byte[7 + dest.length];
- proxyIs.read(response);
- if (response[1] != 0x00) {
- if (response[1] == 0x04) {
- throw new HostNotFoundException("Host unreachable");
- }
- if (response[1] == 0x05) {
- throw new HostNotFoundException("Connection refused");
- }
- throw new SocksConnectionException("Unable to connect to destination "+(int) (response[1]));
- }
- }
+ private static byte[] readDestination(final byte type, final InputStream inputStream) throws IOException {
+ final byte[] bndDestination;
+ if (type == 0x01) {
+ bndDestination = new byte[4];
+ } else if (type == 0x03) {
+ final int length = inputStream.read();
+ bndDestination = new byte[length];
+ } else if (type == 0x04) {
+ bndDestination = new byte[16];
+ } else {
+ throw new IOException(String.format("Unknown Socks address type %02X ", type));
+ }
+ ByteStreams.readFully(inputStream, bndDestination);
+ return bndDestination;
+ }
- public static boolean contains(byte needle, byte[] haystack) {
- for(byte hay : haystack) {
- if (hay == needle) {
- return true;
- }
- }
- return false;
- }
+ public static boolean contains(byte needle, byte[] haystack) {
+ for (byte hay : haystack) {
+ if (hay == needle) {
+ return true;
+ }
+ }
+ return false;
+ }
- private static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException {
- Socket socket = new Socket();
- try {
- socket.connect(address, Config.CONNECT_TIMEOUT * 1000);
- } catch (IOException e) {
- throw new SocksProxyNotFoundException();
- }
- createSocksConnection(socket, destination, port);
- return socket;
- }
+ private static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException {
+ Socket socket = new Socket();
+ try {
+ socket.connect(address, Config.CONNECT_TIMEOUT * 1000);
+ } catch (IOException e) {
+ throw new SocksProxyNotFoundException();
+ }
+ createSocksConnection(socket, destination, port);
+ return socket;
+ }
- public static Socket createSocketOverTor(String destination, int port) throws IOException {
- return createSocket(new InetSocketAddress(InetAddress.getByAddress(LOCALHOST), 9050), destination, port);
- }
+ public static Socket createSocketOverTor(String destination, int port) throws IOException {
+ return createSocket(new InetSocketAddress(InetAddress.getByAddress(LOCALHOST), 9050), destination, port);
+ }
- private static class SocksConnectionException extends IOException {
- SocksConnectionException(String message) {
- super(message);
- }
- }
+ private static class SocksConnectionException extends IOException {
+ SocksConnectionException(String message) {
+ super(message);
+ }
+ }
- public static class SocksProxyNotFoundException extends IOException {
+ public static class SocksProxyNotFoundException extends IOException {
- }
+ }
- public static class HostNotFoundException extends SocksConnectionException {
- HostNotFoundException(String message) {
- super(message);
- }
- }
+ public static class HostNotFoundException extends SocksConnectionException {
+ HostNotFoundException(String message) {
+ super(message);
+ }
+ }
}
@@ -71,10 +71,14 @@ public class TimeFrameUtils {
public static String formatTimePassed(final long since, final long to, final boolean withMilliseconds) {
final long passed = (since < 0) ? 0 : (to - since);
- final int hours = (int) (passed / 3600000);
- final int minutes = (int) (passed / 60000) % 60;
- final int seconds = (int) (passed / 1000) % 60;
- final int milliseconds = (int) (passed / 100) % 10;
+ return formatElapsedTime(passed, withMilliseconds);
+ }
+
+ public static String formatElapsedTime(final long elapsed, final boolean withMilliseconds) {
+ final int hours = (int) (elapsed / 3600000);
+ final int minutes = (int) (elapsed / 60000) % 60;
+ final int seconds = (int) (elapsed / 1000) % 60;
+ final int milliseconds = (int) (elapsed / 100) % 10;
if (hours > 0) {
return String.format(Locale.ENGLISH, "%d:%02d:%02d", hours, minutes, seconds);
} else if (withMilliseconds) {
@@ -0,0 +1,41 @@
+package eu.siacs.conversations.utils;
+
+import com.otaliastudios.transcoder.strategy.DefaultAudioStrategy;
+import com.otaliastudios.transcoder.strategy.DefaultVideoStrategy;
+
+public final class TranscoderStrategies {
+
+ public static final DefaultVideoStrategy VIDEO_720P = DefaultVideoStrategy.atMost(720)
+ .bitRate(2L * 1000 * 1000)
+ .frameRate(30)
+ .keyFrameInterval(3F)
+ .build();
+
+ public static final DefaultVideoStrategy VIDEO_360P = DefaultVideoStrategy.atMost(360)
+ .bitRate(1000 * 1000)
+ .frameRate(30)
+ .keyFrameInterval(3F)
+ .build();
+
+ //TODO do we want to add 240p (@500kbs) and 1080p (@4mbs?) ?
+ // see suggested bit rates on https://www.videoproc.com/media-converter/bitrate-setting-for-h264.htm
+
+ public static final DefaultAudioStrategy AUDIO_HQ = DefaultAudioStrategy.builder()
+ .bitRate(192 * 1000)
+ .channels(2)
+ .sampleRate(DefaultAudioStrategy.SAMPLE_RATE_AS_INPUT)
+ .build();
+
+ public static final DefaultAudioStrategy AUDIO_MQ = DefaultAudioStrategy.builder()
+ .bitRate(128 * 1000)
+ .channels(2)
+ .sampleRate(DefaultAudioStrategy.SAMPLE_RATE_AS_INPUT)
+ .build();
+
+ //TODO if we add 144p we definitely want to add a lower audio bit rate as well
+
+ private TranscoderStrategies() {
+ throw new IllegalStateException("Do not instantiate me");
+ }
+
+}
@@ -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) {
@@ -4,12 +4,15 @@ import android.net.Uri;
import androidx.annotation.NonNull;
+import com.google.common.base.CharMatcher;
+import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -32,6 +35,8 @@ public class XmppUri {
private Map<String, String> parameters = Collections.emptyMap();
private boolean safeSource = true;
+ public static final String INVITE_DOMAIN = "conversations.im";
+
public XmppUri(final String uri) {
try {
parse(Uri.parse(uri));
@@ -133,10 +138,10 @@ public class XmppUri {
return;
}
this.uri = uri;
- String scheme = uri.getScheme();
- String host = uri.getHost();
+ final String scheme = uri.getScheme();
+ final String host = uri.getHost();
List<String> segments = uri.getPathSegments();
- if ("https".equalsIgnoreCase(scheme) && "conversations.im".equalsIgnoreCase(host)) {
+ if ("https".equalsIgnoreCase(scheme) && INVITE_DOMAIN.equalsIgnoreCase(host)) {
if (segments.size() >= 2 && segments.get(1).contains("@")) {
// sample : https://conversations.im/i/foo@bar.com
try {
@@ -167,7 +172,7 @@ public class XmppUri {
}
}
this.fingerprints = parseFingerprints(parameters);
- } else if ("imto".equalsIgnoreCase(scheme)) {
+ } else if ("imto".equalsIgnoreCase(scheme) && Arrays.asList("xmpp", "jabber").contains(uri.getHost())) {
// sample: imto://xmpp/foo@bar.com
try {
jid = URLDecoder.decode(uri.getEncodedPath(), "UTF-8").split("/")[1].trim();
@@ -191,7 +196,10 @@ public class XmppUri {
}
public boolean isAction(final String action) {
- return parameters.containsKey(action);
+ return Collections2.transform(
+ parameters.keySet(),
+ s -> CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z')).retainFrom(s)
+ ).contains(action);
}
public Jid getJid() {
@@ -1,5 +1,7 @@
package eu.siacs.conversations.xml;
+import org.jetbrains.annotations.NotNull;
+
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
@@ -165,8 +167,9 @@ public class Element {
return this.attributes;
}
+ @NotNull
public String toString() {
- StringBuilder elementOutput = new StringBuilder();
+ final StringBuilder elementOutput = new StringBuilder();
if ((content == null) && (children.size() == 0)) {
Tag emptyTag = Tag.empty(name);
emptyTag.setAtttributes(this.attributes);
@@ -28,6 +28,7 @@ public final class Namespace {
public static final String SYNCHRONIZATION = "im.quicksy.synchronization:0";
public static final String AVATAR_CONVERSION = "urn:xmpp:pep-vcard-conversion:0";
public static final String JINGLE = "urn:xmpp:jingle:1";
+ public static final String JINGLE_ERRORS = "urn:xmpp:jingle:errors:1";
public static final String JINGLE_MESSAGE = "urn:xmpp:jingle-message:0";
public static final String JINGLE_ENCRYPTED_TRANSPORT = "urn:xmpp:jingle:jet:0";
public static final String JINGLE_ENCRYPTED_TRANSPORT_OMEMO = "urn:xmpp:jingle:jet-omemo:0";
@@ -8,120 +8,129 @@ import java.io.OutputStreamWriter;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
public class TagWriter {
- private OutputStreamWriter outputStream;
- private boolean finished = false;
- private final LinkedBlockingQueue<AbstractStanza> writeQueue = new LinkedBlockingQueue<AbstractStanza>();
- private CountDownLatch stanzaWriterCountDownLatch = null;
-
- private final Thread asyncStanzaWriter = new Thread() {
-
- @Override
- public void run() {
- stanzaWriterCountDownLatch = new CountDownLatch(1);
- while (!isInterrupted()) {
- if (finished && writeQueue.size() == 0) {
- break;
- }
- try {
- AbstractStanza output = writeQueue.take();
- outputStream.write(output.toString());
- if (writeQueue.size() == 0) {
- outputStream.flush();
- }
- } catch (Exception e) {
- break;
- }
- }
- stanzaWriterCountDownLatch.countDown();
- }
-
- };
-
- public TagWriter() {
- }
-
- public synchronized void setOutputStream(OutputStream out) throws IOException {
- if (out == null) {
- throw new IOException();
- }
- this.outputStream = new OutputStreamWriter(out);
- }
-
- public TagWriter beginDocument() throws IOException {
- if (outputStream == null) {
- throw new IOException("output stream was null");
- }
- outputStream.write("<?xml version='1.0'?>");
- outputStream.flush();
- return this;
- }
-
- public synchronized TagWriter writeTag(Tag tag) throws IOException {
- if (outputStream == null) {
- throw new IOException("output stream was null");
- }
- outputStream.write(tag.toString());
- outputStream.flush();
- return this;
- }
-
- public synchronized TagWriter writeElement(Element element) throws IOException {
- if (outputStream == null) {
- throw new IOException("output stream was null");
- }
- outputStream.write(element.toString());
- outputStream.flush();
- return this;
- }
-
- public TagWriter writeStanzaAsync(AbstractStanza stanza) {
- if (finished) {
- Log.d(Config.LOGTAG,"attempting to write stanza to finished TagWriter");
- return this;
- } else {
- if (!asyncStanzaWriter.isAlive()) {
- try {
- asyncStanzaWriter.start();
- } catch (IllegalThreadStateException e) {
- // already started
- }
- }
- writeQueue.add(stanza);
- return this;
- }
- }
-
- public void finish() {
- this.finished = true;
- }
-
- public boolean await(long timeout, TimeUnit timeunit) throws InterruptedException {
- if (stanzaWriterCountDownLatch == null) {
- return true;
- } else {
- return stanzaWriterCountDownLatch.await(timeout, timeunit);
- }
- }
-
- public boolean isActive() {
- return outputStream != null;
- }
-
- public synchronized void forceClose() {
- asyncStanzaWriter.interrupt();
- if (outputStream != null) {
- try {
- outputStream.close();
- } catch (IOException e) {
- //ignoring
- }
- }
- outputStream = null;
- }
+ private static final int FLUSH_DELAY = 400;
+
+ private OutputStreamWriter outputStream;
+ private boolean finished = false;
+ private final LinkedBlockingQueue<AbstractStanza> writeQueue = new LinkedBlockingQueue<AbstractStanza>();
+ private CountDownLatch stanzaWriterCountDownLatch = null;
+
+ private final Thread asyncStanzaWriter = new Thread() {
+
+ private final AtomicInteger batchStanzaCount = new AtomicInteger(0);
+
+ @Override
+ public void run() {
+ stanzaWriterCountDownLatch = new CountDownLatch(1);
+ while (!isInterrupted()) {
+ if (finished && writeQueue.size() == 0) {
+ break;
+ }
+ try {
+ final AbstractStanza stanza = writeQueue.poll(FLUSH_DELAY, TimeUnit.MILLISECONDS);
+ if (stanza != null) {
+ batchStanzaCount.incrementAndGet();
+ outputStream.write(stanza.toString());
+ } else {
+ final int batch = batchStanzaCount.getAndSet(0);
+ if (batch > 1) {
+ Log.d(Config.LOGTAG, "flushing " + batch + " stanzas");
+ }
+ outputStream.flush();
+ final AbstractStanza nextStanza = writeQueue.take();
+ batchStanzaCount.incrementAndGet();
+ outputStream.write(nextStanza.toString());
+ }
+ } catch (final Exception e) {
+ break;
+ }
+ }
+ stanzaWriterCountDownLatch.countDown();
+ }
+
+ };
+
+ public TagWriter() {
+ }
+
+ public synchronized void setOutputStream(OutputStream out) throws IOException {
+ if (out == null) {
+ throw new IOException();
+ }
+ this.outputStream = new OutputStreamWriter(out);
+ }
+
+ public void beginDocument() throws IOException {
+ if (outputStream == null) {
+ throw new IOException("output stream was null");
+ }
+ outputStream.write("<?xml version='1.0'?>");
+ outputStream.flush();
+ }
+
+ public synchronized void writeTag(Tag tag) throws IOException {
+ if (outputStream == null) {
+ throw new IOException("output stream was null");
+ }
+ outputStream.write(tag.toString());
+ outputStream.flush();
+ }
+
+ public synchronized void writeElement(Element element) throws IOException {
+ if (outputStream == null) {
+ throw new IOException("output stream was null");
+ }
+ outputStream.write(element.toString());
+ outputStream.flush();
+ }
+
+ public void writeStanzaAsync(AbstractStanza stanza) {
+ if (finished) {
+ Log.d(Config.LOGTAG, "attempting to write stanza to finished TagWriter");
+ } else {
+ if (!asyncStanzaWriter.isAlive()) {
+ try {
+ asyncStanzaWriter.start();
+ } catch (IllegalThreadStateException e) {
+ // already started
+ }
+ }
+ writeQueue.add(stanza);
+ }
+ }
+
+ public void finish() {
+ this.finished = true;
+ }
+
+ public boolean await(long timeout, TimeUnit timeunit) throws InterruptedException {
+ if (stanzaWriterCountDownLatch == null) {
+ return true;
+ } else {
+ return stanzaWriterCountDownLatch.await(timeout, timeunit);
+ }
+ }
+
+ public boolean isActive() {
+ return outputStream != null;
+ }
+
+ public synchronized void forceClose() {
+ asyncStanzaWriter.interrupt();
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ //ignoring
+ }
+ }
+ outputStream = null;
+ }
}
@@ -54,7 +54,6 @@ import javax.net.ssl.X509TrustManager;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
-import eu.siacs.conversations.crypto.DomainHostnameVerifier;
import eu.siacs.conversations.crypto.XmppDomainVerifier;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.sasl.Anonymous;
@@ -206,7 +206,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
final Element error = response.addChild("error");
error.setAttribute("type", conditionType);
error.addChild(condition, "urn:ietf:params:xml:ns:xmpp-stanzas");
- error.addChild(jingleCondition, "urn:xmpp:jingle:errors:1");
+ error.addChild(jingleCondition, Namespace.JINGLE_ERRORS);
account.getXmppConnection().sendIqPacket(response, null);
}
@@ -1,6 +1,5 @@
package eu.siacs.conversations.xmpp.jingle;
-import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -8,6 +7,7 @@ import androidx.annotation.Nullable;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.Collections2;
@@ -26,13 +26,15 @@ import org.webrtc.IceCandidate;
import org.webrtc.PeerConnection;
import org.webrtc.VideoTrack;
-import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Queue;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -140,7 +142,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this);
- private final ArrayDeque<Set<Map.Entry<String, RtpContentMap.DescriptionTransport>>> pendingIceCandidates = new ArrayDeque<>();
+ private final Queue<Map.Entry<String, RtpContentMap.DescriptionTransport>> pendingIceCandidates = new LinkedList<>();
private final OmemoVerification omemoVerification = new OmemoVerification();
private final Message message;
private State state = State.NULL;
@@ -148,8 +150,9 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private Set<Media> proposedMedia;
private RtpContentMap initiatorRtpContentMap;
private RtpContentMap responderRtpContentMap;
- private long rtpConnectionStarted = 0; //time of 'connected'
- private long rtpConnectionEnded = 0;
+ private IceUdpTransportInfo.Setup peerDtlsSetup;
+ private final Stopwatch sessionDuration = Stopwatch.createUnstarted();
+ private final Queue<PeerConnection.PeerConnectionState> stateHistory = new LinkedList<>();
private ScheduledFuture<?> ringingTimeoutFuture;
JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) {
@@ -191,7 +194,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
@Override
synchronized void deliverPacket(final JinglePacket jinglePacket) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": packet delivered to JingleRtpConnection");
switch (jinglePacket.getAction()) {
case SESSION_INITIATE:
receiveSessionInitiate(jinglePacket);
@@ -230,12 +232,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
}
- //FIXME: possible implementation
public boolean applyDtmfTone(String tone) {
return webRTCWrapper.applyDtmfTone(tone);
}
-
private void receiveSessionTerminate(final JinglePacket jinglePacket) {
respondOk(jinglePacket);
final JinglePacket.ReasonWrapper wrapper = jinglePacket.getReason();
@@ -258,24 +258,15 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void receiveTransportInfo(final JinglePacket jinglePacket) {
//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 {
contentMap = RtpContentMap.of(jinglePacket);
- } catch (IllegalArgumentException | NullPointerException e) {
+ } catch (final IllegalArgumentException | NullPointerException e) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents; ignoring", e);
+ respondOk(jinglePacket);
return;
}
- final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates = contentMap.contents.entrySet();
- if (this.state == State.SESSION_ACCEPTED) {
- try {
- processCandidates(candidates);
- } catch (final WebRTCWrapper.PeerConnectionNotInitialized e) {
- Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnection was not initialized when processing transport info. this usually indicates a race condition that can be ignored");
- }
- } else {
- pendingIceCandidates.push(candidates);
- }
+ receiveTransportInfo(jinglePacket, contentMap);
} else {
if (isTerminated()) {
respondOk(jinglePacket);
@@ -287,41 +278,170 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
}
- private void processCandidates(final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) {
- final RtpContentMap rtpContentMap = isInitiator() ? this.responderRtpContentMap : this.initiatorRtpContentMap;
- final Group originalGroup = rtpContentMap.group;
- final List<String> identificationTags = originalGroup == null ? rtpContentMap.getNames() : originalGroup.getIdentificationTags();
- if (identificationTags.size() == 0) {
- Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": no identification tags found in initial offer. we won't be able to calculate mLineIndices");
+ private void receiveTransportInfo(final JinglePacket jinglePacket, final RtpContentMap contentMap) {
+ final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates = contentMap.contents.entrySet();
+ if (this.state == State.SESSION_ACCEPTED) {
+ //zero candidates + modified credentials are an ICE restart offer
+ if (checkForIceRestart(jinglePacket, contentMap)) {
+ return;
+ }
+ respondOk(jinglePacket);
+ try {
+ processCandidates(candidates);
+ } catch (final WebRTCWrapper.PeerConnectionNotInitialized e) {
+ Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnection was not initialized when processing transport info. this usually indicates a race condition that can be ignored");
+ }
+ } else {
+ respondOk(jinglePacket);
+ pendingIceCandidates.addAll(candidates);
}
- processCandidates(identificationTags, contents);
}
- private void processCandidates(final List<String> indices, final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) {
+ private boolean checkForIceRestart(final JinglePacket jinglePacket, final RtpContentMap rtpContentMap) {
+ final RtpContentMap existing = getRemoteContentMap();
+ final IceUdpTransportInfo.Credentials existingCredentials;
+ final IceUdpTransportInfo.Credentials newCredentials;
+ try {
+ existingCredentials = existing.getCredentials();
+ newCredentials = rtpContentMap.getCredentials();
+ } catch (final IllegalStateException e) {
+ Log.d(Config.LOGTAG, "unable to gather credentials for comparison", e);
+ return false;
+ }
+ if (existingCredentials.equals(newCredentials)) {
+ return false;
+ }
+ //TODO an alternative approach is to check if we already got an iq result to our ICE-restart
+ // and if that's the case we are seeing an answer.
+ // This might be more spec compliant but also more error prone potentially
+ final boolean isOffer = rtpContentMap.emptyCandidates();
+ final RtpContentMap restartContentMap;
+ try {
+ if (isOffer) {
+ Log.d(Config.LOGTAG, "received offer to restart ICE " + newCredentials);
+ restartContentMap = existing.modifiedCredentials(newCredentials, IceUdpTransportInfo.Setup.ACTPASS);
+ } else {
+ final IceUdpTransportInfo.Setup setup = getPeerDtlsSetup();
+ Log.d(Config.LOGTAG, "received confirmation of ICE restart" + newCredentials + " peer_setup=" + setup);
+ // DTLS setup attribute needs to be rewritten to reflect current peer state
+ // https://groups.google.com/g/discuss-webrtc/c/DfpIMwvUfeM
+ restartContentMap = existing.modifiedCredentials(newCredentials, setup);
+ }
+ if (applyIceRestart(jinglePacket, restartContentMap, isOffer)) {
+ return isOffer;
+ } else {
+ Log.d(Config.LOGTAG, "ignoring ICE restart. sending tie-break");
+ respondWithTieBreak(jinglePacket);
+ return true;
+ }
+ } catch (final Exception exception) {
+ respondOk(jinglePacket);
+ final Throwable rootCause = Throwables.getRootCause(exception);
+ if (rootCause instanceof WebRTCWrapper.PeerConnectionNotInitialized) {
+ //If this happens a termination is already in progress
+ Log.d(Config.LOGTAG, "ignoring PeerConnectionNotInitialized on ICE restart");
+ return true;
+ }
+ Log.d(Config.LOGTAG, "failure to apply ICE restart", rootCause);
+ webRTCWrapper.close();
+ sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
+ return true;
+ }
+ }
+
+ private IceUdpTransportInfo.Setup getPeerDtlsSetup() {
+ final IceUdpTransportInfo.Setup peerSetup = this.peerDtlsSetup;
+ if (peerSetup == null || peerSetup == IceUdpTransportInfo.Setup.ACTPASS) {
+ throw new IllegalStateException("Invalid peer setup");
+ }
+ return peerSetup;
+ }
+
+ private void storePeerDtlsSetup(final IceUdpTransportInfo.Setup setup) {
+ if (setup == null || setup == IceUdpTransportInfo.Setup.ACTPASS) {
+ throw new IllegalArgumentException("Trying to store invalid peer dtls setup");
+ }
+ this.peerDtlsSetup = setup;
+ }
+
+ private boolean applyIceRestart(final JinglePacket jinglePacket, final RtpContentMap restartContentMap, final boolean isOffer) throws ExecutionException, InterruptedException {
+ final SessionDescription sessionDescription = SessionDescription.of(restartContentMap);
+ final org.webrtc.SessionDescription.Type type = isOffer ? org.webrtc.SessionDescription.Type.OFFER : org.webrtc.SessionDescription.Type.ANSWER;
+ org.webrtc.SessionDescription sdp = new org.webrtc.SessionDescription(type, sessionDescription.toString());
+ if (isOffer && webRTCWrapper.getSignalingState() != PeerConnection.SignalingState.STABLE) {
+ if (isInitiator()) {
+ //We ignore the offer and respond with tie-break. This will clause the responder not to apply the content map
+ return false;
+ }
+ }
+ webRTCWrapper.setRemoteDescription(sdp).get();
+ setRemoteContentMap(restartContentMap);
+ if (isOffer) {
+ webRTCWrapper.setIsReadyToReceiveIceCandidates(false);
+ final SessionDescription localSessionDescription = setLocalSessionDescription();
+ setLocalContentMap(RtpContentMap.of(localSessionDescription));
+ //We need to respond OK before sending any candidates
+ respondOk(jinglePacket);
+ webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
+ } else {
+ storePeerDtlsSetup(restartContentMap.getDtlsSetup());
+ }
+ return true;
+ }
+
+ private void processCandidates(final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> contents) {
for (final Map.Entry<String, RtpContentMap.DescriptionTransport> content : contents) {
- final String ufrag = content.getValue().transport.getAttribute("ufrag");
- for (final IceUdpTransportInfo.Candidate candidate : content.getValue().transport.getCandidates()) {
- final String sdp;
- try {
- sdp = candidate.toSdpAttribute(ufrag);
- } catch (IllegalArgumentException e) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring invalid ICE candidate " + e.getMessage());
- continue;
- }
- final String sdpMid = content.getKey();
- final int mLineIndex = indices.indexOf(sdpMid);
- if (mLineIndex < 0) {
- Log.w(Config.LOGTAG, "mLineIndex not found for " + sdpMid + ". available indices " + indices);
- }
- final IceCandidate iceCandidate = new IceCandidate(sdpMid, mLineIndex, sdp);
- Log.d(Config.LOGTAG, "received candidate: " + iceCandidate);
- this.webRTCWrapper.addIceCandidate(iceCandidate);
+ processCandidate(content);
+ }
+ }
+
+ private void processCandidate(final Map.Entry<String, RtpContentMap.DescriptionTransport> content) {
+ final RtpContentMap rtpContentMap = getRemoteContentMap();
+ final List<String> indices = toIdentificationTags(rtpContentMap);
+ final String sdpMid = content.getKey(); //aka content name
+ final IceUdpTransportInfo transport = content.getValue().transport;
+ final IceUdpTransportInfo.Credentials credentials = transport.getCredentials();
+
+ //TODO check that credentials remained the same
+
+ for (final IceUdpTransportInfo.Candidate candidate : transport.getCandidates()) {
+ final String sdp;
+ try {
+ sdp = candidate.toSdpAttribute(credentials.ufrag);
+ } catch (final IllegalArgumentException e) {
+ Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring invalid ICE candidate " + e.getMessage());
+ continue;
+ }
+ final int mLineIndex = indices.indexOf(sdpMid);
+ if (mLineIndex < 0) {
+ Log.w(Config.LOGTAG, "mLineIndex not found for " + sdpMid + ". available indices " + indices);
}
+ final IceCandidate iceCandidate = new IceCandidate(sdpMid, mLineIndex, sdp);
+ Log.d(Config.LOGTAG, "received candidate: " + iceCandidate);
+ this.webRTCWrapper.addIceCandidate(iceCandidate);
+ }
+ }
+
+ private RtpContentMap getRemoteContentMap() {
+ return isInitiator() ? this.responderRtpContentMap : this.initiatorRtpContentMap;
+ }
+
+ private List<String> toIdentificationTags(final RtpContentMap rtpContentMap) {
+ final Group originalGroup = rtpContentMap.group;
+ final List<String> identificationTags = originalGroup == null ? rtpContentMap.getNames() : originalGroup.getIdentificationTags();
+ if (identificationTags.size() == 0) {
+ Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": no identification tags found in initial offer. we won't be able to calculate mLineIndices");
}
+ return identificationTags;
}
private ListenableFuture<RtpContentMap> receiveRtpContentMap(final JinglePacket jinglePacket, final boolean expectVerification) {
- final RtpContentMap receivedContentMap = RtpContentMap.of(jinglePacket);
+ final RtpContentMap receivedContentMap;
+ try {
+ receivedContentMap = RtpContentMap.of(jinglePacket);
+ } catch (final Exception e) {
+ return Futures.immediateFailedFuture(e);
+ }
if (receivedContentMap instanceof OmemoVerifiedRtpContentMap) {
final ListenableFuture<AxolotlService.OmemoVerifiedPayload<RtpContentMap>> future = id.account.getAxolotlService().decrypt((OmemoVerifiedRtpContentMap) receivedContentMap, id.with);
return Futures.transform(future, omemoVerifiedPayload -> {
@@ -372,7 +492,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void receiveSessionInitiate(final JinglePacket jinglePacket, final RtpContentMap contentMap) {
try {
contentMap.requireContentDescriptions();
- contentMap.requireDTLSFingerprint();
+ contentMap.requireDTLSFingerprint(true);
} catch (final RuntimeException e) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents", Throwables.getRootCause(e));
respondOk(jinglePacket);
@@ -400,11 +520,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
if (transition(target, () -> this.initiatorRtpContentMap = contentMap)) {
respondOk(jinglePacket);
-
- final Set<Map.Entry<String, RtpContentMap.DescriptionTransport>> candidates = contentMap.contents.entrySet();
- if (candidates.size() > 0) {
- pendingIceCandidates.push(candidates);
- }
+ pendingIceCandidates.addAll(contentMap.contents.entrySet());
if (target == State.SESSION_INITIALIZED_PRE_APPROVED) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": automatically accepting session-initiate");
sendSessionAccept();
@@ -473,6 +589,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void receiveSessionAccept(final RtpContentMap contentMap) {
this.responderRtpContentMap = contentMap;
+ this.storePeerDtlsSetup(contentMap.getDtlsSetup());
final SessionDescription sessionDescription;
try {
sessionDescription = SessionDescription.of(contentMap);
@@ -491,11 +608,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} catch (final Exception e) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to set remote description after receiving session-accept", Throwables.getRootCause(e));
webRTCWrapper.close();
- sendSessionTerminate(Reason.FAILED_APPLICATION);
+ sendSessionTerminate(Reason.FAILED_APPLICATION, Throwables.getRootCause(e).getMessage());
return;
}
- final List<String> identificationTags = contentMap.group == null ? contentMap.getNames() : contentMap.group.getIdentificationTags();
- processCandidates(identificationTags, contentMap.contents.entrySet());
+ processCandidates(contentMap.contents.entrySet());
}
private void sendSessionAccept() {
@@ -539,7 +655,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
try {
this.webRTCWrapper.setRemoteDescription(sdp).get();
addIceCandidatesFromBlackLog();
- org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createAnswer().get();
+ org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.setLocalDescription().get();
prepareSessionAccept(webRTCSessionDescription);
} catch (final Exception e) {
failureToAcceptSession(e);
@@ -550,15 +666,17 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
if (isTerminated()) {
return;
}
- Log.d(Config.LOGTAG, "unable to send session accept", Throwables.getRootCause(throwable));
+ final Throwable rootCause = Throwables.getRootCause(throwable);
+ Log.d(Config.LOGTAG, "unable to send session accept", rootCause);
webRTCWrapper.close();
- sendSessionTerminate(Reason.ofThrowable(throwable));
+ sendSessionTerminate(Reason.ofThrowable(rootCause), rootCause.getMessage());
}
private void addIceCandidatesFromBlackLog() {
- while (!this.pendingIceCandidates.isEmpty()) {
- processCandidates(this.pendingIceCandidates.poll());
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": added candidates from back log");
+ Map.Entry<String, RtpContentMap.DescriptionTransport> foo;
+ while ((foo = this.pendingIceCandidates.poll()) != null) {
+ processCandidate(foo);
+ Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": added candidate from back log");
}
}
@@ -566,12 +684,14 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
final RtpContentMap respondingRtpContentMap = RtpContentMap.of(sessionDescription);
this.responderRtpContentMap = respondingRtpContentMap;
+ storePeerDtlsSetup(respondingRtpContentMap.getDtlsSetup().flip());
+ webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
final ListenableFuture<RtpContentMap> outgoingContentMapFuture = prepareOutgoingContentMap(respondingRtpContentMap);
Futures.addCallback(outgoingContentMapFuture,
new FutureCallback<RtpContentMap>() {
@Override
public void onSuccess(final RtpContentMap outgoingContentMap) {
- sendSessionAccept(outgoingContentMap, webRTCSessionDescription);
+ sendSessionAccept(outgoingContentMap);
}
@Override
@@ -583,7 +703,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
);
}
- private void sendSessionAccept(final RtpContentMap rtpContentMap, final org.webrtc.SessionDescription webRTCSessionDescription) {
+ private void sendSessionAccept(final RtpContentMap rtpContentMap) {
if (isTerminated()) {
Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": preparing session accept was too slow. already terminated. nothing to do.");
return;
@@ -591,11 +711,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
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) {
@@ -843,9 +958,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return;
}
try {
- org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.createOffer().get();
+ org.webrtc.SessionDescription webRTCSessionDescription = this.webRTCWrapper.setLocalDescription().get();
prepareSessionInitiate(webRTCSessionDescription, targetState);
} catch (final Exception e) {
+ //TODO sending the error text is worthwhile as well. Especially for FailureToSet exceptions
failureToInitiateSession(e, targetState);
}
}
@@ -875,11 +991,12 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
final SessionDescription sessionDescription = SessionDescription.parse(webRTCSessionDescription.description);
final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription);
this.initiatorRtpContentMap = rtpContentMap;
+ this.webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
final ListenableFuture<RtpContentMap> outgoingContentMapFuture = encryptSessionInitiate(rtpContentMap);
Futures.addCallback(outgoingContentMapFuture, new FutureCallback<RtpContentMap>() {
@Override
public void onSuccess(final RtpContentMap outgoingContentMap) {
- sendSessionInitiate(outgoingContentMap, webRTCSessionDescription, targetState);
+ sendSessionInitiate(outgoingContentMap, targetState);
}
@Override
@@ -889,7 +1006,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}, MoreExecutors.directExecutor());
}
- private void sendSessionInitiate(final RtpContentMap rtpContentMap, final org.webrtc.SessionDescription webRTCSessionDescription, final State targetState) {
+ private void sendSessionInitiate(final RtpContentMap rtpContentMap, final State targetState) {
if (isTerminated()) {
Log.w(Config.LOGTAG, id.account.getJid().asBareJid() + ": preparing session was too slow. already terminated. nothing to do.");
return;
@@ -897,11 +1014,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
this.transitionOrThrow(targetState);
final JinglePacket sessionInitiate = rtpContentMap.toJinglePacket(JinglePacket.Action.SESSION_INITIATE, id.sessionId);
send(sessionInitiate);
- try {
- this.webRTCWrapper.setLocalDescription(webRTCSessionDescription).get();
- } catch (Exception e) {
- failureToInitiateSession(e, targetState);
- }
}
private ListenableFuture<RtpContentMap> encryptSessionInitiate(final RtpContentMap rtpContentMap) {
@@ -967,36 +1079,48 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private synchronized void handleIqResponse(final Account account, final IqPacket response) {
if (response.getType() == IqPacket.TYPE.ERROR) {
- final String errorCondition = response.getErrorCondition();
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received IQ-error from " + response.getFrom() + " in RTP session. " + errorCondition);
- if (isTerminated()) {
- Log.i(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring error because session was already terminated");
- return;
- }
- this.webRTCWrapper.close();
- final State target;
- if (Arrays.asList(
- "service-unavailable",
- "recipient-unavailable",
- "remote-server-not-found",
- "remote-server-timeout"
- ).contains(errorCondition)) {
- target = State.TERMINATED_CONNECTIVITY_ERROR;
- } else {
- target = State.TERMINATED_APPLICATION_FAILURE;
- }
- transitionOrThrow(target);
- this.finish();
- } else if (response.getType() == IqPacket.TYPE.TIMEOUT) {
- Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received IQ timeout in RTP session with " + id.with + ". terminating with connectivity error");
- if (isTerminated()) {
- Log.i(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring error because session was already terminated");
- return;
- }
- this.webRTCWrapper.close();
- transitionOrThrow(State.TERMINATED_CONNECTIVITY_ERROR);
- this.finish();
+ handleIqErrorResponse(response);
+ return;
+ }
+ if (response.getType() == IqPacket.TYPE.TIMEOUT) {
+ handleIqTimeoutResponse(response);
+ }
+ }
+
+ private void handleIqErrorResponse(final IqPacket response) {
+ Preconditions.checkArgument(response.getType() == IqPacket.TYPE.ERROR);
+ final String errorCondition = response.getErrorCondition();
+ Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received IQ-error from " + response.getFrom() + " in RTP session. " + errorCondition);
+ if (isTerminated()) {
+ Log.i(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring error because session was already terminated");
+ return;
+ }
+ this.webRTCWrapper.close();
+ final State target;
+ if (Arrays.asList(
+ "service-unavailable",
+ "recipient-unavailable",
+ "remote-server-not-found",
+ "remote-server-timeout"
+ ).contains(errorCondition)) {
+ target = State.TERMINATED_CONNECTIVITY_ERROR;
+ } else {
+ target = State.TERMINATED_APPLICATION_FAILURE;
+ }
+ transitionOrThrow(target);
+ this.finish();
+ }
+
+ private void handleIqTimeoutResponse(final IqPacket response) {
+ Preconditions.checkArgument(response.getType() == IqPacket.TYPE.TIMEOUT);
+ Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received IQ timeout in RTP session with " + id.with + ". terminating with connectivity error");
+ if (isTerminated()) {
+ Log.i(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring error because session was already terminated");
+ return;
}
+ this.webRTCWrapper.close();
+ transitionOrThrow(State.TERMINATED_CONNECTIVITY_ERROR);
+ this.finish();
}
private void terminateWithOutOfOrder(final JinglePacket jinglePacket) {
@@ -1007,8 +1131,16 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
this.finish();
}
+ private void respondWithTieBreak(final JinglePacket jinglePacket) {
+ respondWithJingleError(jinglePacket, "tie-break", "conflict", "cancel");
+ }
+
private void respondWithOutOfOrder(final JinglePacket jinglePacket) {
- jingleConnectionManager.respondWithJingleError(id.account, jinglePacket, "out-of-order", "unexpected-request", "wait");
+ respondWithJingleError(jinglePacket, "out-of-order", "unexpected-request", "wait");
+ }
+
+ void respondWithJingleError(final IqPacket original, String jingleCondition, String condition, String conditionType) {
+ jingleConnectionManager.respondWithJingleError(id.account, original, jingleCondition, condition, conditionType);
}
private void respondOk(final JinglePacket jinglePacket) {
@@ -1045,23 +1177,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return RtpEndUserState.CONNECTING;
}
case SESSION_ACCEPTED:
- final PeerConnection.PeerConnectionState state;
- try {
- state = webRTCWrapper.getState();
- } catch (final WebRTCWrapper.PeerConnectionNotInitialized e) {
- //We usually close the WebRTCWrapper *before* transitioning so we might still
- //be in SESSION_ACCEPTED even though the peerConnection has been torn down
- return RtpEndUserState.ENDING_CALL;
- }
- if (state == PeerConnection.PeerConnectionState.CONNECTED) {
- return RtpEndUserState.CONNECTED;
- } else if (state == PeerConnection.PeerConnectionState.NEW || state == PeerConnection.PeerConnectionState.CONNECTING) {
- return RtpEndUserState.CONNECTING;
- } else if (state == PeerConnection.PeerConnectionState.CLOSED) {
- return RtpEndUserState.ENDING_CALL;
- } else {
- return rtpConnectionStarted == 0 ? RtpEndUserState.CONNECTIVITY_ERROR : RtpEndUserState.CONNECTIVITY_LOST_ERROR;
- }
+ return getPeerConnectionStateAsEndUserState();
case REJECTED:
case REJECTED_RACED:
case TERMINATED_DECLINED_OR_BUSY:
@@ -1082,7 +1198,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return RtpEndUserState.RETRACTED;
}
case TERMINATED_CONNECTIVITY_ERROR:
- return rtpConnectionStarted == 0 ? RtpEndUserState.CONNECTIVITY_ERROR : RtpEndUserState.CONNECTIVITY_LOST_ERROR;
+ return zeroDuration() ? RtpEndUserState.CONNECTIVITY_ERROR : RtpEndUserState.CONNECTIVITY_LOST_ERROR;
case TERMINATED_APPLICATION_FAILURE:
return RtpEndUserState.APPLICATION_ERROR;
case TERMINATED_SECURITY_ERROR:
@@ -1091,6 +1207,29 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
throw new IllegalStateException(String.format("%s has no equivalent EndUserState", this.state));
}
+
+ private RtpEndUserState getPeerConnectionStateAsEndUserState() {
+ final PeerConnection.PeerConnectionState state;
+ try {
+ state = webRTCWrapper.getState();
+ } catch (final WebRTCWrapper.PeerConnectionNotInitialized e) {
+ //We usually close the WebRTCWrapper *before* transitioning so we might still
+ //be in SESSION_ACCEPTED even though the peerConnection has been torn down
+ return RtpEndUserState.ENDING_CALL;
+ }
+ switch (state) {
+ case CONNECTED:
+ return RtpEndUserState.CONNECTED;
+ case NEW:
+ case CONNECTING:
+ return RtpEndUserState.CONNECTING;
+ case CLOSED:
+ return RtpEndUserState.ENDING_CALL;
+ default:
+ return zeroDuration() ? RtpEndUserState.CONNECTIVITY_ERROR : RtpEndUserState.RECONNECTING;
+ }
+ }
+
public Set<Media> getMedia() {
final State current = getState();
if (current == State.NULL) {
@@ -1333,7 +1472,13 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
@Override
public void onIceCandidate(final IceCandidate iceCandidate) {
- final IceUdpTransportInfo.Candidate candidate = IceUdpTransportInfo.Candidate.fromSdpAttribute(iceCandidate.sdp);
+ final RtpContentMap rtpContentMap = isInitiator() ? this.initiatorRtpContentMap : this.responderRtpContentMap;
+ final String ufrag = rtpContentMap.getCredentials().ufrag;
+ final IceUdpTransportInfo.Candidate candidate = IceUdpTransportInfo.Candidate.fromSdpAttribute(iceCandidate.sdp, ufrag);
+ if (candidate == null) {
+ Log.d(Config.LOGTAG, "ignoring (not sending) candidate: " + iceCandidate.toString());
+ return;
+ }
Log.d(Config.LOGTAG, "sending candidate: " + iceCandidate.toString());
sendTransportInfo(iceCandidate.sdpMid, candidate);
}
@@ -1341,26 +1486,97 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
@Override
public void onConnectionChange(final PeerConnection.PeerConnectionState newState) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnectionState changed to " + newState);
- if (newState == PeerConnection.PeerConnectionState.CONNECTED && this.rtpConnectionStarted == 0) {
- this.rtpConnectionStarted = SystemClock.elapsedRealtime();
+ this.stateHistory.add(newState);
+ if (newState == PeerConnection.PeerConnectionState.CONNECTED) {
+ this.sessionDuration.start();
+ updateOngoingCallNotification();
+ } else if (this.sessionDuration.isRunning()) {
+ this.sessionDuration.stop();
+ updateOngoingCallNotification();
}
- if (newState == PeerConnection.PeerConnectionState.CLOSED && this.rtpConnectionEnded == 0) {
- this.rtpConnectionEnded = SystemClock.elapsedRealtime();
+
+ final boolean neverConnected = !this.stateHistory.contains(PeerConnection.PeerConnectionState.CONNECTED);
+
+ if (newState == PeerConnection.PeerConnectionState.FAILED) {
+ if (neverConnected) {
+ 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);
+ return;
+ }
+ webRTCWrapper.execute(this::closeWebRTCSessionAfterFailedConnection);
+ return;
+ } else {
+ webRTCWrapper.restartIce();
+ }
}
- //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
- 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);
+ updateEndUserState();
+ }
+
+ @Override
+ public void onRenegotiationNeeded() {
+ this.webRTCWrapper.execute(this::initiateIceRestart);
+ }
+
+ private void initiateIceRestart() {
+ //TODO discover new TURN/STUN credentials
+ this.stateHistory.clear();
+ this.webRTCWrapper.setIsReadyToReceiveIceCandidates(false);
+ final SessionDescription sessionDescription;
+ try {
+ sessionDescription = setLocalSessionDescription();
+ } catch (final Exception e) {
+ final Throwable cause = Throwables.getRootCause(e);
+ Log.d(Config.LOGTAG, "failed to renegotiate", cause);
+ sendSessionTerminate(Reason.FAILED_APPLICATION, cause.getMessage());
+ return;
+ }
+ final RtpContentMap rtpContentMap = RtpContentMap.of(sessionDescription);
+ final RtpContentMap transportInfo = rtpContentMap.transportInfo();
+ final JinglePacket jinglePacket = transportInfo.toJinglePacket(JinglePacket.Action.TRANSPORT_INFO, id.sessionId);
+ Log.d(Config.LOGTAG, "initiating ice restart: " + jinglePacket);
+ jinglePacket.setTo(id.with);
+ xmppConnectionService.sendIqPacket(id.account, jinglePacket, (account, response) -> {
+ if (response.getType() == IqPacket.TYPE.RESULT) {
+ Log.d(Config.LOGTAG, "received success to our ice restart");
+ setLocalContentMap(rtpContentMap);
+ webRTCWrapper.setIsReadyToReceiveIceCandidates(true);
return;
}
- new Thread(this::closeWebRTCSessionAfterFailedConnection).start();
+ if (response.getType() == IqPacket.TYPE.ERROR) {
+ final Element error = response.findChild("error");
+ if (error != null && error.hasChild("tie-break", Namespace.JINGLE_ERRORS)) {
+ Log.d(Config.LOGTAG, "received tie-break as result of ice restart");
+ return;
+ }
+ handleIqErrorResponse(response);
+ }
+ if (response.getType() == IqPacket.TYPE.TIMEOUT) {
+ handleIqTimeoutResponse(response);
+ }
+ });
+ }
+
+ private void setLocalContentMap(final RtpContentMap rtpContentMap) {
+ if (isInitiator()) {
+ this.initiatorRtpContentMap = rtpContentMap;
} else {
- updateEndUserState();
+ this.responderRtpContentMap = rtpContentMap;
}
}
+ private void setRemoteContentMap(final RtpContentMap rtpContentMap) {
+ if (isInitiator()) {
+ this.responderRtpContentMap = rtpContentMap;
+ } else {
+ this.initiatorRtpContentMap = rtpContentMap;
+ }
+ }
+
+ private SessionDescription setLocalSessionDescription() throws ExecutionException, InterruptedException {
+ final org.webrtc.SessionDescription sessionDescription = this.webRTCWrapper.setLocalDescription().get();
+ return SessionDescription.parse(sessionDescription.description);
+ }
+
private void closeWebRTCSessionAfterFailedConnection() {
this.webRTCWrapper.close();
synchronized (this) {
@@ -1372,12 +1588,12 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
}
- public long getRtpConnectionStarted() {
- return this.rtpConnectionStarted;
+ public boolean zeroDuration() {
+ return this.sessionDuration.elapsed(TimeUnit.NANOSECONDS) <= 0;
}
- public long getRtpConnectionEnded() {
- return this.rtpConnectionEnded;
+ public long getCallDuration() {
+ return this.sessionDuration.elapsed(TimeUnit.MILLISECONDS);
}
public AppRTCAudioManager getAudioManager() {
@@ -1424,8 +1640,15 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
private void updateOngoingCallNotification() {
- if (STATES_SHOWING_ONGOING_CALL.contains(this.state)) {
- xmppConnectionService.setOngoingCall(id, getMedia());
+ final State state = this.state;
+ if (STATES_SHOWING_ONGOING_CALL.contains(state)) {
+ final boolean reconnecting;
+ if (state == State.SESSION_ACCEPTED) {
+ reconnecting = getPeerConnectionStateAsEndUserState() == RtpEndUserState.RECONNECTING;
+ } else {
+ reconnecting = false;
+ }
+ xmppConnectionService.setOngoingCall(id, getMedia(), reconnecting);
} else {
xmppConnectionService.removeOngoingCall();
}
@@ -1504,8 +1727,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
}
private void writeLogMessage(final State state) {
- final long started = this.rtpConnectionStarted;
- long duration = started <= 0 ? 0 : SystemClock.elapsedRealtime() - started;
+ final long duration = getCallDuration();
if (state == State.TERMINATED_SUCCESS || (state == State.TERMINATED_CONNECTIVITY_ERROR && duration > 0)) {
writeLogMessageSuccess(duration);
} else {
@@ -1550,7 +1772,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return webRTCWrapper.getRemoteVideoTrack();
}
-
public EglBase.Context getEglBaseContext() {
return webRTCWrapper.getEglBaseContext();
}
@@ -3,6 +3,8 @@ package eu.siacs.conversations.xmpp.jingle;
import android.os.PowerManager;
import android.util.Log;
+import com.google.common.io.ByteStreams;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -58,22 +60,12 @@ public class JingleSocks5Transport extends JingleTransport {
} else {
destBuilder.append(this.connection.getTransportId());
}
- if (candidate.getType() == JingleCandidate.TYPE_PROXY) {
- if (candidate.isOurs()) {
- destBuilder.append(this.account.getJid());
- destBuilder.append(this.connection.getId().with);
- } else {
- destBuilder.append(this.connection.getId().with);
- destBuilder.append(this.account.getJid());
- }
+ if (candidate.isOurs()) {
+ destBuilder.append(this.account.getJid());
+ destBuilder.append(this.connection.getId().with);
} else {
- if (connection.isInitiator()) {
- destBuilder.append(this.account.getJid());
- destBuilder.append(this.connection.getId().with);
- } else {
- destBuilder.append(this.connection.getId().with);
- destBuilder.append(this.account.getJid());
- }
+ destBuilder.append(this.connection.getId().with);
+ destBuilder.append(this.account.getJid());
}
messageDigest.reset();
this.destination = CryptoHelper.bytesToHex(messageDigest.digest(destBuilder.toString().getBytes()));
@@ -114,26 +106,26 @@ public class JingleSocks5Transport extends JingleTransport {
final byte[] authBegin = new byte[2];
final InputStream inputStream = socket.getInputStream();
final OutputStream outputStream = socket.getOutputStream();
- inputStream.read(authBegin);
+ ByteStreams.readFully(inputStream, authBegin);
if (authBegin[0] != 0x5) {
socket.close();
}
final short methodCount = authBegin[1];
final byte[] methods = new byte[methodCount];
- inputStream.read(methods);
+ ByteStreams.readFully(inputStream, methods);
if (SocksSocketFactory.contains((byte) 0x00, methods)) {
outputStream.write(new byte[]{0x05, 0x00});
} else {
outputStream.write(new byte[]{0x05, (byte) 0xff});
}
- byte[] connectCommand = new byte[4];
- inputStream.read(connectCommand);
+ final byte[] connectCommand = new byte[4];
+ ByteStreams.readFully(inputStream, connectCommand);
if (connectCommand[0] == 0x05 && connectCommand[1] == 0x01 && connectCommand[3] == 0x03) {
int destinationCount = inputStream.read();
final byte[] destination = new byte[destinationCount];
- inputStream.read(destination);
+ ByteStreams.readFully(inputStream, destination);
final byte[] port = new byte[2];
- inputStream.read(port);
+ ByteStreams.readFully(inputStream, port);
final String receivedDestination = new String(destination);
final ByteBuffer response = ByteBuffer.allocate(7 + destination.length);
final byte[] responseHeader;
@@ -187,7 +179,7 @@ public class JingleSocks5Transport extends JingleTransport {
socket.setSoTimeout(0);
isEstablished = true;
callback.established();
- } catch (IOException e) {
+ } catch (final IOException e) {
callback.failed();
}
}).start();
@@ -1,13 +1,12 @@
package eu.siacs.conversations.xmpp.jingle;
-import android.util.Log;
-
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -17,9 +16,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
-import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
import eu.siacs.conversations.xmpp.jingle.stanzas.GenericDescription;
import eu.siacs.conversations.xmpp.jingle.stanzas.GenericTransportInfo;
@@ -97,6 +96,10 @@ public class RtpContentMap {
}
void requireDTLSFingerprint() {
+ requireDTLSFingerprint(false);
+ }
+
+ void requireDTLSFingerprint(final boolean requireActPass) {
if (this.contents.size() == 0) {
throw new IllegalStateException("No contents available");
}
@@ -106,9 +109,13 @@ public class RtpContentMap {
if (fingerprint == null || Strings.isNullOrEmpty(fingerprint.getContent()) || Strings.isNullOrEmpty(fingerprint.getHash())) {
throw new SecurityException(String.format("Use of DTLS-SRTP (XEP-0320) is required for content %s", entry.getKey()));
}
- if (Strings.isNullOrEmpty(fingerprint.getSetup())) {
+ final IceUdpTransportInfo.Setup setup = fingerprint.getSetup();
+ if (setup == null) {
throw new SecurityException(String.format("Use of DTLS-SRTP (XEP-0320) is required for content %s but missing setup attribute", entry.getKey()));
}
+ if (requireActPass && setup != IceUdpTransportInfo.Setup.ACTPASS) {
+ throw new SecurityException("Initiator needs to offer ACTPASS as setup for DTLS-SRTP (XEP-0320)");
+ }
}
}
@@ -137,7 +144,56 @@ public class RtpContentMap {
final IceUdpTransportInfo newTransportInfo = transportInfo.cloneWrapper();
newTransportInfo.addChild(candidate);
return new RtpContentMap(null, ImmutableMap.of(contentName, new DescriptionTransport(null, newTransportInfo)));
+ }
+
+ RtpContentMap transportInfo() {
+ return new RtpContentMap(
+ null,
+ Maps.transformValues(contents, dt -> new DescriptionTransport(null, dt.transport.cloneWrapper()))
+ );
+ }
+
+ public IceUdpTransportInfo.Credentials getCredentials() {
+ final Set<IceUdpTransportInfo.Credentials> allCredentials = ImmutableSet.copyOf(Collections2.transform(
+ contents.values(),
+ dt -> dt.transport.getCredentials()
+ ));
+ final IceUdpTransportInfo.Credentials credentials = Iterables.getFirst(allCredentials, null);
+ if (allCredentials.size() == 1 && credentials != null) {
+ return credentials;
+ }
+ throw new IllegalStateException("Content map does not have distinct credentials");
+ }
+ public IceUdpTransportInfo.Setup getDtlsSetup() {
+ final Set<IceUdpTransportInfo.Setup> setups = ImmutableSet.copyOf(Collections2.transform(
+ contents.values(),
+ dt -> dt.transport.getFingerprint().getSetup()
+ ));
+ final IceUdpTransportInfo.Setup setup = Iterables.getFirst(setups, null);
+ if (setups.size() == 1 && setup != null) {
+ return setup;
+ }
+ throw new IllegalStateException("Content map doesn't have distinct DTLS setup");
+ }
+
+ public boolean emptyCandidates() {
+ int count = 0;
+ for (DescriptionTransport descriptionTransport : contents.values()) {
+ count += descriptionTransport.transport.getCandidates().size();
+ }
+ return count == 0;
+ }
+
+ public RtpContentMap modifiedCredentials(IceUdpTransportInfo.Credentials credentials, final IceUdpTransportInfo.Setup setup) {
+ final ImmutableMap.Builder<String, DescriptionTransport> contentMapBuilder = new ImmutableMap.Builder<>();
+ for (final Map.Entry<String, DescriptionTransport> content : contents.entrySet()) {
+ final RtpDescription rtpDescription = content.getValue().description;
+ IceUdpTransportInfo transportInfo = content.getValue().transport;
+ final IceUdpTransportInfo modifiedTransportInfo = transportInfo.modifyCredentials(credentials, setup);
+ contentMapBuilder.put(content.getKey(), new DescriptionTransport(rtpDescription, modifiedTransportInfo));
+ }
+ return new RtpContentMap(this.group, contentMapBuilder.build());
}
public static class DescriptionTransport {
@@ -159,7 +215,6 @@ public class RtpContentMap {
} else if (description instanceof RtpDescription) {
rtpDescription = (RtpDescription) description;
} else {
- Log.d(Config.LOGTAG, "description was " + description);
throw new UnsupportedApplicationException("Content does not contain rtp description");
}
if (transportInfo instanceof IceUdpTransportInfo) {
@@ -4,6 +4,7 @@ public enum RtpEndUserState {
INCOMING_CALL, //received a 'propose' message
CONNECTING, //session-initiate or session-accepted but no webrtc peer connection yet
CONNECTED, //session-accepted and webrtc peer connection is connected
+ RECONNECTING, //session-accepted and webrtc peer connection was connected once but is currently disconnected or failed
FINDING_DEVICE, //'propose' has been sent out; no 184 ack yet
RINGING, //'propose' has been sent out and it has been 184 acked
ACCEPTING_CALL, //'proceed' message has been sent; but no session-initiate has been received
@@ -156,7 +156,10 @@ public class SessionDescription {
final IceUdpTransportInfo.Fingerprint fingerprint = transport.getFingerprint();
if (fingerprint != null) {
mediaAttributes.put("fingerprint", fingerprint.getHash() + " " + fingerprint.getContent());
- mediaAttributes.put("setup", fingerprint.getSetup());
+ final IceUdpTransportInfo.Setup setup = fingerprint.getSetup();
+ if (setup != null) {
+ mediaAttributes.put("setup", setup.toString().toLowerCase(Locale.ROOT));
+ }
}
final ImmutableList.Builder<Integer> formatBuilder = new ImmutableList.Builder<>();
for (RtpDescription.PayloadType payloadType : description.getPayloadTypes()) {
@@ -51,7 +51,7 @@ class ToneManager {
return ToneState.ENDING_CALL;
}
}
- if (state == RtpEndUserState.CONNECTED) {
+ if (state == RtpEndUserState.CONNECTED || state == RtpEndUserState.RECONNECTING) {
if (media.contains(Media.VIDEO)) {
return ToneState.NULL;
} else {
@@ -19,7 +19,6 @@ import com.google.common.util.concurrent.SettableFuture;
import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
-import org.webrtc.Camera1Enumerator;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerationAndroid;
import org.webrtc.CameraEnumerator;
@@ -48,9 +47,14 @@ import org.webrtc.voiceengine.WebRtcAudioEffects;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Queue;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -63,7 +67,8 @@ public class WebRTCWrapper {
private static final String EXTENDED_LOGGING_TAG = WebRTCWrapper.class.getSimpleName();
- //we should probably keep this in sync with: https://github.com/signalapp/Signal-Android/blob/master/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java#L296
+ private final ExecutorService executorService = Executors.newSingleThreadExecutor();
+
private static final Set<String> HARDWARE_AEC_BLACKLIST = new ImmutableSet.Builder<String>()
.add("Pixel")
.add("Pixel XL")
@@ -77,6 +82,9 @@ public class WebRTCWrapper {
.add("Redmi Note 5")
.add("FP2") // Fairphone FP2
.add("MI 5")
+ .add("GT-I9515") // Samsung Galaxy S4 Value Edition (jfvelte)
+ .add("GT-I9515L") // Samsung Galaxy S4 Value Edition (jfvelte)
+ .add("GT-I9505") // Samsung Galaxy S4 (jfltexx)
.build();
private static final int TONE_DURATION = 200;
@@ -102,6 +110,8 @@ public class WebRTCWrapper {
private static final int CAPTURING_MAX_FRAME_RATE = 30;
private final EventCallback eventCallback;
+ private final AtomicBoolean readyToReceivedIceCandidates = new AtomicBoolean(false);
+ private final Queue<IceCandidate> iceCandidates = new LinkedList<>();
private final AppRTCAudioManager.AudioManagerEvents audioManagerEvents = new AppRTCAudioManager.AudioManagerEvents() {
@Override
public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) {
@@ -121,13 +131,13 @@ public class WebRTCWrapper {
}
@Override
- public void onConnectionChange(PeerConnection.PeerConnectionState newState) {
+ public void onConnectionChange(final PeerConnection.PeerConnectionState newState) {
eventCallback.onConnectionChange(newState);
}
@Override
public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
-
+ Log.d(EXTENDED_LOGGING_TAG, "onIceConnectionChange(" + iceConnectionState + ")");
}
@Override
@@ -148,7 +158,11 @@ public class WebRTCWrapper {
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
- eventCallback.onIceCandidate(iceCandidate);
+ if (readyToReceivedIceCandidates.get()) {
+ eventCallback.onIceCandidate(iceCandidate);
+ } else {
+ iceCandidates.add(iceCandidate);
+ }
}
@Override
@@ -173,7 +187,11 @@ public class WebRTCWrapper {
@Override
public void onRenegotiationNeeded() {
-
+ Log.d(EXTENDED_LOGGING_TAG, "onRenegotiationNeeded()");
+ final PeerConnection.PeerConnectionState currentState = peerConnection == null ? null : peerConnection.connectionState();
+ if (currentState != null && currentState != PeerConnection.PeerConnectionState.NEW) {
+ eventCallback.onRenegotiationNeeded();
+ }
}
@Override
@@ -274,10 +292,7 @@ public class WebRTCWrapper {
.createPeerConnectionFactory();
- final PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
- rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED; //XEP-0176 doesn't support tcp
- rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
- rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
+ final PeerConnection.RTCConfiguration rtcConfig = buildConfiguration(iceServers);
final PeerConnection peerConnection = peerConnectionFactory.createPeerConnection(rtcConfig, peerConnectionObserver);
if (peerConnection == null) {
throw new InitializationException("Unable to create PeerConnection");
@@ -311,6 +326,31 @@ public class WebRTCWrapper {
this.peerConnection = peerConnection;
}
+ private static PeerConnection.RTCConfiguration buildConfiguration(final List<PeerConnection.IceServer> iceServers) {
+ final PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
+ rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED; //XEP-0176 doesn't support tcp
+ rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
+ rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
+ rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.NEGOTIATE;
+ rtcConfig.enableImplicitRollback = true;
+ return rtcConfig;
+ }
+
+ void reconfigurePeerConnection(final List<PeerConnection.IceServer> iceServers) {
+ requirePeerConnection().setConfiguration(buildConfiguration(iceServers));
+ }
+
+ void restartIce() {
+ executorService.execute(() -> requirePeerConnection().restartIce());
+ }
+
+ public void setIsReadyToReceiveIceCandidates(final boolean ready) {
+ readyToReceivedIceCandidates.set(ready);
+ while (ready && iceCandidates.peek() != null) {
+ eventCallback.onIceCandidate(iceCandidates.poll());
+ }
+ }
+
synchronized void close() {
final PeerConnection peerConnection = this.peerConnection;
final CapturerChoice capturerChoice = this.capturerChoice;
@@ -425,70 +465,36 @@ public class WebRTCWrapper {
videoTrack.setEnabled(enabled);
}
- ListenableFuture<SessionDescription> createOffer() {
- return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
- final SettableFuture<SessionDescription> future = SettableFuture.create();
- peerConnection.createOffer(new CreateSdpObserver() {
- @Override
- public void onCreateSuccess(SessionDescription sessionDescription) {
- future.set(sessionDescription);
- }
-
- @Override
- public void onCreateFailure(String s) {
- future.setException(new IllegalStateException("Unable to create offer: " + s));
- }
- }, new MediaConstraints());
- return future;
- }, MoreExecutors.directExecutor());
- }
-
- ListenableFuture<SessionDescription> createAnswer() {
+ synchronized ListenableFuture<SessionDescription> setLocalDescription() {
return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
final SettableFuture<SessionDescription> future = SettableFuture.create();
- peerConnection.createAnswer(new CreateSdpObserver() {
+ peerConnection.setLocalDescription(new SetSdpObserver() {
@Override
- public void onCreateSuccess(SessionDescription sessionDescription) {
- future.set(sessionDescription);
+ public void onSetSuccess() {
+ final SessionDescription description = peerConnection.getLocalDescription();
+ Log.d(EXTENDED_LOGGING_TAG, "set local description:");
+ logDescription(description);
+ future.set(description);
}
@Override
- public void onCreateFailure(String s) {
- future.setException(new IllegalStateException("Unable to create answer: " + s));
+ public void onSetFailure(final String message) {
+ future.setException(new FailureToSetDescriptionException(message));
}
- }, new MediaConstraints());
+ });
return future;
}, MoreExecutors.directExecutor());
}
- ListenableFuture<Void> setLocalDescription(final SessionDescription sessionDescription) {
- Log.d(EXTENDED_LOGGING_TAG, "setting local description:");
+ private static void logDescription(final SessionDescription sessionDescription) {
for (final String line : sessionDescription.description.split(eu.siacs.conversations.xmpp.jingle.SessionDescription.LINE_DIVIDER)) {
Log.d(EXTENDED_LOGGING_TAG, line);
}
- return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
- final SettableFuture<Void> future = SettableFuture.create();
- peerConnection.setLocalDescription(new SetSdpObserver() {
- @Override
- public void onSetSuccess() {
- future.set(null);
- }
-
- @Override
- public void onSetFailure(final String s) {
- future.setException(new IllegalArgumentException("unable to set local session description: " + s));
-
- }
- }, sessionDescription);
- return future;
- }, MoreExecutors.directExecutor());
}
- ListenableFuture<Void> setRemoteDescription(final SessionDescription sessionDescription) {
+ synchronized ListenableFuture<Void> setRemoteDescription(final SessionDescription sessionDescription) {
Log.d(EXTENDED_LOGGING_TAG, "setting remote description:");
- for (final String line : sessionDescription.description.split(eu.siacs.conversations.xmpp.jingle.SessionDescription.LINE_DIVIDER)) {
- Log.d(EXTENDED_LOGGING_TAG, line);
- }
+ logDescription(sessionDescription);
return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
final SettableFuture<Void> future = SettableFuture.create();
peerConnection.setRemoteDescription(new SetSdpObserver() {
@@ -498,9 +504,8 @@ public class WebRTCWrapper {
}
@Override
- public void onSetFailure(String s) {
- future.setException(new IllegalArgumentException("unable to set remote session description: " + s));
-
+ public void onSetFailure(final String message) {
+ future.setException(new FailureToSetDescriptionException(message));
}
}, sessionDescription);
return future;
@@ -511,15 +516,18 @@ public class WebRTCWrapper {
private ListenableFuture<PeerConnection> getPeerConnectionFuture() {
final PeerConnection peerConnection = this.peerConnection;
if (peerConnection == null) {
- return Futures.immediateFailedFuture(new IllegalStateException("initialize PeerConnection first"));
+ return Futures.immediateFailedFuture(new PeerConnectionNotInitialized());
} else {
return Futures.immediateFuture(peerConnection);
}
}
- //TODO: remove - hack to test dtmfSending
- public DtmfSender getDtmfSender() {
- return peerConnection.getSenders().get(0).dtmf();
+ private PeerConnection requirePeerConnection() {
+ final PeerConnection peerConnection = this.peerConnection;
+ if (peerConnection == null) {
+ throw new PeerConnectionNotInitialized();
+ }
+ return peerConnection;
}
public boolean applyDtmfTone(String tone) {
@@ -535,16 +543,8 @@ public class WebRTCWrapper {
requirePeerConnection().addIceCandidate(iceCandidate);
}
- private CameraEnumerator getCameraEnumerator() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- return new Camera2Enumerator(requireContext());
- } else {
- return new Camera1Enumerator();
- }
- }
-
private Optional<CapturerChoice> getVideoCapturer() {
- final CameraEnumerator enumerator = getCameraEnumerator();
+ final CameraEnumerator enumerator = new Camera2Enumerator(requireContext());
final Set<String> deviceNames = ImmutableSet.copyOf(enumerator.getDeviceNames());
for (final String deviceName : deviceNames) {
if (isFrontFacing(enumerator, deviceName)) {
@@ -563,10 +563,15 @@ public class WebRTCWrapper {
}
}
- public PeerConnection.PeerConnectionState getState() {
+ PeerConnection.PeerConnectionState getState() {
return requirePeerConnection().connectionState();
}
+ public PeerConnection.SignalingState getSignalingState() {
+ return requirePeerConnection().signalingState();
+ }
+
+
EglBase.Context getEglBaseContext() {
return this.eglBase.getEglBaseContext();
}
@@ -579,14 +584,6 @@ public class WebRTCWrapper {
return Optional.fromNullable(this.remoteVideoTrack);
}
- private PeerConnection requirePeerConnection() {
- final PeerConnection peerConnection = this.peerConnection;
- if (peerConnection == null) {
- throw new PeerConnectionNotInitialized();
- }
- return peerConnection;
- }
-
private Context requireContext() {
final Context context = this.context;
if (context == null) {
@@ -599,12 +596,18 @@ public class WebRTCWrapper {
return appRTCAudioManager;
}
+ void execute(final Runnable command) {
+ executorService.execute(command);
+ }
+
public interface EventCallback {
void onIceCandidate(IceCandidate iceCandidate);
void onConnectionChange(PeerConnection.PeerConnectionState newState);
void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices);
+
+ void onRenegotiationNeeded();
}
private static abstract class SetSdpObserver implements SdpObserver {
@@ -655,6 +658,12 @@ public class WebRTCWrapper {
}
+ private static class FailureToSetDescriptionException extends IllegalArgumentException {
+ public FailureToSetDescriptionException(String message) {
+ super(message);
+ }
+ }
+
private static class CapturerChoice {
private final CameraVideoCapturer cameraVideoCapturer;
private final CameraEnumerationAndroid.CaptureFormat captureFormat;
@@ -1,6 +1,8 @@
package eu.siacs.conversations.xmpp.jingle.stanzas;
import com.google.common.base.Joiner;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
@@ -8,6 +10,8 @@ import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
@@ -58,6 +62,12 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
return fingerprint == null ? null : Fingerprint.upgrade(fingerprint);
}
+ public Credentials getCredentials() {
+ final String ufrag = this.getAttribute("ufrag");
+ final String password = this.getAttribute("pwd");
+ return new Credentials(ufrag, password);
+ }
+
public List<Candidate> getCandidates() {
final ImmutableList.Builder<Candidate> builder = new ImmutableList.Builder<>();
for (final Element child : getChildren()) {
@@ -74,6 +84,53 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
return transportInfo;
}
+ public IceUdpTransportInfo modifyCredentials(final Credentials credentials, final Setup setup) {
+ final IceUdpTransportInfo transportInfo = new IceUdpTransportInfo();
+ transportInfo.setAttribute("ufrag", credentials.ufrag);
+ transportInfo.setAttribute("pwd", credentials.password);
+ for (final Element child : getChildren()) {
+ if (child.getName().equals("fingerprint") && Namespace.JINGLE_APPS_DTLS.equals(child.getNamespace())) {
+ final Fingerprint fingerprint = new Fingerprint();
+ fingerprint.setAttributes(new Hashtable<>(child.getAttributes()));
+ fingerprint.setContent(child.getContent());
+ fingerprint.setAttribute("setup", setup.toString().toLowerCase(Locale.ROOT));
+ transportInfo.addChild(fingerprint);
+ }
+ }
+ return transportInfo;
+ }
+
+ public static class Credentials {
+ public final String ufrag;
+ public final String password;
+
+ public Credentials(String ufrag, String password) {
+ this.ufrag = ufrag;
+ this.password = password;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Credentials that = (Credentials) o;
+ return Objects.equal(ufrag, that.ufrag) && Objects.equal(password, that.password);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(ufrag, password);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("ufrag", ufrag)
+ .add("password", password)
+ .toString();
+ }
+ }
+
public static class Candidate extends Element {
private Candidate() {
@@ -89,7 +146,7 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
}
// https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-39#section-5.1
- public static Candidate fromSdpAttribute(final String attribute) {
+ public static Candidate fromSdpAttribute(final String attribute, String currentUfrag) {
final String[] pair = attribute.split(":", 2);
if (pair.length == 2 && "candidate".equals(pair[0])) {
final String[] segments = pair[1].split(" ");
@@ -105,6 +162,10 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
for (int i = 6; i < segments.length - 1; i = i + 2) {
additional.put(segments[i], segments[i + 1]);
}
+ final String ufrag = additional.get("ufrag");
+ if (ufrag != null && !ufrag.equals(currentUfrag)) {
+ return null;
+ }
final Candidate candidate = new Candidate();
candidate.setAttribute("component", component);
candidate.setAttribute("foundation", foundation);
@@ -285,8 +346,31 @@ public class IceUdpTransportInfo extends GenericTransportInfo {
return this.getAttribute("hash");
}
- public String getSetup() {
- return this.getAttribute("setup");
+ public Setup getSetup() {
+ final String setup = this.getAttribute("setup");
+ return setup == null ? null : Setup.of(setup);
+ }
+ }
+
+ public enum Setup {
+ ACTPASS, PASSIVE, ACTIVE;
+
+ public static Setup of(String setup) {
+ try {
+ return valueOf(setup.toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ public Setup flip() {
+ if (this == PASSIVE) {
+ return ACTIVE;
+ }
+ if (this == ACTIVE) {
+ return PASSIVE;
+ }
+ throw new IllegalStateException(this.name()+" can not be flipped");
}
}
}
@@ -119,13 +119,13 @@
android:gravity="center"
android:visibility="gone">
- <org.webrtc.SurfaceViewRenderer
+ <eu.siacs.conversations.ui.widget.SurfaceViewRenderer
android:id="@+id/remote_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
- <org.webrtc.SurfaceViewRenderer
+ <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"
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?colorPrimaryDark"
+ android:padding="24dp">
+
+ <ProgressBar
+ android:id="@+id/progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:indeterminate="true"
+ android:indeterminateTint="@color/white" />
+
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/progress"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="16dp"
+ android:gravity="center_horizontal"
+ android:textAppearance="@style/TextAppearance.Conversations.Body2"
+ android:textColor="@color/white87"
+ android:visibility="invisible" />
+
+ </RelativeLayout>
+</layout>
@@ -1,368 +1,385 @@
<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/dialpad_holder"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:focusableInTouchMode="true"
- android:paddingTop="@dimen/medium_margin"
- tools:ignore="HardcodedText">
-
- <RelativeLayout
- android:id="@+id/dialpad_1_holder"
- android:tag="1"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginStart="@dimen/activity_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toBottomOf="@+id/dialpad_2_holder"
- app:layout_constraintEnd_toStartOf="@+id/dialpad_2_holder"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/dialpad_2_holder"
- android:focusable="true" >
-
- <TextView
- android:id="@+id/dialpad_1"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:text="1" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_2_holder"
- android:tag="2"
- android:layout_width="0dp"
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <data>
+ <variable name="dialpadView" type="eu.siacs.conversations.ui.widget.DialpadView"/>
+ </data>
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/dialpad_holder"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/medium_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toTopOf="@+id/dialpad_5_holder"
- app:layout_constraintEnd_toStartOf="@+id/dialpad_3_holder"
- app:layout_constraintStart_toEndOf="@+id/dialpad_1_holder">
-
- <TextView
- android:id="@+id/dialpad_2"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="2" />
-
- <TextView
- android:id="@+id/dialpad_2_letters"
- style="@style/DialpadLetterStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/dialpad_2"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="@dimen/medium_margin"
- android:gravity="center_horizontal"
- android:text="ABC" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_3_holder"
- android:tag="3"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginEnd="@dimen/activity_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toBottomOf="@+id/dialpad_2_holder"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@+id/dialpad_2_holder"
- app:layout_constraintTop_toTopOf="@+id/dialpad_2_holder">
-
- <TextView
- android:id="@+id/dialpad_3"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:focusableInTouchMode="true"
+ android:paddingTop="@dimen/medium_margin"
+ tools:ignore="HardcodedText">
+
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_1_holder"
+ android:tag="1"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="@dimen/activity_margin"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toBottomOf="@+id/dialpad_2_holder"
+ app:layout_constraintEnd_toStartOf="@+id/dialpad_2_holder"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/dialpad_2_holder"
+ android:focusable="true" >
+
+ <TextView
+ android:id="@+id/dialpad_1"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:text="1" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_2_holder"
+ android:tag="2"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="3" />
-
- <TextView
- android:id="@+id/dialpad_3_letters"
- style="@style/DialpadLetterStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/dialpad_3"
- android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/medium_margin"
- android:gravity="center_horizontal"
- android:text="DEF" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_4_holder"
- android:tag="4"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginStart="@dimen/activity_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toBottomOf="@+id/dialpad_5_holder"
- app:layout_constraintEnd_toStartOf="@+id/dialpad_5_holder"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/dialpad_5_holder">
-
- <TextView
- android:id="@+id/dialpad_4"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="4" />
-
- <TextView
- android:id="@+id/dialpad_4_letters"
- style="@style/DialpadLetterStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/dialpad_4"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="@dimen/medium_margin"
- android:gravity="center_horizontal"
- android:text="GHI" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_5_holder"
- android:tag="5"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/medium_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toTopOf="@+id/dialpad_8_holder"
- app:layout_constraintEnd_toStartOf="@+id/dialpad_6_holder"
- app:layout_constraintStart_toEndOf="@+id/dialpad_4_holder">
-
- <TextView
- android:id="@+id/dialpad_5"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="5" />
-
- <TextView
- android:id="@+id/dialpad_5_letters"
- style="@style/DialpadLetterStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/dialpad_5"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="@dimen/medium_margin"
- android:gravity="center_horizontal"
- android:text="JKL" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_6_holder"
- android:tag="6"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginEnd="@dimen/activity_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toBottomOf="@+id/dialpad_5_holder"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@+id/dialpad_5_holder"
- app:layout_constraintTop_toTopOf="@+id/dialpad_5_holder">
-
- <TextView
- android:id="@+id/dialpad_6"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="6" />
-
- <TextView
- android:id="@+id/dialpad_6_letters"
- style="@style/DialpadLetterStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/dialpad_6"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="@dimen/medium_margin"
- android:gravity="center_horizontal"
- android:text="MNO" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_7_holder"
- android:tag="7"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginStart="@dimen/activity_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toBottomOf="@+id/dialpad_8_holder"
- app:layout_constraintEnd_toStartOf="@+id/dialpad_8_holder"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/dialpad_8_holder">
-
- <TextView
- android:id="@+id/dialpad_7"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="7" />
-
- <TextView
- android:id="@+id/dialpad_7_letters"
- style="@style/DialpadLetterStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/dialpad_7"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="@dimen/medium_margin"
- android:gravity="center_horizontal"
- android:text="PQRS" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_8_holder"
- android:tag="8"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/medium_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toTopOf="@+id/dialpad_0_holder"
- app:layout_constraintEnd_toStartOf="@+id/dialpad_9_holder"
- app:layout_constraintStart_toEndOf="@+id/dialpad_7_holder">
-
- <TextView
- android:id="@+id/dialpad_8"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="8" />
-
- <TextView
- android:id="@+id/dialpad_8_letters"
- style="@style/DialpadLetterStyle"
- android:layout_width="wrap_content"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toTopOf="@+id/dialpad_5_holder"
+ app:layout_constraintEnd_toStartOf="@+id/dialpad_3_holder"
+ app:layout_constraintStart_toEndOf="@+id/dialpad_1_holder">
+
+ <TextView
+ android:id="@+id/dialpad_2"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:text="2" />
+
+ <TextView
+ android:id="@+id/dialpad_2_letters"
+ style="@style/DialpadLetterStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/dialpad_2"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/medium_margin"
+ android:gravity="center_horizontal"
+ android:text="ABC" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_3_holder"
+ android:tag="3"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginEnd="@dimen/activity_margin"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toBottomOf="@+id/dialpad_2_holder"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/dialpad_2_holder"
+ app:layout_constraintTop_toTopOf="@+id/dialpad_2_holder">
+
+ <TextView
+ android:id="@+id/dialpad_3"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:text="3" />
+
+ <TextView
+ android:id="@+id/dialpad_3_letters"
+ style="@style/DialpadLetterStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/dialpad_3"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/medium_margin"
+ android:gravity="center_horizontal"
+ android:text="DEF" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_4_holder"
+ android:tag="4"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="@dimen/activity_margin"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toBottomOf="@+id/dialpad_5_holder"
+ app:layout_constraintEnd_toStartOf="@+id/dialpad_5_holder"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/dialpad_5_holder">
+
+ <TextView
+ android:id="@+id/dialpad_4"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:text="4" />
+
+ <TextView
+ android:id="@+id/dialpad_4_letters"
+ style="@style/DialpadLetterStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/dialpad_4"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/medium_margin"
+ android:gravity="center_horizontal"
+ android:text="GHI" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_5_holder"
+ android:tag="5"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_below="@+id/dialpad_8"
- android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/medium_margin"
- android:gravity="center_horizontal"
- android:text="TUV" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_9_holder"
- android:tag="9"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginEnd="@dimen/activity_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toBottomOf="@+id/dialpad_8_holder"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@+id/dialpad_8_holder"
- app:layout_constraintTop_toTopOf="@+id/dialpad_8_holder">
-
- <TextView
- android:id="@+id/dialpad_9"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="9" />
-
- <TextView
- android:id="@+id/dialpad_9_letters"
- style="@style/DialpadLetterStyle"
- android:layout_width="wrap_content"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toTopOf="@+id/dialpad_8_holder"
+ app:layout_constraintEnd_toStartOf="@+id/dialpad_6_holder"
+ app:layout_constraintStart_toEndOf="@+id/dialpad_4_holder">
+
+ <TextView
+ android:id="@+id/dialpad_5"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:text="5" />
+
+ <TextView
+ android:id="@+id/dialpad_5_letters"
+ style="@style/DialpadLetterStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/dialpad_5"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/medium_margin"
+ android:gravity="center_horizontal"
+ android:text="JKL" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_6_holder"
+ android:tag="6"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginEnd="@dimen/activity_margin"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toBottomOf="@+id/dialpad_5_holder"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/dialpad_5_holder"
+ app:layout_constraintTop_toTopOf="@+id/dialpad_5_holder">
+
+ <TextView
+ android:id="@+id/dialpad_6"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:text="6" />
+
+ <TextView
+ android:id="@+id/dialpad_6_letters"
+ style="@style/DialpadLetterStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/dialpad_6"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/medium_margin"
+ android:gravity="center_horizontal"
+ android:text="MNO" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_7_holder"
+ android:tag="7"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="@dimen/activity_margin"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toBottomOf="@+id/dialpad_8_holder"
+ app:layout_constraintEnd_toStartOf="@+id/dialpad_8_holder"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/dialpad_8_holder">
+
+ <TextView
+ android:id="@+id/dialpad_7"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:text="7" />
+
+ <TextView
+ android:id="@+id/dialpad_7_letters"
+ style="@style/DialpadLetterStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/dialpad_7"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/medium_margin"
+ android:gravity="center_horizontal"
+ android:text="PQRS" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_8_holder"
+ android:tag="8"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_below="@+id/dialpad_9"
- android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/medium_margin"
- android:gravity="center_horizontal"
- android:text="WXYZ" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_asterisk_holder"
- android:tag="*"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginStart="@dimen/activity_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toBottomOf="@+id/dialpad_0_holder"
- app:layout_constraintEnd_toStartOf="@+id/dialpad_0_holder"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/dialpad_0_holder">
-
- <TextView
- android:id="@+id/dialpad_asterisk"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:text="*" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_0_holder"
- android:tag="0"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/dialpad_pound_holder"
- app:layout_constraintStart_toEndOf="@+id/dialpad_asterisk_holder">
-
- <TextView
- android:id="@+id/dialpad_0"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="0" />
-
- <TextView
- android:id="@+id/dialpad_plus"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignTop="@+id/dialpad_0"
- android:layout_alignBottom="@+id/dialpad_0"
- android:layout_centerHorizontal="true"
- android:layout_toEndOf="@+id/dialpad_0"
- android:gravity="center"
- android:paddingStart="@dimen/small_margin"
- android:paddingTop="@dimen/small_margin"
- android:text="+"
- android:textSize="@dimen/actionbar_text_size" />
-
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/dialpad_pound_holder"
- android:tag="#"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginEnd="@dimen/activity_margin"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:layout_constraintBottom_toBottomOf="@+id/dialpad_0_holder"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@+id/dialpad_0_holder"
- app:layout_constraintTop_toTopOf="@+id/dialpad_0_holder">
-
- <TextView
- android:id="@+id/dialpad_pound"
- style="@style/DialpadNumberStyle"
- android:layout_width="wrap_content"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toTopOf="@+id/dialpad_0_holder"
+ app:layout_constraintEnd_toStartOf="@+id/dialpad_9_holder"
+ app:layout_constraintStart_toEndOf="@+id/dialpad_7_holder">
+
+ <TextView
+ android:id="@+id/dialpad_8"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:text="8" />
+
+ <TextView
+ android:id="@+id/dialpad_8_letters"
+ style="@style/DialpadLetterStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/dialpad_8"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/medium_margin"
+ android:gravity="center_horizontal"
+ android:text="TUV" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_9_holder"
+ android:tag="9"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginEnd="@dimen/activity_margin"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toBottomOf="@+id/dialpad_8_holder"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/dialpad_8_holder"
+ app:layout_constraintTop_toTopOf="@+id/dialpad_8_holder">
+
+ <TextView
+ android:id="@+id/dialpad_9"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:text="9" />
+
+ <TextView
+ android:id="@+id/dialpad_9_letters"
+ style="@style/DialpadLetterStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/dialpad_9"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/medium_margin"
+ android:gravity="center_horizontal"
+ android:text="WXYZ" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_asterisk_holder"
+ android:tag="*"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="@dimen/activity_margin"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toBottomOf="@+id/dialpad_0_holder"
+ app:layout_constraintEnd_toStartOf="@+id/dialpad_0_holder"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/dialpad_0_holder">
+
+ <TextView
+ android:id="@+id/dialpad_asterisk"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:text="*" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_0_holder"
+ android:tag="0"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
+ android:layout_marginBottom="@dimen/activity_margin"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/dialpad_pound_holder"
+ app:layout_constraintStart_toEndOf="@+id/dialpad_asterisk_holder">
+
+ <TextView
+ android:id="@+id/dialpad_0"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:text="0" />
+
+ <TextView
+ android:id="@+id/dialpad_plus"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/dialpad_0"
+ android:layout_alignBottom="@+id/dialpad_0"
+ android:layout_centerHorizontal="true"
+ android:layout_toEndOf="@+id/dialpad_0"
+ android:gravity="center"
+ android:paddingStart="@dimen/small_margin"
+ android:paddingTop="@dimen/small_margin"
+ android:text="+"
+ android:textSize="@dimen/actionbar_text_size" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:onClick="@{dialpadView::onClick}"
+ android:id="@+id/dialpad_pound_holder"
+ android:tag="#"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
android:layout_marginEnd="@dimen/activity_margin"
- android:text="#" />
- </RelativeLayout>
-</androidx.constraintlayout.widget.ConstraintLayout>
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:layout_constraintBottom_toBottomOf="@+id/dialpad_0_holder"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/dialpad_0_holder"
+ app:layout_constraintTop_toTopOf="@+id/dialpad_0_holder">
+
+ <TextView
+ android:id="@+id/dialpad_pound"
+ style="@style/DialpadNumberStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_marginEnd="@dimen/activity_margin"
+ android:text="#" />
+ </RelativeLayout>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>
@@ -18,6 +18,4 @@
android:icon="?attr/icon_goto_chat"
android:title="@string/switch_to_conversation"
app:showAsAction="ifRoom" />
-
-
-</menu>
+</menu>
@@ -2,22 +2,22 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/discover_public_channels"
- android:title="@string/discover_channels"
- android:icon="@drawable/ic_search_white_24dp"/>
+ android:icon="@drawable/ic_search_white_24dp"
+ android:title="@string/discover_channels" />
<item
android:id="@+id/join_public_channel"
- android:title="@string/join_public_channel"
- android:icon="@drawable/ic_input_white_24dp"/>
+ android:icon="@drawable/ic_input_white_24dp"
+ android:title="@string/join_public_channel" />
<item
android:id="@+id/create_public_channel"
- android:title="@string/create_public_channel"
- android:icon="@drawable/ic_public_white_24dp"/>
+ android:icon="@drawable/ic_public_white_24dp"
+ android:title="@string/create_public_channel" />
<item
android:id="@+id/create_private_group_chat"
- android:title="@string/create_private_group_chat"
- android:icon="@drawable/ic_group_white_24dp"/>
+ android:icon="@drawable/ic_group_white_24dp"
+ android:title="@string/create_private_group_chat" />
<item
android:id="@+id/create_contact"
- android:title="@string/add_contact"
- android:icon="@drawable/ic_person_white_48dp"/>
+ android:icon="@drawable/ic_person_white_48dp"
+ android:title="@string/add_contact" />
</menu>
@@ -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>
@@ -29,10 +29,17 @@
<string name="just_now">току-що</string>
<string name="minute_ago">преди 1 минута</string>
<string name="minutes_ago">преди %d минути</string>
+ <plurals name="x_unread_conversations">
+ <item quantity="one">%d непрочетен разговор</item>
+
+
+ <item quantity="other">%d непрочетени разговора</item>
+
+ </plurals>
<string name="sending">изпращане…</string>
<string name="message_decrypting">Дешифроване на съобщението. Моля, изчакайте…</string>
<string name="pgp_message">Съобщение, шифр. чрез OpenPGP</string>
- <string name="nick_in_use">Псевдонимът вече се използва</string>
+ <string name="nick_in_use">Псевдонимът е зает</string>
<string name="invalid_muc_nick">Неправилен псевдоним</string>
<string name="admin">Администратор</string>
<string name="owner">Собственик</string>
@@ -52,6 +59,7 @@
<string name="share_with">Споделяне с…</string>
<string name="start_conversation">Започване на разговор</string>
<string name="invite_contact">Поканете контакт</string>
+ <string name="invite">Поканете</string>
<string name="contacts">Контакти</string>
<string name="contact">Контакт</string>
<string name="cancel">Отказ</string>
@@ -63,14 +71,23 @@
<string name="unblock">Деблокиране</string>
<string name="save">Запазване</string>
<string name="ok">Добре</string>
+ <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="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="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>
+ <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>
@@ -81,16 +98,20 @@
<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">Неуспешно дешифроване. Възможно е да нямате правилния частен ключ.</string>
<string name="openkeychain_required">OpenKeychain</string>
+ <string name="openkeychain_required_long"><![CDATA[%1$s използва <b>OpenKeychain</b>, за да шифрова и дешифрова съобщенията и да управлява публичните Ви ключове. <br><br> OpenKeychain е под лиценза GPLv3+ и може се свали от F-Droid и Google Play.<br><br><small>(Моля, рестартирайте %1$s след това.)</small>]]></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 ключ</string>
+ <string name="contact_has_no_pgp_key">Съобщението Ви не може да се шифрова, тъй като контактът Ви не е обявил публичния си ключ.\n\n<small>Моля, помолете го да инсталира и настрои OpenPGP.</small></string>
<string name="no_pgp_keys">Не са открити OpenPGP ключове</string>
+ <string name="contacts_have_no_pgp_keys">Съобщението Ви не може да се шифрова, тъй като контактите Ви не са обявили публичните си ключове.\n\n<small>Моля, помолете ги да инсталират и настроят OpenPGP.</small></string>
<string name="pref_general">Общи</string>
<string name="pref_accept_files">Приемане на файлове</string>
<string name="pref_accept_files_summary">Автоматично приемане на файлове с размер, по-малък от…</string>
@@ -101,12 +122,20 @@
<string name="pref_led">Известие чрез светодиода</string>
<string name="pref_led_summary">Мигане на индикаторния светодиод при получаване на ново съобщение</string>
<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>
<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_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>
<string name="accept">Приемане</string>
<string name="error">Възникна грешка</string>
@@ -119,8 +148,11 @@
<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">Обща В/И грешка. Може би нямате достатъчно свободно място?</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>
@@ -132,7 +164,10 @@
<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_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>
@@ -147,13 +182,17 @@
<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="openpgp_has_been_published">Публичният OpenPGP ключ е публикуван.</string>
<string name="mgmt_account_enable">Активиране на профила</string>
- <string name="mgmt_account_are_you_sure">Сигурни ли сте?</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">XMPP адрес</string>
+ <string name="block_jabber_id">Блокиране на XMPP адрес</string>
<string name="account_settings_example_jabber_id">username@example.com</string>
<string name="password">Парола</string>
- <string name="invalid_jid">Това не е валиден XMPP адрес</string>
+ <string name="invalid_jid">Това не е правилен XMPP адрес</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>
@@ -162,6 +201,7 @@
<string name="server_info_blocking">XEP-0191: Команда за блокиране</string>
<string name="server_info_roster_version">XEP-0237: Поддържане на версия на списъка</string>
<string name="server_info_stream_management">XEP-0198: Управление на потоците</string>
+ <string name="server_info_external_service_discovery">XEP-0215: Откриване на външни услуги</string>
<string name="server_info_pep">XEP-0163: PEP (Аватари / OMEMO)</string>
<string name="server_info_http_upload">XEP-0363: Качване на файл през HTTP</string>
<string name="server_info_push">XEP-0357: Изпращане</string>
@@ -169,17 +209,25 @@
<string name="server_info_unavailable">не е налично</string>
<string name="missing_public_keys">Липсват обявления за публичен ключ</string>
<string name="last_seen_now">последно видян току-що</string>
+ <string name="last_seen_min">последно видян преди една минута</string>
<string name="last_seen_mins">последно видян преди %d минути</string>
+ <string name="last_seen_hour">последно видян преди час</string>
<string name="last_seen_hours">последно видян преди %d часа</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>
<string name="openpgp_key_id">Ид. на OpenPGP ключа</string>
<string name="omemo_fingerprint">Отпечатък OMEMO</string>
<string name="omemo_fingerprint_x509">Отпечатък v\\OMEMO</string>
+ <string name="omemo_fingerprint_selected_message">Отпечатък OMEMO (източник на съобщението)</string>
+ <string name="omemo_fingerprint_x509_selected_message">v\\Отпечатък OMEMO (източник на съобщението)</string>
<string name="other_devices">Други устройства</string>
<string name="trust_omemo_fingerprints">Доверяване на отпечатъци OMEMO</string>
<string name="fetching_keys">Изтегляне на ключове…</string>
<string name="done">Готово</string>
<string name="decrypt">Дешифроване</string>
+ <string name="bookmarks">Отметки</string>
<string name="search">Търсене</string>
<string name="enter_contact">Въведете контакт</string>
<string name="delete_contact">Изтриване на контакта</string>
@@ -190,11 +238,16 @@
<string name="select">Избиране</string>
<string name="contact_already_exists">Контактът вече съществува</string>
<string name="join">Присъединяване</string>
+ <string name="channel_full_jid_example">канал@беседа.сървър.com/псевдоним</string>
+ <string name="channel_bare_jid_example">канал@беседа.сървър.com</string>
<string name="save_as_bookmark">Запазване като отметка</string>
<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>
<string name="topic">Тема</string>
<string name="joining_conference">Присъединяване в групов разговор…</string>
@@ -203,25 +256,32 @@
<string name="add_back">Добавяне обратно</string>
<string name="contact_has_read_up_to_this_point">%s е прочел до тук</string>
<string name="contacts_have_read_up_to_this_point">%s човека са прочели до тук</string>
+ <string name="contacts_and_n_more_have_read_up_to_this_point">%1$s и още %2$d човека са прочели до тук</string>
<string name="everyone_has_read_up_to_this_point">Всички са прочели до тук</string>
<string name="publish">Публикуване</string>
+ <string name="touch_to_choose_picture">Докоснете аватара, за да изберете снимка от галерията</string>
<string name="publishing">Публикуване…</string>
<string name="error_publish_avatar_server_reject">Сървърът отказа Вашето публикуване</string>
- <string name="error_saving_avatar">Неуспешно запазване на аватара на диска</string>
+ <string name="error_publish_avatar_converting">Снимката Ви не може да бъде преобразувана</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="connect">Свързване</string>
<string name="account_already_exists">Този профил вече съществува</string>
<string name="next">Следващо</string>
+ <string name="server_info_session_established">Установена сесия</string>
<string name="skip">Пропускане</string>
<string name="disable_notifications">Изключване на известията</string>
<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>
@@ -235,6 +295,8 @@
<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_members_only">Този групов разговор е само за членове</string>
<string name="conference_resource_constraint">Ограничение на ресурса</string>
@@ -253,6 +315,7 @@
<string name="send_again">Повторно изпращане</string>
<string name="file_url">Адрес на файла</string>
<string name="url_copied_to_clipboard">Копиране на адреса</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">Сканиране на 2-измерен баркод</string>
@@ -263,6 +326,14 @@
<string name="try_again">Повторен опит</string>
<string name="pref_keep_foreground_service">Услуга на преден план</string>
<string name="pref_keep_foreground_service_summary">Предотвратява прекъсването на връзката Ви от операционната система</string>
+ <string name="pref_create_backup">Създаване на резервно копие</string>
+ <string name="pref_create_backup_summary">Резервните копия ще се пазят в %s</string>
+ <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="notification_restored_backup_title">Възстановяването от резервно копие е завършено</string>
+ <string name="notification_restored_backup_subtitle">Не забравяйте да включите профила.</string>
<string name="choose_file">Изберете файл</string>
<string name="receiving_x_file">Получаване на %1$s (%2$d%% завършено)</string>
<string name="download_x_file">Сваляне на %s</string>
@@ -270,25 +341,37 @@
<string name="file">файл</string>
<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="cancel_transmission">Отказ на прехвърлянето</string>
+ <string name="file_transmission_failed">файлът не може да бъде споделен</string>
+ <string name="file_transmission_cancelled">изпращането на файла е отменено</string>
+ <string name="file_deleted">Файлът е изтрит</string>
+ <string name="no_application_found_to_open_file">Няма намерено приложение за отваряне на файла</string>
+ <string name="no_application_found_to_open_link">Няма намерено приложение за отваряне на връзката</string>
+ <string name="no_application_found_to_view_contact">Няма намерено приложение за преглед на контакта</string>
<string name="pref_show_dynamic_tags">Динамични етикети</string>
<string name="pref_show_dynamic_tags_summary">Показване на етикети, предназначени само за четене под контактите</string>
<string name="enable_notifications">Включване на известията</string>
<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="regenerate_omemo_key">Повторно създаване на ключа OMEMO</string>
<string name="clear_other_devices">Премахване на устройствата</string>
+ <string name="clear_other_devices_desc">Наистина ли искате да премахнете всички останали устройства от обявлението OMEMO? Следващия път, когато устройствата Ви се свържат, те ще обявят себе си отново, но може да не получат съобщенията, изпратени междувременно.</string>
+ <string name="error_no_keys_to_trust_server_error">Няма ключове, които могат да бъдат използвани за този контакт.\nОт сървъра не могат да бъдат изтеглени нови ключове. Възможно е да има проблем със сървъра на контакта Ви.</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="password_changed">Паролата е променена!</string>
- <string name="could_not_change_password">Неуспешна промяна на паролата</string>
+ <string name="could_not_change_password">Паролата не може да бъде променена</string>
<string name="change_password">Промяна на паролата</string>
<string name="current_password">Текуща парола</string>
<string name="new_password">Нова парола</string>
+ <string name="password_should_not_be_empty">Паролата не може да е празна</string>
<string name="enable_all_accounts">Активиране на всички профили</string>
<string name="disable_all_accounts">Деактивиране на всички профили</string>
<string name="perform_action_with">Изпълнение на действието с</string>
@@ -297,20 +380,28 @@
<string name="outcast">Отхвърлен</string>
<string name="member">Член</string>
<string name="advanced_mode">Разширен режим</string>
- <string name="grant_membership">Дай членски привилегии</string>
- <string name="remove_membership">Премахни членски привилегии</string>
- <string name="grant_admin_privileges">Даване на администраторски права</string>
+ <string name="grant_membership">Даване на правомощия на член</string>
+ <string name="remove_membership">Премахване на правомощията на член</string>
+ <string name="grant_admin_privileges">Даване на правомощия на администратор</string>
<string name="remove_admin_privileges">Отмяна на администраторските права</string>
- <string name="grant_owner_privileges">Дай права на собственик</string>
+ <string name="grant_owner_privileges">Даване на правомощия на собственик</string>
+ <string name="remove_owner_privileges">Премахване на правомощията на собственик</string>
<string name="remove_from_room">Премахване от груповия разговор</string>
- <string name="could_not_change_affiliation">Неуспешна промяна на принадлежността на %s</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="could_not_change_role">Неуспешна промяна на ролята на %s</string>
+ <string name="could_not_change_role">Ролята на %s не може да бъде променена</string>
+ <string name="conference_options">Настройка на частен групов разговор</string>
+ <string name="channel_options">Настройка на публичен групов разговор</string>
<string name="members_only">Частно, само за членове</string>
+ <string name="non_anonymous">Нека XMPP адресите бъдат видими за всички</string>
+ <string name="moderated">Нека каналът да се модерира</string>
<string name="you_are_not_participating">Вие не участвате</string>
<string name="modified_conference_options">Настройките на груповия разговор бяха променени!</string>
- <string name="could_not_modify_conference_options">Неуспешна промяна на настройките на груповия разговор</string>
+ <string name="could_not_modify_conference_options">Настройките на груповия разговор не могат да бъдат променени</string>
<string name="never">Никога</string>
<string name="until_further_notice">До отмяна</string>
<string name="snooze">Отлагане</string>
@@ -318,11 +409,13 @@
<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_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">Приложение за Андроид</string>
<string name="vcard">Контакт</string>
@@ -338,8 +431,11 @@
<string name="pref_chat_states_summary">Така контактите Ви ще разбират, когато им пишете съобщения</string>
<string name="send_location">Изпращане на местоположението</string>
<string name="show_location">Показване на местоположението</string>
+ <string name="no_application_found_to_display_location">Няма намерено приложение за показване на местоположението</string>
<string name="location">Местоположение</string>
<string name="title_undo_swipe_out_conversation">Conversation се затвори</string>
+ <string name="title_undo_swipe_out_group_chat">Напуснахте частния групов разговор</string>
+ <string name="title_undo_swipe_out_channel">Напуснахте публичния канал</string>
<string name="pref_dont_trust_system_cas_title">Да не се вярва на системните сертификати</string>
<string name="pref_dont_trust_system_cas_summary">Всички сертификати трябва да бъдат одобрени на ръка</string>
<string name="pref_remove_trusted_certificates_title">Премахване на сертификатите</string>
@@ -352,12 +448,15 @@
<item quantity="one">%d сертификат е изтрит</item>
<item quantity="other">%d сертификата са изтрити</item>
</plurals>
+ <string name="pref_quick_action_summary">Замяна на бутона „Изпращане“ с бързо действие</string>
<string name="pref_quick_action">Бързо действие</string>
<string name="none">Нищо</string>
<string name="recently_used">Използвани наскоро</string>
<string name="choose_quick_action">Изберете бързо действие</string>
<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="username">Потребителско име</string>
<string name="username_hint">Потребителско име</string>
<string name="invalid_username">Това не е правилно потребителско име</string>
@@ -367,16 +466,28 @@
<string name="download_failed_could_not_write_file">Неуспешно сваляне: Файлът не може да бъде записан</string>
<string name="account_status_tor_unavailable">Мрежата на Тор е недостъпна</string>
<string name="account_status_bind_failure">Грешка при свързване</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_treat_vibrate_as_silent">Тих режим при режим на вибриране</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="captcha_required">Проверката е задължителна</string>
<string name="captcha_hint">Въведете текста от горното изображение</string>
+ <string name="certificate_chain_is_not_trusted">Недоверен верижен сертификат</string>
+ <string name="jid_does_not_match_certificate">XMPP адресът не съответства на сертификата</string>
<string name="action_renew_certificate">Подновяване на сертификата</string>
<string name="error_fetching_omemo_key">Грешка при получаването на ключа за OMEMO!</string>
<string name="verified_omemo_key_with_certificate">Ключът за OMEMO беше потвърден със сертификат!</string>
@@ -386,6 +497,7 @@
<string name="pref_use_tor_summary">Всички връзки да минават през мрежата на Тор. Изисква Орбот</string>
<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>
@@ -394,23 +506,41 @@
<item quantity="other">%d съобщения</item>
</plurals>
<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_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>
<string name="sync_with_contacts_quicksy"><![CDATA[Quicksy се нуждае от достъп до телефонните номера на контактите Ви, за да прави предложения относно контакти, които вероятно вече са регистрирани в системата на Quicksy.<br><br>Ние няма да пазим копия на тези телефонни номера.\n\nЗа повече информация, прочетете <a href="https://quicksy.im/#privacy">декларацията ни за поверителност</a>.<br><br>Сега ще Ви помолим да дадете достъп до контактите си.]]></string>
<string name="notify_on_all_messages">Известяване за всички съобщения</string>
<string name="notify_only_when_highlighted">Известяване само при споменаване</string>
<string name="notify_never">Известията са изключени</string>
<string name="notify_paused">Известията са спрени временно</string>
<string name="pref_picture_compression">Компресия на изображенията</string>
+ <string name="pref_picture_compression_summary">Съвет: използвайте „Изберете файл“ вместо „Изберете снимка“, за да изпращате снимките некомпресирани, независимо от тази настройка.</string>
<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_dialog">Устройството Ви прилага сериозни оптимизации за използването на батерията върху %1$s, които може да доведат до забавени известия и дори пропуснати съобщения.\nСега ще бъдете помолен(а) да ги изключите.</string>
<string name="disable">Изключване</string>
<string name="selection_too_large">Избраната област е твърде голяма</string>
<string name="no_accounts">(Няма активирани профили)</string>
<string name="this_field_is_required">Това поле е задължително</string>
<string name="correct_message">Поправяне на съобщението</string>
<string name="send_corrected_message">Изпращане на поправеното съобщение</string>
+ <string name="no_keys_just_confirm">Вече сте потвърдили доверието си в този човек, чрез защитена проверка на отпечатъка му. Ако изберете „Готово“, ще потвърдите само това, че %s е част от този групов разговор.</string>
<string name="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="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 има ръководство за създаване на профил.\nИзбирайки conversations.im за доставчик, Вие ще можете да общувате и с потребители на други доставчици, като им предоставите своя пълен адрес за XMPP.</string>
+ <string name="your_full_jid_will_be">Пълният Ви XMPP адрес ще бъде: %s</string>
<string name="create_account">Създаване на профил</string>
<string name="use_own_provider">Използване на собствен доставчик</string>
<string name="pick_your_username">Изберете потребителското си име</string>
@@ -433,11 +563,17 @@
<string name="gp_short">Кратко</string>
<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_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_use_green_background">Зелен фон</string>
<string name="pref_use_green_background_summary">Получените съобщения ще бъдат на зелен фон</string>
+ <string name="unable_to_connect_to_keychain">Свързването с OpenKeychain е невъзможно</string>
<string name="this_device_is_no_longer_in_use">Това устройство вече не се използва</string>
<string name="type_pc">Компютър</string>
<string name="type_phone">Мобилен телефон</string>
@@ -445,20 +581,29 @@
<string name="type_web">Браузър</string>
<string name="type_console">Конзола</string>
<string name="payment_required">Изисква се плащане</string>
+ <string name="missing_internet_permission">Дайте разрешение за достъп до Интернет</string>
<string name="me">Аз</string>
<string name="contact_asks_for_presence_subscription">Контакт моли за абонамент за присъствието</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>
+ <string name="unable_to_update_account">Профилът не може да бъде обновен</string>
+ <string name="report_jid_as_spammer">Докладване този XMPP адрес за спам.</string>
<string name="pref_delete_omemo_identities">Изтриване на идентификаторите OMEMO</string>
+ <string name="pref_delete_omemo_identities_summary">Пресъздайте своите ключове OMEMO. Всички Ваши контакти ще трябва да Ви потвърдят отново. Използвайте това само в краен случай.</string>
<string name="delete_selected_keys">Изтриване на избраните ключове.</string>
<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="data_saver_enabled">Пестенето на данни е включено</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="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>
@@ -466,8 +611,11 @@
<string name="share_as_uri">Споделяне като адрес на XMPP</string>
<string name="share_as_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">Грешен 2-измерен баркод</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>
@@ -477,6 +625,7 @@
<string name="show_inactive_devices">Показване на неактивните</string>
<string name="hide_inactive_devices">Скриване на неактивните</string>
<string name="distrust_omemo_key">Сваляне на доверието</string>
+ <string name="distrust_omemo_key_text">Наистина ли искате да заличите потвърждението на това устройство?\nТова устройство и съобщенията от него ще бъдат отбелязани като „недоверени“.</string>
<plurals name="seconds">
<item quantity="one">%d секунда</item>
<item quantity="other">%d секунди</item>
@@ -509,6 +658,7 @@
<string name="corresponding_conversations_closed">Съответстващите разговори са затворени.</string>
<string name="contact_blocked_past_tense">Контактът е блокиран.</string>
<string name="pref_notifications_from_strangers">Известия от непознати</string>
+ <string name="pref_notifications_from_strangers_summary">Известяване за съобщения и обаждания от непознати.</string>
<string name="received_message_from_stranger">Получено е съобщение от непознат</string>
<string name="block_stranger">Блокиране на непознатия</string>
<string name="block_entire_domain">Блокиране на целия домейн</string>
@@ -518,11 +668,14 @@
<string name="sasl_downgrade">Механизмът на SASL е понижен</string>
<string name="account_status_regis_web">Сървърът изисква регистриране чрез уеб сайт</string>
<string name="open_website">Отваряне на уеб сайта</string>
+ <string name="application_found_to_open_website">Няма намерено приложение за отваряне на уеб сайта</string>
<string name="pref_headsup_notifications">Изскачащи известия</string>
+ <string name="pref_headsup_notifications_summary">Показване на изскачащи известия</string>
<string name="today">Днес</string>
<string name="yesterday">Вчера</string>
<string name="pref_validate_hostname">Проверка на името на сървъра чрез DNSSEC</string>
<string name="pref_validate_hostname_summary">Сървърните сертификати, които съдържат проверено име на сървъра, се смятат за потвърдени</string>
+ <string name="certificate_does_not_contain_jid">Сертификатът не съдържа XMPP адрес</string>
<string name="server_info_partial">частично</string>
<string name="attach_record_video">Запис на видео</string>
<string name="copy_to_clipboard">Копиране в буфера</string>
@@ -544,6 +697,9 @@
<string name="edit_status_message_title">Редактиране на съобщението за състоянието</string>
<string name="edit_status_message">Редактиране на съобщението за състоянието</string>
<string name="disable_encryption">Изключване на шифроването</string>
+ <string name="error_trustkey_general">%1$s не може да изпраща шифровани съобщения до %2$s. Възможно е Вашият контакт да използва остарял сървър или клиент, който не може да работи с OMEMO.</string>
+ <string name="error_trustkey_device_list">Неуспешно получаване на списъка с устройства</string>
+ <string name="error_trustkey_bundle">Неуспешно получаване на ключове за шифроване</string>
<string name="error_trustkey_hint_mutual">Съвет: В някои случаи това може да се оправи, ако се добавите един друг в списъците си с контакти.</string>
<string name="disable_encryption_message">Наистина ли искате да изключите шифроването чрез OMEMO за този разговор?\nТова ще позволи на администратора Ви да чете съобщенията Ви, но пък най-вероятно е единственият начин за общуване с хората, използващи стари клиенти.</string>
<string name="disable_now">Изключване сега</string>
@@ -572,13 +728,16 @@
<string name="title_activity_share_location">Споделяне на местоположението</string>
<string name="title_activity_show_location">Показване на местоположението</string>
<string name="share">Споделяне</string>
+ <string name="unable_to_start_recording">Записът не може да започне</string>
<string name="please_wait">Моля, изчакайте…</string>
+ <string name="no_microphone_permission">Дайте на %1$s разрешение за достъп до микрофона</string>
<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="copy_link">Копиране на уеб адрес</string>
+ <string name="copy_jabber_id">Копиране на XMPP адрес</string>
<string name="p1_s3_filetransfer">Споделяне на файлове през HTTP за S3</string>
<string name="pref_start_search">Директно търсене</string>
<string name="pref_start_search_summary">На екрана за „Започване на разговор“ да се отваря клавиатурата и да се поставя курсорът в полето за търсене</string>
@@ -591,17 +750,26 @@
<string name="providing_a_name_is_optional">Въвеждането на име не е задължитално</string>
<string name="create_dialog_group_chat_name">Име на груповия разговор</string>
<string name="conference_destroyed">Този групов разговор е унищожен</string>
+ <string name="unable_to_save_recording">Записът не може да бъде запазен</string>
<string name="foreground_service_channel_name">Услуга на преден план</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>
<string name="notification_group_messages">Съобщения</string>
+ <string name="notification_group_calls">Обаждания</string>
<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>
+ <string name="pref_incoming_call_notification_settings">Настройки на известията за обаждания</string>
<string name="pref_more_notification_settings_summary">Важност, звук, вибрация</string>
<string name="video_compression_channel_name">Компресия на видеото</string>
<string name="view_media">Преглед на медийното съдържание</string>
+ <string name="group_chat_members">Участници</string>
<string name="media_browser">Разглеждане на медийното съдържание</string>
<string name="security_violation_not_attaching_file">Файлът е пропуснат поради нарушение на сигурността.</string>
<string name="pref_video_compression">Качество на видеото</string>
@@ -639,6 +807,9 @@
<string name="pin_expired">Кодът, който Ви изпратихме, е с изтекла давност.</string>
<string name="unknown_api_error_network">Неизвестна мрежова грешка.</string>
<string name="unknown_api_error_response">Непознат отговор от сървъра.</string>
+ <string name="unable_to_connect_to_server">Свързването със сървъра е невъзможно.</string>
+ <string name="unable_to_establish_secure_connection">Установяването на защитена връзка е невъзможно.</string>
+ <string name="unable_to_find_server">Сървърът не може да бъде намерен.</string>
<string name="something_went_wrong_processing_your_request">Нещо се обърка при обработването на заявката Ви.</string>
<string name="invalid_user_input">Неправилно въведени данни</string>
<string name="temporarily_unavailable">Временно недостъпно. Опитайте отново по-късно.</string>
@@ -657,6 +828,7 @@
<string name="install_orbot">Инсталиране на Orbot</string>
<string name="start_orbot">Пускане на Orbot</string>
<string name="no_market_app_installed">Няма инсталирано приложение за инсталиране на приложения.</string>
+ <string name="group_chat_will_make_your_jabber_id_public">Този канал ще направи Вашия XMPP-адрес публичен</string>
<string name="ebook">е-книга</string>
<string name="video_original">Оригинално (некомпресирано)</string>
<string name="open_with">Отваряне с…</string>
@@ -666,9 +838,132 @@
<string name="restore">Възстановяване</string>
<string name="enter_password_to_restore">Въведете паролата си за профила %s, за да направите възстановяване от резервно копие.</string>
<string name="restore_warning">Не използвайте възможността за възстановяване от резервно копие, за да клонирате (да изпълнявате едновременно) инсталацията. Възстановяването от резервно копие е предназначено за мигриране или в случай, че сте загубили устройството си.</string>
+ <string name="unable_to_restore_backup">Не може да се извърши възстановяване от резервно копие.</string>
+ <string name="unable_to_decrypt_backup">Резервното копие не може да бъде дешифрирано. Правилна ли е паролата?</string>
+ <string name="backup_channel_name">Резервни копия и възстановяване</string>
+ <string name="enter_jabber_id">Въведете XMPP адрес</string>
<string name="create_group_chat">Създаване на групов разговор</string>
+ <string name="join_public_channel">Присъединяване към публичен канал</string>
+ <string name="create_private_group_chat">Създаване на частен групов разговор</string>
+ <string name="create_public_channel">Създаване на публичен канал</string>
+ <string name="create_dialog_channel_name">Име на канала</string>
<string name="xmpp_address">XMPP адрес</string>
+ <string name="please_enter_name">Моля, задайте име за канала</string>
+ <string name="please_enter_xmpp_address">Моля, задайте XMPP адрес</string>
+ <string name="this_is_an_xmpp_address">Това е XMPP адрес. Моля, задайте име.</string>
+ <string name="creating_channel">Създаване на публичен канал…</string>
+ <string name="channel_already_exists">Този канал вече съществува</string>
<string name="joined_an_existing_channel">Присъединихте се към съществуващ канал</string>
- <string name="add_existing_account">Добави съществуващ профил</string>
+ <string name="unable_to_set_channel_configuration">Настройката на канала не може да бъде запазена</string>
+ <string name="allow_participants_to_edit_subject">Нека всеки може да редактира темата</string>
+ <string name="allow_participants_to_invite_others">Нека всеки може да кани други хора</string>
+ <string name="anyone_can_edit_subject">Всеки може да редактира темата.</string>
+ <string name="owners_can_edit_subject">Собствениците могат да редактират темата.</string>
+ <string name="admins_can_edit_subject">Администраторите могат да редактират темата.</string>
+ <string name="owners_can_invite_others">Собствениците могат да канят други хора.</string>
+ <string name="anyone_can_invite_others">Всеки може да кани други хора.</string>
+ <string name="jabber_ids_are_visible_to_admins">XMPP адресите са видими за администраторите.</string>
+ <string name="jabber_ids_are_visible_to_anyone">XMPP адресите са видими за всички.</string>
+ <string name="no_users_hint_channel">В този публичен канал няма никакви участници. Поканете контактите си или използвайте бутона за споделяне, за да разпространите XMPP-адреса на канала.</string>
+ <string name="no_users_hint_group_chat">В този частен групов разговор няма никакви участници.</string>
+ <string name="manage_permission">Управление на правомощията</string>
+ <string name="search_participants">Търсене на участници</string>
+ <string name="file_too_large">Файлът е твърде голям</string>
+ <string name="attach">Прикачане</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</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>
+ <string name="add_anway">Добавяне въпреки това</string>
+ <string name="this_looks_like_channel">Това прилича на адрес на канал</string>
+ <string name="share_backup_files">Споделяне на файловете с резервни копия</string>
+ <string name="conversations_backup">Резервно копие от Conversations</string>
+ <string name="event">Събитие</string>
+ <string name="open_backup">Отваряне на резервно копие</string>
+ <string name="not_a_backup_file">Избраният файл не е резервно копие от Conversations</string>
+ <string name="account_already_setup">Този профил вече е настроен</string>
+ <string name="please_enter_password">Въведете паролата за този профил</string>
+ <string name="unable_to_perform_this_action">Това действие не може да бъде извършено</string>
+ <string name="open_join_dialog">Присъединяване към публичен канал…</string>
+ <string name="sharing_application_not_grant_permission">Приложението за споделяне не даде разрешение за достъп до този файл.</string>
+ <string name="group_chats_and_channels"><![CDATA[Групови разговори и канали]]></string>
+ <string name="jabber_network">jabber.network</string>
+ <string name="local_server">Локален сървър</string>
+ <string name="pref_channel_discovery_summary">Повечето потребители трябва да изберат „jabber.network“ за по-добри предложения от цялата публична екосистема на XMPP.</string>
+ <string name="pref_channel_discovery">Метод за откриване на канали</string>
+ <string name="backup">Резервно копие</string>
+ <string name="category_about">Относно</string>
+ <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_connected">Установена връзка</string>
+ <string name="rtp_state_accepting_call">Приемане на обаждане</string>
+ <string name="rtp_state_ending_call">Приключване на обаждане</string>
+ <string name="answer_call">Отговор</string>
+ <string name="dismiss_call">Отхвърляне</string>
+ <string name="rtp_state_finding_device">Откриване на устройства</string>
+ <string name="rtp_state_ringing">Позвъняване</string>
<string name="rtp_state_declined_or_busy">Зает</string>
- </resources>
+ <string name="rtp_state_connectivity_error">Свързването с разговора е невъзможно</string>
+ <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>
+ <string name="disable_tor_to_make_call">Изключете Tor, за да правите обаждания</string>
+ <string name="incoming_call">Входящо обаждане</string>
+ <string name="incoming_call_duration">Входящо обаждане · %s</string>
+ <string name="missed_call_timestamp">Пропуснато обаждане · %s</string>
+ <string name="outgoing_call">Изходящо обаждане</string>
+ <string name="outgoing_call_duration">Изходящо обаждане · %s</string>
+ <string name="missed_call">Пропуснато обаждане</string>
+ <string name="audio_call">Гласово обаждане</string>
+ <string name="video_call">Видео обаждане</string>
+ <string name="help">Помо</string>
+ <string name="switch_to_conversation">Превключване към разговор</string>
+ <string name="microphone_unavailable">Микрофонът не е наличен</string>
+ <string name="only_one_call_at_a_time">Не може да има повече от едно обаждане едновременно.</string>
+ <string name="return_to_ongoing_call">Обратно към текущия разговор</string>
+ <string name="add_to_favorites">Закачане горе</string>
+ <string name="remove_from_favorites">Откачане от горе</string>
+ <string name="could_not_correct_message">Съобщението не може да бъде поправено</string>
+ <string name="search_all_conversations">Всички разговори</string>
+ <string name="search_this_conversation">Този разговор</string>
+ <string name="your_avatar">Вашият аватар</string>
+ <string name="avatar_for_x">Аватар за %s</string>
+ <string name="encrypted_with_omemo">Шифровано с OMEMO</string>
+ <string name="encrypted_with_openpgp">Шифровано с OpenPGP</string>
+ <string name="not_encrypted">Нешифровано</string>
+ <string name="exit">Изход</string>
+ <string name="record_voice_mail">Запис на гласова поща</string>
+ <string name="play_audio">Възпроизвеждане на звука</string>
+ <string name="pause_audio">Пауза на звука</string>
+ <string name="add_contact_or_create_or_join_group_chat">Добавете контакт, създайте или се присъединете към групов разговор, или разгледайте каналите</string>
+ <plurals name="view_users">
+ <item quantity="one">Преглед на %1$d член</item>
+ <item quantity="other">Преглед на %1$d членове</item>
+ </plurals>
+ <plurals name="some_messages_could_not_be_delivered">
+ <item quantity="one">Едно съобщение не може да бъде доставено</item>
+ <item quantity="other">Някои съобщения не могат да бъдат доставени</item>
+ </plurals>
+ <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>
+ <string name="plain_text_document">Обикновен текстов документ</string>
+
+</resources>
@@ -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>
@@ -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>
@@ -132,6 +132,8 @@
<string name="pref_never_send_crash_summary">Ved at indsende \"stack traces\" hjælper du udviklingen</string>
<string name="pref_confirm_messages">Bekræft beskeder</string>
<string name="pref_confirm_messages_summary">Lad dine kontakter vide når du har modtaget og læst deres beskeder</string>
+ <string name="pref_prevent_screenshots">Forbyd skærmbillede</string>
+ <string name="pref_prevent_screenshots_summary">Skjul app indhold i app-skifteren og bloker skærmbilleder</string>
<string name="pref_ui_options">UI</string>
<string name="openpgp_error">OpenKeychain producerede en fejl</string>
<string name="bad_key_for_encryption">Dårlig nøgle til kryptering</string>
@@ -150,6 +152,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 +167,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>
@@ -412,6 +416,7 @@
<string name="audio">lyd</string>
<string name="video">video</string>
<string name="image">billede</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>
@@ -910,6 +915,7 @@
<string name="rtp_state_connectivity_lost_error">Forbindelsen tabt</string>
<string name="rtp_state_retracted">Tilbagetrukket opkald</string>
<string name="rtp_state_application_failure">App fejl</string>
+ <string name="rtp_state_security_error">Bekræftelsesproblem</string>
<string name="hang_up">Læg på</string>
<string name="ongoing_call">Udgående opkald</string>
<string name="ongoing_video_call">Igangværende videoopkald</string>
@@ -960,4 +966,7 @@
<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>
- </resources>
+ <string name="unable_to_enable_video">Kunne ikke aktivere video.</string>
+ <string name="plain_text_document">Ren tekstdokument</string>
+
+</resources>
@@ -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,25 +252,25 @@
<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>
<string name="error_saving_avatar">Avatar kann nicht gespeichert werden</string>
- <string name="or_long_press_for_default">(Oder klicke lange, um Standard wiederherzustellen)</string>
+ <string name="or_long_press_for_default">(Oder klicke lange, um den Standard wiederherzustellen)</string>
<string name="error_publish_avatar_no_server_support">Dein Server unterstützt die Veröffentlichung von Avataren nicht</string>
<string name="private_message">private Nachricht:</string>
<string name="private_message_to">an %s</string>
<string name="send_private_message_to">Private Nachricht an %s senden</string>
<string name="connect">Verbinden</string>
- <string name="account_already_exists">Das Konto existiert bereits</string>
+ <string name="account_already_exists">Dieses Konto existiert bereits</string>
<string name="next">Weiter</string>
<string name="server_info_session_established">Sitzung wiederhergestellt</string>
<string name="skip">Überspringen</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>
@@ -324,7 +326,7 @@
<string name="confirm">Bestätigen</string>
<string name="try_again">Erneut versuchen</string>
<string name="pref_keep_foreground_service">Vordergrunddienst</string>
- <string name="pref_keep_foreground_service_summary">Verhindert, dass Android Conversations beendet und die Verbindung unterbricht</string>
+ <string name="pref_keep_foreground_service_summary">Verhindert, dass das Betriebssystem deine Verbindung unterbricht</string>
<string name="pref_create_backup">Sicherung erstellen</string>
<string name="pref_create_backup_summary">Sicherungsdateien werden gespeichert in %s</string>
<string name="notification_create_backup_title">Erstelle Sicherungsdateien</string>
@@ -362,7 +364,7 @@
<string name="error_no_keys_to_trust_server_error">Für diesen Kontakt sind keine nutzbaren Schlüssel verfügbar.\nEs konnten keine neuen Schlüssel vom Server abgerufen werden. Gibt es vielleicht ein Problem mit dem Server deines Kontaktes?</string>
<string name="error_no_keys_to_trust_presence">Für diesen Kontakt sind keine benutzbaren Schlüssel verfügbar.\nStelle sicher, dass ihre beide gegenseitig den Online-Status aktiviert habt.</string>
<string name="error_trustkeys_title">Etwas ist schief gelaufen</string>
- <string name="fetching_history_from_server">Lade Chatverlauf…</string>
+ <string name="fetching_history_from_server">Lade Chatverlauf vom Server</string>
<string name="no_more_history_on_server">Keine weiteren Nachrichten vorhanden</string>
<string name="updating">Aktualisieren…</string>
<string name="password_changed">Passwort geändert!</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>
@@ -554,7 +557,7 @@
<string name="device_does_not_support_battery_op">Dein Gerät unterstützt kein Ausschalten der Akkuoptimierung</string>
<string name="registration_please_wait">Registrierung fehlgeschlagen: Bitte später versuchen</string>
<string name="registration_password_too_weak">Registrierung fehlgeschlagen: Passwort zu schwach</string>
- <string name="choose_participants">Mitglieder wählen</string>
+ <string name="choose_participants">Teilnehmer wählen</string>
<string name="creating_conference">Erstelle Gruppenchat…</string>
<string name="invite_again">Erneut einladen</string>
<string name="gp_disable">Deaktivieren</string>
@@ -603,7 +606,7 @@
<string name="all_omemo_keys_have_been_verified">Du hast alle in deinem Besitz befindlichen OMEMO-Schlüssel überprüft</string>
<string name="barcode_does_not_contain_fingerprints_for_this_conversation">Der Barcode enthält keine Fingerabdrücke für diese Unterhaltung.</string>
<string name="verified_fingerprints">Überprüfte Fingerabdrücke</string>
- <string name="use_camera_icon_to_scan_barcode">Nutze Kamera, um Barcodes deiner Kontakte zu scannen</string>
+ <string name="use_camera_icon_to_scan_barcode">Nutze die Kamera, um Barcodes deiner Kontakte zu scannen</string>
<string name="please_wait_for_keys_to_be_fetched">Bitte warten, bis die Schlüssel abgerufen werden</string>
<string name="share_as_barcode">Als Barcode teilen</string>
<string name="share_as_uri">Als XMPP URI teilen</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>
@@ -893,7 +896,7 @@
<string name="local_server">Lokaler Server</string>
<string name="pref_channel_discovery_summary">Die meisten Benutzer sollten hier ‘jabber.network’ auswählen, um bessere Vorschläge aus dem gesamten, öffentlichen XMPP-Ökosystem zu bekommen.</string>
<string name="pref_channel_discovery">Channelsuchmethode</string>
- <string name="backup">Sicherungskopie</string>
+ <string name="backup">Sicherung</string>
<string name="category_about">Über</string>
<string name="please_enable_an_account">Bitte aktiviere ein Konto</string>
<string name="make_call">Anrufen</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>
@@ -961,6 +965,8 @@
<string name="unable_to_parse_invite">Einladung kann nicht gelesen werden</string>
<string name="server_does_not_support_easy_onboarding_invites">Server unterstützt keine Generierung von Einladungen</string>
<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="backup_started_message">Die Sicherung wurde gestartet. Du bekommst eine Benachrichtigung, sobald sie fertig ist.</string>
<string name="unable_to_enable_video">Video kann nicht aktiviert werden.</string>
+ <string name="plain_text_document">Textdokument</string>
+
</resources>
@@ -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">Δημιουργία & Επαναφορά αντιγράφων ασφαλείας</string>
+ <string name="backup_channel_name">Δημιουργία & Επαναφορά</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>
@@ -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>
@@ -131,6 +132,8 @@
<string name="pref_never_send_crash_summary">Al enviar las trazas de error estás ayudando en el desarrollo</string>
<string name="pref_confirm_messages">Confirmar mensajes</string>
<string name="pref_confirm_messages_summary">Permitir a tus contactos saber cuando has recibido y leído sus mensajes</string>
+ <string name="pref_prevent_screenshots">Impedir capturas de pantalla</string>
+ <string name="pref_prevent_screenshots_summary">Ocultar el contenido de la aplicación en el selector de aplicaciones y bloquear las capturas de pantalla</string>
<string name="pref_ui_options">Pantalla</string>
<string name="openpgp_error">OpenKeychain causó un error.</string>
<string name="bad_key_for_encryption">Clave errónea para el cifrado.</string>
@@ -149,6 +152,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 +167,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 +220,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>
@@ -409,6 +416,7 @@
<string name="audio">audio</string>
<string name="video">vídeo</string>
<string name="image">imagen</string>
+ <string name="vector_graphic">gráfico de vectores</string>
<string name="pdf_document">documento PDF</string>
<string name="apk">Android App</string>
<string name="vcard">Contacto</string>
@@ -462,6 +470,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>
@@ -905,6 +915,7 @@
<string name="rtp_state_connectivity_lost_error">Conexión perdida</string>
<string name="rtp_state_retracted">Llamada rechazada</string>
<string name="rtp_state_application_failure">Fallo en la aplicación</string>
+ <string name="rtp_state_security_error">Problema de verificación</string>
<string name="hang_up">Colgar</string>
<string name="ongoing_call">Llamada saliente</string>
<string name="ongoing_video_call">Video llamada saliente</string>
@@ -954,4 +965,8 @@
<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>
- </resources>
+ <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>
+ <string name="plain_text_document">Documento de texto plano</string>
+
+</resources>
@@ -0,0 +1,918 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="action_settings">Asetukset</string>
+ <string name="action_add">Uusi keskustelu</string>
+ <string name="action_accounts">Hallitse tilejä</string>
+ <string name="action_account">Hallinnoi tiliä</string>
+ <string name="action_end_conversation">Päätä keskustelu</string>
+ <string name="action_contact_details">Yhteystiedot</string>
+ <string name="action_muc_details">Ryhmäkeskustelun tiedot</string>
+ <string name="channel_details">Kanavan tiedot</string>
+ <string name="action_add_account">Lisää tili</string>
+ <string name="action_edit_contact">Muokkaa nimeä</string>
+ <string name="action_add_phone_book">Lisää yhteystietoihin</string>
+ <string name="action_delete_contact">Poista yhteystietolistasta</string>
+ <string name="action_block_contact">Estä yhteystieto</string>
+ <string name="action_unblock_contact">Peru yhteystiedon esto</string>
+ <string name="action_block_domain">Estä verkkotunnus</string>
+ <string name="action_unblock_domain">Peru verkkotunnuksen esto</string>
+ <string name="action_block_participant">Estä osallistuja</string>
+ <string name="action_unblock_participant">Peru osallistujan esto</string>
+ <string name="title_activity_manage_accounts">Hallitse tilejä</string>
+ <string name="title_activity_settings">Asetukset</string>
+ <string name="title_activity_start_conversation">Aloita keskustelu</string>
+ <string name="title_activity_choose_contact">Valitse yhteystieto</string>
+ <string name="title_activity_choose_contacts">Valitse yhteystiedot</string>
+ <string name="title_activity_share_via_account">Jaa tilillä</string>
+ <string name="title_activity_block_list">Estolista</string>
+ <string name="just_now">äskettäin</string>
+ <string name="minute_ago">minuutti sitten</string>
+ <string name="minutes_ago">%d minuuttia sitten</string>
+ <plurals name="x_unread_conversations">
+ <item quantity="one">%d lukematon keskustelu</item>
+
+
+ <item quantity="other">%d lukematonta keskustelua</item>
+
+ </plurals>
+ <string name="sending">lähettää...</string>
+ <string name="message_decrypting">Puretaan viestin salausta. Odota hetki...</string>
+ <string name="pgp_message">OpenPGP-salattu viesti</string>
+ <string name="nick_in_use">Nimimerkki on jo käytössä</string>
+ <string name="invalid_muc_nick">Nimimerkki on virheellinen</string>
+ <string name="admin">Ylläpitäjä</string>
+ <string name="owner">Omistaja</string>
+ <string name="moderator">Moderaattori</string>
+ <string name="participant">Osallistuja</string>
+ <string name="visitor">Vierailija</string>
+ <string name="remove_contact_text">Poistetaanko %s yhteystiedoistasi? Keskustelujasi hänen kanssaan ei poisteta.</string>
+ <string name="block_contact_text">Estetäänkö %s lähettämästä viestejä sinulle?</string>
+ <string name="unblock_contact_text">Perutaanko %s:n esto lähettää viestejä sinulle?</string>
+ <string name="block_domain_text">Estetäänkö kaikki yhteydet verkkotunnuksesta %s?</string>
+ <string name="unblock_domain_text">Perutaanko kaikkien verkkotunnuksen %s käyttäjien esto?</string>
+ <string name="contact_blocked">Yhteystieto estetty</string>
+ <string name="blocked">Estetty</string>
+ <string name="remove_bookmark_text">Poistetaanko %s kirjanmerkeistä? Mitään keskustelujasi sen kanssa ei poisteta.</string>
+ <string name="register_account">Rekisteröi uusi tili palvelimella</string>
+ <string name="change_password_on_server">Vaihda salasanaa palvelimelle</string>
+ <string name="start_conversation">Aloita keskustelu</string>
+ <string name="invite_contact">Kutsu yhteystieto</string>
+ <string name="invite">Kutsu</string>
+ <string name="contacts">Yhteystiedot</string>
+ <string name="contact">Yhteystieto</string>
+ <string name="cancel">Peruuta</string>
+ <string name="set">Aseta</string>
+ <string name="add">Lisää</string>
+ <string name="edit">Muokkaa</string>
+ <string name="delete">Poista</string>
+ <string name="block">Estä</string>
+ <string name="unblock">Peruuta esto</string>
+ <string name="save">Tallenna</string>
+ <string name="ok">OK</string>
+ <string name="crash_report_title">%1$s kaatui</string>
+ <string name="crash_report_message">Virheenkorjaustietojen lähettäminen XMPP-tililläsi helpottaa %1$s:n kehitystyötä.</string>
+ <string name="send_now">Lähetä nyt</string>
+ <string name="send_never">Älä koskaan pyydä lähettämään</string>
+ <string name="problem_connecting_to_account">Tiliin ei saatu yhteyttä</string>
+ <string name="problem_connecting_to_accounts">Useisiin tileihin ei saatu yhteyttä</string>
+ <string name="touch_to_fix">Napauta hallinnoidaksesi tilejä</string>
+ <string name="attach_file">Liitä tiedosto</string>
+ <string name="not_in_roster">Lisätäänkö tämä puuttuva yhteystieto listaasi?</string>
+ <string name="add_contact">Lisää yhteystieto</string>
+ <string name="send_failed">toimitus epäonnistui</string>
+ <string name="preparing_image">Valmistaudutaan lähettämään kuva</string>
+ <string name="preparing_images">Valmistaudutaan lähettämään kuvat</string>
+ <string name="sharing_files_please_wait">Jaetaan tiedostoja. Odota hetki...</string>
+ <string name="action_clear_history">Pyyhi historia</string>
+ <string name="clear_conversation_history">Pyyhi keskusteluhistoria</string>
+ <string name="clear_histor_msg">Poistetaanko kaikki keskustelun viestit?\n\n<b>Varoitus:</b> Muilla laitteilla tai palvelimilla säilytettyjä kopioita ei poisteta.</string>
+ <string name="delete_file_dialog">Poista tiedosto</string>
+ <string name="delete_file_dialog_msg">Haluatko varmasti poistaa tämän tiedoston?\n\n<b>Varoitus:</b> Muilla laitteilla tai palvelimilla olevia kopioita ei poisteta.</string>
+ <string name="also_end_conversation">Päätä keskustelu myös</string>
+ <string name="choose_presence">Valitse laite</string>
+ <string name="send_unencrypted_message">Lähetä salaamaton viesti</string>
+ <string name="send_message">Lähetä viesti</string>
+ <string name="send_message_to_x">Lähetä viesti henkilölle %s</string>
+ <string name="send_omemo_message">Lähetä OMEMO-salattu viesti</string>
+ <string name="send_omemo_x509_message">Lähetä v\\OMEMO-salattu viesti</string>
+ <string name="send_pgp_message">Lähetä OpenPGP-salattu viesti</string>
+ <string name="your_nick_has_been_changed">Uusi nimimerkki on jo varattu</string>
+ <string name="send_unencrypted">Lähetä salaamaton</string>
+ <string name="decryption_failed">Salauksen purku epäonnistui. Sinulle ei varmaan ole oikeaa salaista avainta.</string>
+ <string name="openkeychain_required">OpenKeychain</string>
+ <string name="openkeychain_required_long"><![CDATA[%1$s käyttää <b>OpenKeychain</b>ia viestien salaamiseeen ja salauksen purkamiseen, sekä julkisten avaintesi hallinointiin.<br><br>Se on GPLv3+-lisensoitu ja saatavilla F-Droidista sekä Google Playsta.<br><br><small>(Käynnistä %1$s uudelleen asennettuasi sovelluksen.)</small>]]></string>
+ <string name="restart">Käynnistä uudelleen</string>
+ <string name="install">Asenna</string>
+ <string name="openkeychain_not_installed">Asenna OpenKeychain</string>
+ <string name="offering">tarjotaan...</string>
+ <string name="waiting">odotetaan...</string>
+ <string name="no_pgp_key">OpenPGP-avainta ei löydy</string>
+ <string name="contact_has_no_pgp_key">Viestin salaaminen ei onnistu koska vastaanottaja ei mainosta julkista avaintaan.\n\n<small>Pyydä kontaktiasi ottamaan OpenPGP käyttöön.</small></string>
+ <string name="no_pgp_keys">OpenPGP-avaimia ei löydy</string>
+ <string name="contacts_have_no_pgp_keys">Viestin salaaminen ei onnistu koska kontaktisi eivät mainosta julkisia avaimiaan.\n\n<small>Pyydä heitä ottamaan OpenPGP käyttöön.</small></string>
+ <string name="pref_general">Yleinen</string>
+ <string name="pref_accept_files">Lataa tiedostot</string>
+ <string name="pref_accept_files_summary">Lataa automaattisesti tiedostot jotka ovat pienempiä kuin...</string>
+ <string name="pref_attachments">Liitteet</string>
+ <string name="pref_notification_settings">Ilmoitus</string>
+ <string name="pref_vibrate">Värinä</string>
+ <string name="pref_vibrate_summary">Värise uuden viestin saapuessa</string>
+ <string name="pref_led">LED-ilmoitus</string>
+ <string name="pref_led_summary">Vilkuta ilmoitusvaloa vastaanotettuasi uuden viestin</string>
+ <string name="pref_ringtone">Soittoääni</string>
+ <string name="pref_notification_sound">Ilmoitusääni</string>
+ <string name="pref_notification_sound_summary">Uusien viestien ilmoitusääni</string>
+ <string name="pref_call_ringtone_summary">Soittoääni saamillesi puheluille</string>
+ <string name="pref_notification_grace_period">Rauhanaika</string>
+ <string name="pref_notification_grace_period_summary">Kuinka pitkäksi aikaa ilmoitukset hiljennetään kun jollain toisella laitteillasi tehdään jotain.</string>
+ <string name="pref_advanced_options">Edistyneet</string>
+ <string name="pref_never_send_crash">Älä koskaan lähetä vikailmoituksia</string>
+ <string name="pref_never_send_crash_summary">Vikailmoituksia lähettämällä autat kehitystyötä</string>
+ <string name="pref_confirm_messages">Lukukuittaus</string>
+ <string name="pref_confirm_messages_summary">Ilmoita lähettäjälle kun olet vastaanottanut ja lukenut viestin</string>
+ <string name="pref_prevent_screenshots">Estä kuvankaappaukset</string>
+ <string name="pref_prevent_screenshots_summary">Piilota sovelluksen sisältö sovellusvaihtajassa ja estä ruutukaappaukset</string>
+ <string name="pref_ui_options">Käyttöliittymä</string>
+ <string name="openpgp_error">OpenKeychain-virhe</string>
+ <string name="bad_key_for_encryption">Avain ei kelpaa salaamiseen.</string>
+ <string name="accept">Hyväksy</string>
+ <string name="error">Virhe tapahtui</string>
+ <string name="recording_error">Virhe</string>
+ <string name="your_account">Tilisi</string>
+ <string name="send_presence_updates">Lähetä tilapäivityksiä</string>
+ <string name="receive_presence_updates"> Vastaanota tilapäivityksiä</string>
+ <string name="ask_for_presence_updates">Pyydä tilapäivityksiä</string>
+ <string name="attach_choose_picture">Valitse kuva</string>
+ <string name="attach_take_picture">Ota kuva</string>
+ <string name="preemptively_grant">Alustavasti hyväksy liittymispyynnöt</string>
+ <string name="error_not_an_image_file">Valitsemasi tiedosto ei ole kuva</string>
+ <string name="error_compressing_image">Kuvatiedoston pakkaaminen epäonnistui</string>
+ <string name="error_file_not_found">Tiedostoa ei löytynyt</string>
+ <string name="error_io_exception">Yleinen I/O-virhe. Ehkä vapaa tallennustila loppui kesken?</string>
+ <string name="error_security_exception_during_image_copy">Kuvan valitsemiseen käyttämäsi sovellus ei myöntänyt riittävästi oikeuksia kuvan lukemiseen.\n\n<small>Käytä toista tiedostonhallintaohjelmaa kuvan valitsemiseen</small>.</string>
+ <string name="error_security_exception">Jakamiseen käyttämäsi sovellus ei myöntänyt riittävästi oikeuksia.</string>
+ <string name="account_status_unknown">Tuntematon</string>
+ <string name="account_status_disabled">Väliaikaisesti poistettu käytöstä</string>
+ <string name="account_status_online">Paikalla</string>
+ <string name="account_status_connecting">Yhdistäää\u2026</string>
+ <string name="account_status_offline">Poissa</string>
+ <string name="account_status_unauthorized">Ei sallittu</string>
+ <string name="account_status_not_found">Palvelinta ei löydy</string>
+ <string name="account_status_no_internet">Ei yhteyttä</string>
+ <string name="account_status_regis_fail">Rekisteröinti epäonnistui</string>
+ <string name="account_status_regis_conflict">Käyttäjänimi on varattu</string>
+ <string name="account_status_regis_success">Rekisteröinti valmis</string>
+ <string name="account_status_regis_not_sup">Palvelin ei tue rekisteröintiä</string>
+ <string name="account_status_regis_invalid_token">Viallinen rekisteröintitunnus</string>
+ <string name="account_status_tls_error">TLS-kättely epäonnistui</string>
+ <string name="account_status_tls_error_domain">Verkkotunnuksen varmentaminen epäonnistui</string>
+ <string name="account_status_incompatible_server">Yhteensopimaton palvelin</string>
+ <string name="encryption_choice_unencrypted">Salaamaton</string>
+ <string name="encryption_choice_otr">OTR</string>
+ <string name="encryption_choice_pgp">OpenPGP</string>
+ <string name="encryption_choice_omemo">OMEMO</string>
+ <string name="mgmt_account_delete">Poista tili</string>
+ <string name="mgmt_account_disable">Poista käytöstä väliaikaisesti</string>
+ <string name="mgmt_account_publish_avatar">Julkaise proofilikuva</string>
+ <string name="mgmt_account_publish_pgp">Julkaise OpenPGP julkinen avain</string>
+ <string name="unpublish_pgp">Poista OpenPGP julkinen avain</string>
+ <string name="unpublish_pgp_message">Haluatko varmasti poistaa OpenPGP-avaimesi tilamainostuksistasi?\nYhteystietosi eivät voi enää lähettää sinulle OpenPGP-salattuja viestejä.</string>
+ <string name="openpgp_has_been_published">OpenPGP julkinen avain julkaistu.</string>
+ <string name="mgmt_account_enable">Ota tunnus käyttöön</string>
+ <string name="mgmt_account_are_you_sure">Oletko varma?</string>
+ <string name="mgmt_account_delete_confirm_text">Tilin poistaminen pyyhkii koko keskusteluhistoriasi</string>
+ <string name="attach_record_voice">Nauhoita ääntä</string>
+ <string name="account_settings_jabber_id">XMPP-osoite</string>
+ <string name="block_jabber_id">Estä XMPP-osoite</string>
+ <string name="account_settings_example_jabber_id">käyttäjä@esimerkki.fi</string>
+ <string name="password">Salasana</string>
+ <string name="invalid_jid">Tämä ei ole kunnollinen XMPP-osoite</string>
+ <string name="error_out_of_memory">Muisti loppui. Kuva on liian suuri.</string>
+ <string name="add_phone_book_text">Lisätäänkö %s osoitekirjaan?</string>
+ <string name="server_info_show_more">Tietoa palvelimesta</string>
+ <string name="server_info_mam">XEP-0313: MAM</string>
+ <string name="server_info_carbon_messages">XEP-0280: Viestin kopiot</string>
+ <string name="server_info_csi">XEP-0352: Asiakkaan tilan vihjaus</string>
+ <string name="server_info_blocking">XEP-0191: Estokomennot</string>
+ <string name="server_info_roster_version">XEP-0237: Yhteystietolistan versiointi</string>
+ <string name="server_info_stream_management">XEP-0198: Virran hallinta</string>
+ <string name="server_info_external_service_discovery">XEP-0215: Ulkoisten palveluiden löytäminen</string>
+ <string name="server_info_pep">XEP-0163: PEP (Profiilikuvat / OMEMO)</string>
+ <string name="server_info_http_upload">XEP-0363: Tiedoston lähetys HTTP:llä</string>
+ <string name="server_info_push">XEP-0357: Työntö</string>
+ <string name="server_info_available">käytössä</string>
+ <string name="server_info_unavailable">ei käytössä</string>
+ <string name="missing_public_keys">Julkisten avainten mainostus puuttuu</string>
+ <string name="last_seen_now">nähty juuri äsken</string>
+ <string name="last_seen_min">nähty minuutti sitten</string>
+ <string name="last_seen_mins">nähty %d minuuttia sitten</string>
+ <string name="last_seen_hour">nähty tunti sitten</string>
+ <string name="last_seen_hours">nähty %d tuntia sitten</string>
+ <string name="last_seen_day">nähty päivä sitten</string>
+ <string name="last_seen_days">nähty %d päivää sitten</string>
+ <string name="install_openkeychain">Salattu viesti. Asenna OpenKeychain purkaaksesi salauksen.</string>
+ <string name="openpgp_messages_found">Löytyi uusi OpenPGP-salattu viesti</string>
+ <string name="openpgp_key_id">OpenPGP-avaimen tunniste</string>
+ <string name="omemo_fingerprint">OMEMO-sormenjälki</string>
+ <string name="omemo_fingerprint_x509">v\\OMEMO-sormenjälki</string>
+ <string name="omemo_fingerprint_selected_message">OMEMO-sormenjälki (viestin lähettäjä)</string>
+ <string name="omemo_fingerprint_x509_selected_message">v\\OMEMO-sormenjälki (viestin lähettäjä)</string>
+ <string name="other_devices">Muut laitteet</string>
+ <string name="trust_omemo_fingerprints">Luota OMEMO-sormenjälkiin</string>
+ <string name="fetching_keys">Haetaan avaimia...</string>
+ <string name="done">Valmis</string>
+ <string name="decrypt">Pura salaus</string>
+ <string name="bookmarks">Kirjanmerkit</string>
+ <string name="search">Haku</string>
+ <string name="enter_contact">Syötä yhteystieto</string>
+ <string name="delete_contact">Poista yhteystieto</string>
+ <string name="view_contact_details">Näytä yhteystieto</string>
+ <string name="block_contact">Estä yhteystieto</string>
+ <string name="unblock_contact">Peruuta yhteystiedon esto</string>
+ <string name="create">Luo</string>
+ <string name="select">Valitse</string>
+ <string name="contact_already_exists">Yhteystieto on jo olemassa</string>
+ <string name="join">Liity</string>
+ <string name="channel_full_jid_example">kanava@kokous.esimerkki.fi/nimimerkki</string>
+ <string name="channel_bare_jid_example">kanava@kokous.esimerkki.fi</string>
+ <string name="save_as_bookmark">Tallenna kirjanmerkkinä</string>
+ <string name="delete_bookmark">Poista kirjanmerkki</string>
+ <string name="destroy_room">Tuhoa ryhmäkeskustelu</string>
+ <string name="destroy_channel">Tuhoa kanava</string>
+ <string name="destroy_room_dialog">Haluatko varmasti tuhota tämän ryhmäkeskustelun?\n\n<b>Varoitus:</b> Ryhmäkeskustelu poistetaan lopullisesti palvelimelta.</string>
+ <string name="destroy_channel_dialog">Haluatko varmasti tuhota tämän julkisen kanavan?\n\n<b>Varoitus:</b> Kanava poistetaan lopullisesti palvelimelta.</string>
+ <string name="could_not_destroy_room">Ryhmäkeskustelun tuhomainen epäonnistui</string>
+ <string name="could_not_destroy_channel">Kanavan tuhoaminen epäonnistui</string>
+ <string name="action_edit_subject">Muokkaa ryhmäkeskustelun aihetta</string>
+ <string name="topic">Aihe</string>
+ <string name="joining_conference">Liitytään ryhmäkeskusteluun...</string>
+ <string name="leave">Poistu</string>
+ <string name="contact_added_you">Yhteystieto lisätty luetteloon</string>
+ <string name="add_back">Lisää takaisin</string>
+ <string name="contact_has_read_up_to_this_point">%s on lukenut tähän asti</string>
+ <string name="contacts_have_read_up_to_this_point">%s ovat lukeneet tähän asti</string>
+ <string name="contacts_and_n_more_have_read_up_to_this_point">%1$s ja %2$d muuta on lukenut tähän asti</string>
+ <string name="everyone_has_read_up_to_this_point">Kaikki ovat lukeneet tähän asti</string>
+ <string name="publish">Julkaise</string>
+ <string name="touch_to_choose_picture">Napauta profiilikuvaa valitaksesi kuvan galleriasta</string>
+ <string name="publishing">Julkaistaan...</string>
+ <string name="error_publish_avatar_server_reject">Palvelin hylkäsi julkaisusi</string>
+ <string name="error_publish_avatar_converting">Kuvan muuntaminen epäonnistui</string>
+ <string name="error_saving_avatar">Profiilikuvan tallentaminen levylle epäonnistui</string>
+ <string name="or_long_press_for_default">(Tai paina pitkään palauttaaksesi oletuksen)</string>
+ <string name="error_publish_avatar_no_server_support">Palvelin ei tue profiilikuvien julkaisua</string>
+ <string name="private_message">kuiskasi</string>
+ <string name="private_message_to">%s:lle</string>
+ <string name="send_private_message_to">Lähetä yksityisviesti %s:lle</string>
+ <string name="connect">Yhdistä</string>
+ <string name="account_already_exists">Tili on jo olemassa</string>
+ <string name="next">Seuraava</string>
+ <string name="server_info_session_established">Istunto aloitettu</string>
+ <string name="skip">Ohita</string>
+ <string name="disable_notifications">Poista ilmoitukset käytöstä</string>
+ <string name="enable">Ota käyttöön</string>
+ <string name="conference_requires_password">Ryhmäkeskustelu vaatii salasanan</string>
+ <string name="enter_password">Kirjoita salasana</string>
+ <string name="request_presence_updates">Pyydä yhteystietoa ensin lähettämään tilapäivityksiä.\n\n<small>Tätä käytetään sen tunnistamiseen mitä sovellusta tämä käyttää</small>.</string>
+ <string name="request_now">Pyydä nyt</string>
+ <string name="ignore">Ohita</string>
+ <string name="without_mutual_presence_updates"><b>Varoitus:</b> Tämän lähettäminen ilman molemminpuolisia tilapäivityksiä voi aiheuttaa odottamattomia ongelmia.\n\n<small>Mene \"Yhteystiedon tietoihin\" tarkistaaksesi tilapäivitysten tilauksesi.</small></string>
+ <string name="pref_security_settings">Turvallisuus</string>
+ <string name="pref_allow_message_correction">Salli viestien korjaaminen</string>
+ <string name="pref_allow_message_correction_summary">Mahdollistaa muiden muokata sinulle lähettämiään viestejä jälkikäteen</string>
+ <string name="pref_expert_options">Edistyneet asetukset</string>
+ <string name="pref_expert_options_summary">Ole varovainen näiden kanssa</string>
+ <string name="title_activity_about_x">Tietoa %s</string>
+ <string name="title_pref_quiet_hours">Hiljaisuus</string>
+ <string name="title_pref_quiet_hours_start_time">Alku</string>
+ <string name="title_pref_quiet_hours_end_time">Loppu</string>
+ <string name="title_pref_enable_quiet_hours">Ota käyttöön hiljaisuus</string>
+ <string name="pref_quiet_hours_summary">Ilmoitukset vaimennetaan hiljaisuuden aikana</string>
+ <string name="pref_expert_options_other">Muut</string>
+ <string name="pref_autojoin">Synkronoi kirjanmerkkien kanssa</string>
+ <string name="pref_autojoin_summary">Liity ryhmään automaattisesti jos se on kirjanmerkeissäsi</string>
+ <string name="toast_message_omemo_fingerprint">OMEMO-sormenjälki kopioitu leikepöydälle</string>
+ <string name="conference_banned">Sinut on estetty tästä ryhmäkeskustelusta</string>
+ <string name="conference_members_only">Tämä ryhmäkeskustelu on vain jäsenille</string>
+ <string name="conference_resource_constraint">Resurssin rajallisuus</string>
+ <string name="conference_kicked">Sinut on poistettu tästä ryhmästä</string>
+ <string name="conference_shutdown">Ryhmäkeskustelu on suljettu</string>
+ <string name="conference_unknown_error">Et ole enää tässä ryhmäkeskustelussa</string>
+ <string name="using_account">käytetään tiliä %s</string>
+ <string name="hosted_on">palvelimella %s</string>
+ <string name="checking_x">Tarkistetaan %s HTTP-palvelimella</string>
+ <string name="not_connected_try_again">Et ole yhteydessä. Yritä myöhemmin uudelleen</string>
+ <string name="check_x_filesize">Tarkista %s:n koko</string>
+ <string name="check_x_filesize_on_host">Tarkista %1$s:n koko palvelimella %2$s</string>
+ <string name="message_options">Toiminnot</string>
+ <string name="quote">Lainaa</string>
+ <string name="paste_as_quote">Liitä lainauksena</string>
+ <string name="copy_original_url">Kopio alkuperäinen URL</string>
+ <string name="send_again">Lähetä uudestaan</string>
+ <string name="file_url">Tiedoston URL</string>
+ <string name="url_copied_to_clipboard">URL kopioitu leikepöydälle</string>
+ <string name="jabber_id_copied_to_clipboard">XMPP-osoite kopioitu leikepöydälle</string>
+ <string name="error_message_copied_to_clipboard">Vikailmoitus kopioitu leikepöydälle</string>
+ <string name="web_address">web-osoite</string>
+ <string name="scan_qr_code">Lue 2D-viivakoodi</string>
+ <string name="show_qr_code">Näytä 2D-viivakoodi</string>
+ <string name="show_block_list">Näytä estolista</string>
+ <string name="account_details">Tilitiedot</string>
+ <string name="confirm">Vahvista</string>
+ <string name="try_again">Yritä uudelleen</string>
+ <string name="pref_keep_foreground_service">Palvelu etualalla</string>
+ <string name="pref_keep_foreground_service_summary">Estää käyttöjärjestelmää katkaisemasta yhteyttäsi</string>
+ <string name="pref_create_backup">Tee varmuuskopio</string>
+ <string name="pref_create_backup_summary">Varmuuskopioiden säilytyspaikka: %s</string>
+ <string name="notification_create_backup_title">Tehdään varmuuskopiota</string>
+ <string name="notification_backup_created_title">Varmuuskopiosi on luotu</string>
+ <string name="notification_backup_created_subtitle">Varmuuskopiotiedostot tallennettu kansioon %s</string>
+ <string name="restoring_backup">Palautetaan varmuuskopiota</string>
+ <string name="notification_restored_backup_title">Varmuuskopiosi on palautettu</string>
+ <string name="notification_restored_backup_subtitle">Älä unohda ottaa tiliä käyttöön.</string>
+ <string name="choose_file">Valitse tiedosto</string>
+ <string name="receiving_x_file">Vastaanotetaan %1$s (%2$d%% valmis)</string>
+ <string name="download_x_file">Lataa %s</string>
+ <string name="delete_x_file">Poista %s</string>
+ <string name="file">tiedosto</string>
+ <string name="open_x_file">Avaa %s</string>
+ <string name="sending_file">Lähetetään (%1$d%% valmis)</string>
+ <string name="preparing_file">Valmistellaan tiedoston lähettämistä</string>
+ <string name="x_file_offered_for_download">%s tarjottu ladattavaksi</string>
+ <string name="cancel_transmission">Peru siirto</string>
+ <string name="file_transmission_failed">tiedoston jako epäonnistui</string>
+ <string name="file_transmission_cancelled">tiedoston siirto peruttu</string>
+ <string name="file_deleted">Tiedosto poistettu</string>
+ <string name="no_application_found_to_open_file">Tiedoston avaamiseen sopivaa sovellusta ei löytynyt</string>
+ <string name="no_application_found_to_open_link">Linkin avaamiseen sopivaa sovellusta ei löytynyt</string>
+ <string name="no_application_found_to_view_contact">Yhteystiedon näyttämiseen sopivaa sovellusta ei löytynyt</string>
+ <string name="pref_show_dynamic_tags">Dynaamiset tunnisteet</string>
+ <string name="pref_show_dynamic_tags_summary">Näytä tunnisteet yhteystiedon alla (vain luku)</string>
+ <string name="enable_notifications">Näytä ilmoitukset</string>
+ <string name="no_conference_server_found">Ryhmäkeskustelupalvelinta ei löytynyt</string>
+ <string name="conference_creation_failed">Ryhmäkeskustelun luominen epäonnistui</string>
+ <string name="account_image_description">Tilin profiilikuva</string>
+ <string name="copy_omemo_clipboard_description">Kopioi OMEMO-sormenjälki leikepöydälle</string>
+ <string name="regenerate_omemo_key">Uusi OMEMO-avain</string>
+ <string name="clear_other_devices">Poista laitteet</string>
+ <string name="clear_other_devices_desc">Haluatko poistaa kaikki muut laitteet OMEMO-mainoksistasi? Kun laite seuraavan kerran yhdistää, se lisää itsensä, mutta ennen sitä lähetettyjä viestejä et välttämättä saa.</string>
+ <string name="error_trustkeys_title">Jokin meni pieleen</string>
+ <string name="fetching_history_from_server">Haetaan historiaa palvelimelta</string>
+ <string name="no_more_history_on_server">Palvelimella ei ollut enempää historiaa</string>
+ <string name="updating">Päivitetään...</string>
+ <string name="password_changed">Salasana vaihdettu!</string>
+ <string name="could_not_change_password">Salasanan vaihto epäonnistui</string>
+ <string name="change_password">Vaihda salasana</string>
+ <string name="current_password">Nykyinen salasana</string>
+ <string name="new_password">Uusi salasana</string>
+ <string name="password_should_not_be_empty">Salasana ei voi olla tyhjä</string>
+ <string name="enable_all_accounts">Kaikki tilit käyttöön</string>
+ <string name="disable_all_accounts">Kaikki tilit pois käytöstä</string>
+ <string name="no_role">Poissa</string>
+ <string name="outcast">Hylkiö</string>
+ <string name="member">Jäsen</string>
+ <string name="advanced_mode">Edistynyt tila</string>
+ <string name="grant_membership">Myönnä jäsenyys</string>
+ <string name="remove_membership">Peru jäsenyys</string>
+ <string name="grant_admin_privileges">Myönnä ylläpitäjän oikeudet</string>
+ <string name="remove_admin_privileges">Peru ylläpitäjän oikeudet</string>
+ <string name="grant_owner_privileges">Myönnä omistajuus</string>
+ <string name="remove_owner_privileges">Peru omistajuus</string>
+ <string name="remove_from_room">Poista ryhmäkeskustelusta</string>
+ <string name="remove_from_channel">Poista kanavalta</string>
+ <string name="ban_from_conference">Estä ryhmäkeskustelusta</string>
+ <string name="ban_from_channel">Estä kanavalta</string>
+ <string name="removing_from_public_conference">Olet poistamassa %s:n julkiselta kanavalta. Ainoa tapa tehdä se on estää kyseinen käyttäjä ikuisesti. </string>
+ <string name="ban_now">Estä nyt</string>
+ <string name="could_not_change_role">%s:n roolin muuttaminen epäonnistui</string>
+ <string name="conference_options">Yksityisen ryhmäkeskustelun asetukset</string>
+ <string name="channel_options">Julkisen kanavan asetukset</string>
+ <string name="members_only">Yksityinen, vain jäsenille</string>
+ <string name="non_anonymous">Tee XMPP-osoitteista kaikille näkyvät</string>
+ <string name="moderated">Tee kanavasta moderoitu</string>
+ <string name="you_are_not_participating">Et osallistu</string>
+ <string name="modified_conference_options">Ryhmäkeskustelun asetuksia muutettu!</string>
+ <string name="could_not_modify_conference_options">Ryhmäkeskustelun asetuksia ei voitu muuttaa</string>
+ <string name="never">Ei koskaan</string>
+ <string name="until_further_notice">Kunnes toisin mainitaan</string>
+ <string name="snooze">Torkku</string>
+ <string name="reply">Vastaa</string>
+ <string name="mark_as_read">Merkitse luetuksi</string>
+ <string name="pref_input_options">Syöttö</string>
+ <string name="pref_enter_is_send">Enter lähettää</string>
+ <string name="pref_enter_is_send_summary">Käytä Enteriä viestin lähetämiseen. Ctrl+Enter lähettää viestin joka tapauksessa.</string>
+ <string name="pref_display_enter_key">Näytä enter-nappi</string>
+ <string name="pref_display_enter_key_summary">Vaihtaa emojinapin enteriksi</string>
+ <string name="audio">ääni</string>
+ <string name="video">video</string>
+ <string name="image">kuva</string>
+ <string name="vector_graphic">vektorigrafiikka</string>
+ <string name="pdf_document">PDF-asiakirja</string>
+ <string name="apk">Android-sovellus</string>
+ <string name="vcard">Yhteystieto</string>
+ <string name="avatar_has_been_published">Profiilikuva julkaistu!</string>
+ <string name="sending_x_file">Lähetetään %s</string>
+ <string name="offering_x_file">Tarjotaan %s</string>
+ <string name="hide_offline">Piilota poissaolevat</string>
+ <string name="contact_is_typing">%s kirjoittaa...</string>
+ <string name="contact_has_stopped_typing">%s lopetti kirjoittamisen</string>
+ <string name="contacts_are_typing">%s kirjoittavat...</string>
+ <string name="contacts_have_stopped_typing">%s lopettivat kirjoittamisen</string>
+ <string name="pref_chat_states">Kirjoitusilmoitukset</string>
+ <string name="pref_chat_states_summary">Näyttää ytheystiedoillesi kun kirjoitat heille viestiä</string>
+ <string name="send_location">Lähetä sijainti</string>
+ <string name="show_location">Näytä sijainti</string>
+ <string name="no_application_found_to_display_location">Sijainnin näyttämiseen sopivaa sovellusta ei löytynyt</string>
+ <string name="location">Sijainti</string>
+ <string name="title_undo_swipe_out_conversation">Keskustelu suljettu</string>
+ <string name="title_undo_swipe_out_group_chat">Poistuit yksityisestä ryhmäkeskustelusta</string>
+ <string name="title_undo_swipe_out_channel">Poistuit julkisesta kanavasta</string>
+ <string name="pref_dont_trust_system_cas_title">Älä luota varmenteiden myöntäjiin</string>
+ <string name="pref_dont_trust_system_cas_summary">Kaikki varmenteet täytyy hyväksyä käsin</string>
+ <string name="pref_remove_trusted_certificates_title">Poista varmenteet</string>
+ <string name="pref_remove_trusted_certificates_summary">Poista käsin hyväksytyt varmenteet</string>
+ <string name="toast_no_trusted_certs">Ei käsin hyväksyttyjä varmenteita</string>
+ <string name="dialog_manage_certs_title">Poista varmenteet</string>
+ <string name="dialog_manage_certs_negativebutton">Peruuta</string>
+ <plurals name="toast_delete_certificates">
+ <item quantity="one">%d varmenne poistettiin</item>
+ <item quantity="other">%d varmennetta poistettiin</item>
+ </plurals>
+ <string name="pref_quick_action_summary">Korvaa \"Lähetä\"-nappi pikatoiminnolla</string>
+ <string name="pref_quick_action">Pikatoiminto</string>
+ <string name="none">Ei mikään</string>
+ <string name="recently_used">Viimeksi käytetty</string>
+ <string name="choose_quick_action">Valitse pikatoiminto</string>
+ <string name="send_private_message">Lähetä yksityisviesti</string>
+ <string name="user_has_left_conference">%1$s poistui ryhmäkeskustelusta</string>
+ <string name="username">Käyttäjänimi</string>
+ <string name="username_hint">Käyttäjänimi</string>
+ <string name="download_failed_server_not_found">Lataus epäonnistui: Palvelinta ei löydy</string>
+ <string name="download_failed_file_not_found">Lataus epäonnistui: Tiedostoa ei löytynyt</string>
+ <string name="download_failed_could_not_connect">Lataus epöonnistui: Isäntään ei saatu yhteyttä</string>
+ <string name="download_failed_could_not_write_file">Lataus epäonnistui: Tiedoston tallennus epäonnistui</string>
+ <string name="account_status_tor_unavailable">Tor-verkkoa ei saavutettu</string>
+ <string name="account_status_host_unknown">Palvelin ei vastaa tästä verkkotunnuksesta</string>
+ <string name="server_info_broken">Rikki</string>
+ <string name="pref_presence_settings">Saatavuus</string>
+ <string name="pref_away_when_screen_off">Poissa kun laite on lukittu</string>
+ <string name="pref_away_when_screen_off_summary">Näytä minut poissaolevana kun näyttö on lukittu</string>
+ <string name="pref_dnd_on_silent_mode">Kiireinen kun laite on äänetön</string>
+ <string name="pref_dnd_on_silent_mode_summary">Näytä minut kiireisenä kun laite on äänettömänäq</string>
+ <string name="pref_treat_vibrate_as_silent">Kohtele vain värinä -tilaa äänettömän lailla</string>
+ <string name="pref_treat_vibrate_as_dnd_summary">Näytä minut kiireisenä kun laite on vain värinä -tilassa</string>
+ <string name="pref_show_connection_options">Laajemmat yhteysasetukset</string>
+ <string name="pref_show_connection_options_summary">Näytä isäntänimen ja portin valinta tiliä lisätessä</string>
+ <string name="hostname_example">xmpp.esimerkki.fi</string>
+ <string name="action_add_account_with_certificate">Kirjaudu varmenteella</string>
+ <string name="unable_to_parse_certificate">Varmenteen jäsennys epäonnistui</string>
+ <string name="mam_prefs">Arkistointiasetukset</string>
+ <string name="server_side_mam_prefs">Palvelimen arkitsointiasetukset</string>
+ <string name="fetching_mam_prefs">Kysytään arkistointiasetuksia. Odota hetki...</string>
+ <string name="unable_to_fetch_mam_prefs">Arkistointiasetusten haku epäonnistui</string>
+ <string name="captcha_required">CAPTCHA vaaditaan</string>
+ <string name="captcha_hint">Kirjoita ylläolevassa kuvassa näkyvä teksti</string>
+ <string name="certificate_chain_is_not_trusted">Varmenneketju ei ole luotettu</string>
+ <string name="jid_does_not_match_certificate">XMPP-osoite on eri kuin varmenteessa</string>
+ <string name="action_renew_certificate">Uusi varmenne</string>
+ <string name="error_fetching_omemo_key">OMEMO-avaimen lataus epäonnistui!</string>
+ <string name="verified_omemo_key_with_certificate">OMEMO-avain vahvistettu varmenteella!</string>
+ <string name="device_does_not_support_certificates">Laitteesi ei tue käyttäjän varmenteen valitsemista!</string>
+ <string name="pref_connection_options">Yhteys</string>
+ <string name="pref_use_tor">Käytä Tor-verkkoa</string>
+ <string name="pref_use_tor_summary">Tunneloi kaikki yhteydet Tor-verkon kautta. Vaatii Orbot-sovelluksen</string>
+ <string name="account_settings_hostname">Isäntänimi</string>
+ <string name="account_settings_port">Portti</string>
+ <string name="hostname_or_onion">Palvelin- tai .onion-osoite</string>
+ <string name="not_a_valid_port">Porttinumero on virheellinen</string>
+ <string name="not_valid_hostname">Isäntänimi on virheellinen</string>
+ <string name="connected_accounts">%1$d tunnusta %2$d:sta yhdistetty</string>
+ <plurals name="x_messages">
+ <item quantity="one">%d viesti</item>
+ <item quantity="other">%d viestiä</item>
+ </plurals>
+ <string name="load_more_messages">Lataa lisää viestejä</string>
+ <string name="shared_file_with_x">Tiedosto jaettu %s:n kanssa</string>
+ <string name="shared_image_with_x">Kuva jaettu %s:n kanssa</string>
+ <string name="shared_images_with_x">Kuvat jaettu %s:n kanssa</string>
+ <string name="no_storage_permission">Salli %1$s:n käyttää ulkoista tallennustilaa</string>
+ <string name="no_camera_permission">Salli %1$s:n käyttää kameraa </string>
+ <string name="sync_with_contacts">Synkronoi yhteystietojen kanssa</string>
+ <string name="sync_with_contacts_long">%1$s haluaa pääsyn osoitekirjaasi yhdistääkseen sen XMPP-yhteystietojesi kanssa.\nTämä näyttää yhteystietojesi koko nimen ja kuvan.\n\n%1$s pelkästään lukee osoitekirjaasi ja vertailee niitä paikallisesti, lähettämättä mitään palvelimelle.</string>
+ <string name="notify_on_all_messages">Ilmoita kaikista uusista viesteistä</string>
+ <string name="notify_only_when_highlighted">Ilmoita vain kun minut mainitaan</string>
+ <string name="notify_never">Ilmoitukset pois käytöstä</string>
+ <string name="notify_paused">Ilmoitukset keskeytetty</string>
+ <string name="pref_picture_compression">Kuvan pakkaus</string>
+ <string name="pref_picture_compression_summary">Vinkki: Käytä \'Lähetä tiedosto\':a \'Lähetä kuva\':n sijaan lähettääksesi kuvan pakkaamattomana sillä kertaa tästä asetuksesta huolimatta.</string>
+ <string name="always">Aina</string>
+ <string name="large_images_only">Vain isot kuvat</string>
+ <string name="battery_optimizations_enabled">Akun käytön optimointi käytössä</string>
+ <string name="disable">Poista käytöstä</string>
+ <string name="selection_too_large">Valittu alue on liian suuri</string>
+ <string name="no_accounts">(Ei aktivoituja tilejä)</string>
+ <string name="this_field_is_required">Tämä kenttä on pakollinen</string>
+ <string name="correct_message">Korjaa viestiä</string>
+ <string name="send_corrected_message">Lähetä korjattu viesti</string>
+ <string name="no_keys_just_confirm">Olet jo varmistanut tämän henkilön OMEMO-sormenjäljen turvallisesti luottamuksen varmistamikseksi. Hyväksymällä varmistat vain että %s on osa tätä ryhmäkeskustelua.</string>
+ <string name="this_account_is_disabled">Olet poistanut tämän tilin käytöstä</string>
+ <string name="no_application_to_share_uri">URI:n jakamiseen sopivaa sovellusta ei löytynyt</string>
+ <string name="share_uri_with">Jaa URI sovelluksella...</string>
+ <string name="agree_and_continue">Hyväksy ja jatka</string>
+ <string name="your_full_jid_will_be">XMPP-osoitteesi tulee olemaan kokonaisuudessaan: %s</string>
+ <string name="create_account">Luo tunnus</string>
+ <string name="use_own_provider">Käytän itse valitsemaani palveluntarjoajaa</string>
+ <string name="pick_your_username">Valitse käyttäjänimi</string>
+ <string name="pref_manually_change_presence">Hallitse saatavuutta käsin</string>
+ <string name="pref_manually_change_presence_summary">Valitse saatavuutesi itse muokatessasi tilaviestiäsi.</string>
+ <string name="status_message">Tila</string>
+ <string name="presence_chat">Vapaa juttelemaan</string>
+ <string name="presence_online">Paikalla</string>
+ <string name="presence_away">Poissa</string>
+ <string name="presence_xa">Ei saatavilla</string>
+ <string name="presence_dnd">Kiireellinen</string>
+ <string name="secure_password_generated">Turvallinen salasana on luotu</string>
+ <string name="device_does_not_support_battery_op">Laitteesi ei tue akun kulutuksen optimoinnin ohittamista</string>
+ <string name="registration_please_wait">Rekisteröinti epäonnistui: Yritä myöhemmin uudelleen</string>
+ <string name="registration_password_too_weak">Rekisteröinti epäonnistui: Salasana on liian heikko</string>
+ <string name="choose_participants">Valitse osallistujat</string>
+ <string name="creating_conference">Luodaan ryhmää...</string>
+ <string name="invite_again">Kutsu uudestaan</string>
+ <string name="gp_disable">Poista käytöstä</string>
+ <string name="gp_short">Lyhyt</string>
+ <string name="gp_medium">Keskipitkä</string>
+ <string name="gp_long">Pitkä</string>
+ <string name="pref_broadcast_last_activity_summary">Kertoo yhteystiedoillesi milloin käytät Conversationsia</string>
+ <string name="pref_privacy">Yksityisyys</string>
+ <string name="pref_theme_options">Teema</string>
+ <string name="pref_theme_options_summary">Valitse väripaletti</string>
+ <string name="pref_theme_automatic">Automaattinen</string>
+ <string name="pref_theme_light">Vaalea</string>
+ <string name="pref_theme_dark">Tumma</string>
+ <string name="pref_use_green_background">Vihreä tausta</string>
+ <string name="pref_use_green_background_summary">Näytä vastaanotetut viestit vihreällä taustalla</string>
+ <string name="unable_to_connect_to_keychain">OpenKeychainiin ei saatu yhteyttä</string>
+ <string name="this_device_is_no_longer_in_use">Laite ei ole enää käytössä</string>
+ <string name="type_pc">Tietokone</string>
+ <string name="type_phone">Puhelin</string>
+ <string name="type_tablet">Tabletti</string>
+ <string name="type_web">Selain</string>
+ <string name="type_console">Pääte</string>
+ <string name="payment_required">Maksu vaaditaan</string>
+ <string name="missing_internet_permission">Salli internetin käyttö</string>
+ <string name="me">Minä</string>
+ <string name="allow">Salli</string>
+ <string name="no_permission_to_access_x">Käyttöoikeus %s puuttuu</string>
+ <string name="remote_server_not_found">Etäpalvelinta ei löytynyt</string>
+ <string name="remote_server_timeout">Etäpalvelimen aikakatkaisu</string>
+ <string name="unable_to_update_account">Tiliä ei saatu muutettua</string>
+ <string name="report_jid_as_spammer">Ilmoita tämä XMPP-osoite roskapostista.</string>
+ <string name="pref_delete_omemo_identities">Poista OMEMO-identiteetit</string>
+ <string name="pref_delete_omemo_identities_summary">Tee uudet OMEMO-avaimet. Kaikkien yhteystietojesi pitää varmistaa sinut uudestaan. Käytä tätä ainoastaan viimeisenä oljenkortena.</string>
+ <string name="delete_selected_keys">Poista valitut avaimet</string>
+ <string name="error_publish_avatar_offline">Yhteys vaaditaan profiilikuvan julkaisemista varten.</string>
+ <string name="show_error_message">Näytä virheilmoitus</string>
+ <string name="error_message">Virheilmoitus</string>
+ <string name="data_saver_enabled">Datansäästö käytössä</string>
+ <string name="data_saver_enabled_explained">Käyttöjärjestelmäsi estää %1$s:tä käyttämästä nettiä ollessaan taustalla. Vastaanottaaksesi ilmoitukset uusista viesteistä, salli %1$s:n käyttää esteettä verkkoa datansäästön ollessa käytössä. %1$s tekee silti parhaansa käyttääkseen mahdollisimman vähän dataa. </string>
+ <string name="device_does_not_support_data_saver">Laitteesi ei tue datansäästön poistamista käytöstä sovellukselle %1$s.</string>
+ <string name="error_unable_to_create_temporary_file">Väliaikaisen tiedoston luominen epäonnistui</string>
+ <string name="this_device_has_been_verified">Laite on varmennettu</string>
+ <string name="copy_fingerprint">Kopioi sormenjälki</string>
+ <string name="all_omemo_keys_have_been_verified">Olet varmentanut kaikki hallussasi olevat OMEMO-avaimet</string>
+ <string name="barcode_does_not_contain_fingerprints_for_this_conversation">Viivakoodi ei sisällä tähän keskusteluun liittyviä sormenjälkiä.</string>
+ <string name="verified_fingerprints">Varmennetut sormenjäljet</string>
+ <string name="use_camera_icon_to_scan_barcode">Käytä kameraa yhteystietosi viivakoodin lukemiseen</string>
+ <string name="please_wait_for_keys_to_be_fetched">Odota hetki, avaimia ladataan</string>
+ <string name="share_as_barcode">Jaa viivakoodina</string>
+ <string name="share_as_uri">Jaa XMPP-osoitteena</string>
+ <string name="share_as_http">Jaa HTTP-linkkinä</string>
+ <string name="pref_blind_trust_before_verification">Sokea luottamus ennen varmistusta</string>
+ <string name="pref_blind_trust_before_verification_summary">Luota uusiin laitteisiin varmistamattomilta yhteystiedoilta, mutta vaadi varmistettujen yhteystietojen uusien laitteiden manuaalinen hyväksyminen.</string>
+ <string name="blindly_trusted_omemo_keys">OMEMO-avaimiin luotetaan sokeasti, eli ne voivat olla jonkun muun tai joku voi salakuunnella.</string>
+ <string name="not_trusted">Ei luotettu</string>
+ <string name="invalid_barcode">Viallinen 2D-viivakoodi</string>
+ <string name="pref_clean_cache_summary">Tyhjennä välimuisti (kamerasovelluksen käyttämä)</string>
+ <string name="pref_clean_cache">Tyhjennä välimuisti</string>
+ <string name="pref_clean_private_storage">Siivoa yksityinen tallennustila</string>
+ <string name="pref_clean_private_storage_summary">Tyhjennä tallennustila jossa tiedostoja säilytetään (Ne voi ladata uudestaan palvelimelta)</string>
+ <string name="i_followed_this_link_from_a_trusted_source">Seurasin tätä linkkiä luotettavasta lähteestä</string>
+ <string name="verifying_omemo_keys_trusted_source">Olet varmistamassa yhteystiedon %1$s OMEMO-avaimia linkin avaamalla. Tämä on turvallista ainoastaan jos löysit linkin luotettavasta paikasta jossa vain %2$s on sen kyennyt julkaisemaan.</string>
+ <string name="verify_omemo_keys">Varmista OMEMO-avaimet</string>
+ <plurals name="seconds">
+ <item quantity="one">%d sekunti</item>
+ <item quantity="other">%d sekuntia</item>
+ </plurals>
+ <plurals name="minutes">
+ <item quantity="one">%d minuutti</item>
+ <item quantity="other">%d minuuttia</item>
+ </plurals>
+ <plurals name="hours">
+ <item quantity="one">%d tunti</item>
+ <item quantity="other">%d tuntia</item>
+ </plurals>
+ <plurals name="days">
+ <item quantity="one">%d päivä</item>
+ <item quantity="other">%d päivää</item>
+ </plurals>
+ <plurals name="weeks">
+ <item quantity="one">%d viikko</item>
+ <item quantity="other">%d viikkoa</item>
+ </plurals>
+ <plurals name="months">
+ <item quantity="one">%d kuukausi</item>
+ <item quantity="other">%d kuukautta</item>
+ </plurals>
+ <string name="pref_automatically_delete_messages">Viestien automaattinen poisto</string>
+ <string name="pref_automatically_delete_messages_description">Poista tältä laitteelta automaattisesti valittua vanhemmat viestit.</string>
+ <string name="encrypting_message">Salataan viestiä</string>
+ <string name="transcoding_video">Pakataan videota</string>
+ <string name="contact_blocked_past_tense">Yhteystieto estettiin.</string>
+ <string name="pref_notifications_from_strangers">Ilmoitukset tuntemattomila</string>
+ <string name="pref_notifications_from_strangers_summary">Ilmoita tuntemattomien henkilöiden viesteistä ja puheluista.</string>
+ <string name="received_message_from_stranger">Sait viestin tuntemattomalta henkilöltä</string>
+ <string name="block_stranger">Estä tuntematon</string>
+ <string name="block_entire_domain">Estä koko verkkotunnus</string>
+ <string name="online_right_now">paikalla juuri nyt</string>
+ <string name="retry_decryption">Yritä salauksen purkua uudestaan</string>
+ <string name="session_failure">Istuntovirhe</string>
+ <string name="sasl_downgrade">Alennettu SASL-mekanismi</string>
+ <string name="account_status_regis_web">Palvelin vaatii tekemään rekisteröinnin verkkosivulla</string>
+ <string name="open_website">Avaa verkkosivu</string>
+ <string name="application_found_to_open_website">Verkkosivun avaamiseen sopivaa sovellusta ei löytynyt</string>
+ <string name="today">Tänään</string>
+ <string name="yesterday">Eilen</string>
+ <string name="pref_validate_hostname">Varmenna verkkotunnus DNSSEC:llä</string>
+ <string name="pref_validate_hostname_summary">Palvelinvarmenteet jotka sisältävät varmennetun verkkotunnukset hyväksytään</string>
+ <string name="certificate_does_not_contain_jid">Varmenne ei sisällä XMPP-osoitetta</string>
+ <string name="server_info_partial">osittain</string>
+ <string name="attach_record_video">Nauhoita video</string>
+ <string name="copy_to_clipboard">Kopioi leikepöydälle</string>
+ <string name="message_copied_to_clipboard">Viesti kopioitu leikepöydälle</string>
+ <string name="message">Viesti</string>
+ <string name="private_messages_are_disabled">Yksityisviestit on poistettu käytöstä</string>
+ <string name="huawei_protected_apps">Suojatut sovellukset</string>
+ <string name="huawei_protected_apps_summary">Saadaksesi ilmoituksia silloinkin kun näyttö on sammutettu, Conversations pitää lisätä suojattujen sovellusten luetteloon.</string>
+ <string name="mtm_accept_cert">Hyväksytäänkö tuntematon varmenne?</string>
+ <string name="mtm_trust_anchor">Palvelimen varmenne ei ole luotetun myöntäjän allekirjoittama.</string>
+ <string name="mtm_accept_servername">Hyväksytäänkö eriävä palvelimen nimi?</string>
+ <string name="mtm_hostname_mismatch">Palvelin ei voinut tunnistautua olevansa verkkotunnusta \"%s\". Varmenne sisältää vain seuraavat verkkotunnukset:</string>
+ <string name="mtm_connect_anyway">Haluatko yhdistää joka tapauksessa?</string>
+ <string name="mtm_cert_details">Varmenteen tiedot:</string>
+ <string name="once">Kerran</string>
+ <string name="qr_code_scanner_needs_access_to_camera">QR-koodilukija tarvitsee luvan käyttää kameraa</string>
+ <string name="pref_scroll_to_bottom">Vieritä alas asti</string>
+ <string name="pref_scroll_to_bottom_summary">Vieritä alas veistin lähetyksen jälkeen</string>
+ <string name="edit_status_message_title">Vaihda tila</string>
+ <string name="edit_status_message">Muokkaa tilaa</string>
+ <string name="disable_encryption">Poista salaus käytöstä</string>
+ <string name="error_trustkey_general">%1$s ei voi lähettää salattuja viestejä %2$s:lle. Se voi johtua siitä että vastaanottajan palvelin on vanhentunut tai hänen käyttämänsä sovellus ei tue OMEMO:a.</string>
+ <string name="error_trustkey_device_list">Laiteluettelon lataus epäonnistui</string>
+ <string name="error_trustkey_bundle">Salausavainten lataus epäonnistui</string>
+ <string name="error_trustkey_hint_mutual">Vinkki: Jossain tapauksissa tämä ratkeaa kun molemmat osapuolet lisäävät toisena yhteystietoihinsa.</string>
+ <string name="disable_encryption_message">Haluatko varmasti poistaa OMEMO-salauksen käytöstä tässä keskustelussa?\nPalvelimen ylläpitäjä voi tällöin lukea viestinne, mutta se voi olla ainoa mahdollinen tapa keskustella vanhentunutta sovellusta käyttävän henkilön kanssa.</string>
+ <string name="disable_now">Poista käytöstä nyt</string>
+ <string name="draft">Luonnos:</string>
+ <string name="pref_omemo_setting">OMEMO-salaus</string>
+ <string name="pref_omemo_setting_summary_default_on">OMEMO:a ei koskaan käytetä oletuksena uusissa keskusteluissa.</string>
+ <string name="create_shortcut">Luo pikakuvake</string>
+ <string name="pref_font_size">Kirjasinkoko</string>
+ <string name="pref_font_size_summary">Kirjasimen suhteellinen koko sovelluksen sisällä.</string>
+ <string name="default_on">Käytössä oletuksena</string>
+ <string name="default_off">Oletuksena pois käytöstä</string>
+ <string name="small">Pieni</string>
+ <string name="medium">Keksikokoinen</string>
+ <string name="large">Suuri</string>
+ <string name="not_encrypted_for_this_device">Viestiä ei salattu tälle laitteelle</string>
+ <string name="omemo_decryption_failed">OMEMO-salatun viestin purku epäonnistui</string>
+ <string name="undo">peru</string>
+ <string name="location_disabled">Sijainnin jakaminen on pois käytöstä</string>
+ <string name="action_copy_location">Kopioi sijainti</string>
+ <string name="action_share_location">Jaa sijainti</string>
+ <string name="action_directions">Reittiohjeet</string>
+ <string name="title_activity_share_location">Jaa sijainti</string>
+ <string name="title_activity_show_location">Näytä sijainti</string>
+ <string name="share">Jaa</string>
+ <string name="please_wait">Odota hetki...</string>
+ <string name="no_microphone_permission">Salli %1$s:n käyttää mikrofonia</string>
+ <string name="gif">GIF</string>
+ <string name="view_conversation">Näytä keskustelu</string>
+ <string name="pref_use_share_location_plugin">Sijainnin jako -lisäosa</string>
+ <string name="pref_use_share_location_plugin_summary">Käytä lisäosaa sisäänrakennetun kartan sijaan</string>
+ <string name="copy_link">Kopioi web-osoite</string>
+ <string name="copy_jabber_id">Kopioi XMPP-osoite</string>
+ <string name="p1_s3_filetransfer">Tiedostonjako HTTP:llä S3:een</string>
+ <string name="pref_start_search_summary">\'Aloita keskustelu\' -näytöllä avaa näppäimistö ja siirrä kursori hakukenttään</string>
+ <string name="group_chat_avatar">Ryhmän kuvake</string>
+ <string name="host_does_not_support_group_chat_avatars">Isäntäpalvelin ei tue ryhmäkeskustelun kuvakkeita</string>
+ <string name="only_the_owner_can_change_group_chat_avatar">Vain omistaja voi vaihtaa kuvakkeen</string>
+ <string name="contact_name">Yhteystiedon nimi</string>
+ <string name="nickname">Nimimerkki</string>
+ <string name="group_chat_name">Nimi</string>
+ <string name="providing_a_name_is_optional">Nimen antaminen on vapaaehtoista</string>
+ <string name="create_dialog_group_chat_name">Ryhmäkeskustelun nimi</string>
+ <string name="conference_destroyed">Ryhmäkeskustelu on tuhottu</string>
+ <string name="unable_to_save_recording">Tallennetta ei voitu tallentaa</string>
+ <string name="foreground_service_channel_name">Edustapalvelu</string>
+ <string name="notification_group_status_information">Tilatieto</string>
+ <string name="error_channel_name">Yhteysongelmat</string>
+ <string name="notification_group_messages">Viestit</string>
+ <string name="notification_group_calls">Puhelut</string>
+ <string name="messages_channel_name">Viestit</string>
+ <string name="incoming_calls_channel_name">Saapuvat puhelut</string>
+ <string name="delivery_failed_channel_name">Toimitusvirheet</string>
+ <string name="pref_more_notification_settings_summary">Tärkeys, ääni, värinä</string>
+ <string name="video_compression_channel_name">Videonpakkaus</string>
+ <string name="view_media">Näytä media</string>
+ <string name="group_chat_members">Osallistujat</string>
+ <string name="media_browser">Mediaselain</string>
+ <string name="pref_video_compression">Videon laatu</string>
+ <string name="pref_video_compression_summary">Heikompi laatu tarkoittaa pienempiä tiedostoja</string>
+ <string name="video_360p">Keski (360p)</string>
+ <string name="video_720p">Korkea (720p)</string>
+ <string name="cancelled">peruutettu</string>
+ <string name="already_drafting_message">Olet jo aloittanut viestin luonnostelun</string>
+ <string name="feature_not_implemented">Ominaisuutta ei ole toteutettu</string>
+ <string name="invalid_country_code">Virheellinen maakoodi</string>
+ <string name="choose_a_country">Valitse maa</string>
+ <string name="phone_number">puhelinnumero</string>
+ <string name="verify_your_phone_number">Vahvista puhelinnumerosi</string>
+ <string name="enter_country_code_and_phone_number">Quicksy lähettää viestin sinulle varmistaaksesi puhelinnumerosi. Operaattorisi saattaa laskuttaa siitä. Syötä maakoodi ja puhelinnumero:</string>
+ <string name="we_will_be_verifying"><![CDATA[Vahvistamme puhelinnumeron <br/><br/><b>%s</b><br/><br/>Onko se oikein, vai haluaisitko muuttaa numeroa?]]></string>
+ <string name="not_a_valid_phone_number">%s ei ole kelvollinen puhelinnumero.</string>
+ <string name="please_enter_your_phone_number">Syötä puhelinnumerosi.</string>
+ <string name="verify_x">Varmista %s</string>
+ <string name="we_have_sent_you_an_sms_to_x"><![CDATA[Lähetimme sinulle tekstiviestin numeroon <b>%s</b>.]]></string>
+ <string name="we_have_sent_you_another_sms">Lähetimme sinulle uuden viestin 6-numeroisella koodilla.</string>
+ <string name="please_enter_pin_below">Kirjoita 6-numeroinen PIN-koodi alapuolelle.</string>
+ <string name="resend_sms">Lähetä uusi tekstiviesti</string>
+ <string name="resend_sms_in">Lähetä uusi tekstivieti (%s)</string>
+ <string name="wait_x">Odota (%s)</string>
+ <string name="back">takaisin</string>
+ <string name="possible_pin">Mahdollinen PIN-koodi liitettiin leikepöydältä automaattisesti</string>
+ <string name="please_enter_pin">Syötä 6-numeroinen PIN-koodisi.</string>
+ <string name="abort_registration_procedure">Haluatko varmasti perua rekisteröintiprosessin?</string>
+ <string name="yes">Kyllä</string>
+ <string name="no">Ei</string>
+ <string name="verifying">Varmistetaan...</string>
+ <string name="requesting_sms">Pyydetään tekstiviestiä</string>
+ <string name="incorrect_pin">Syöttämäsi PIN-koodi on väärä.</string>
+ <string name="pin_expired">Lähettämämme PIN-koodi on vanhentunut.</string>
+ <string name="unknown_api_error_network">Tuntematon verkkovirhe.</string>
+ <string name="unknown_api_error_response">Tuntematon vastaus palvelimelta.</string>
+ <string name="unable_to_connect_to_server">Palvelimeen ei saatu yhteyttä.</string>
+ <string name="unable_to_establish_secure_connection">Turvallinen yhteys epäonnistui.</string>
+ <string name="unable_to_find_server">Palvelinta ei löytynyt.</string>
+ <string name="something_went_wrong_processing_your_request">Pyyntösi käsittelyssä tapahtui jokin virhe.</string>
+ <string name="no_network_connection">Ei verkkoyhteyttä</string>
+ <string name="try_again_in_x">Odota %s ja yritä uudelleen</string>
+ <string name="too_many_attempts">Liian monta yritystä</string>
+ <string name="the_app_is_out_of_date">Käytät vanhentunutta versiota tästä sovelluksesta.</string>
+ <string name="update">Päivitä</string>
+ <string name="logged_in_with_another_device">Puhelinnumerolla on tällä hetkellä kirjauduttu sisään toiselle laitteelle.</string>
+ <string name="your_name">Nimesi</string>
+ <string name="enter_your_name">Kirjoita nimesi</string>
+ <string name="no_name_set_instructions">Napauta muokkaa-nappia valitaksesi nimesi.</string>
+ <string name="reject_request">Hylkää pyyntö</string>
+ <string name="install_orbot">Asenna Orbot</string>
+ <string name="start_orbot">Käynnistä Orbot</string>
+ <string name="no_market_app_installed">Sovelluskauppaa ei löydy.</string>
+ <string name="group_chat_will_make_your_jabber_id_public">Tämä kanava julkaisee XMPP-osoitteesi</string>
+ <string name="ebook">e-kirja</string>
+ <string name="video_original">Alkuperäinen (pakkaamaton)</string>
+ <string name="open_with">Avaa sovelluksella...</string>
+ <string name="set_profile_picture">Conversations-profiilikuva</string>
+ <string name="choose_account">Valitse tili</string>
+ <string name="restore_backup">Palauta varmuuskopiosta</string>
+ <string name="restore">Palauta</string>
+ <string name="enter_password_to_restore">Syötä salasanasi tilille %s palauttaaksesi varmuuskopion.</string>
+ <string name="restore_warning">Älä käytä varmuuskopion palautusta asennuksen kloonaamiseksi (käyttääksesi yhtä aikaa toisella laitteella). Varmuuskopion palautus on tarkoitettu ainoiastaan laitteen tai sovelluksen vaihtamiseksi tai jos olet kadottanut alkuperäisen laitteesi.</string>
+ <string name="unable_to_restore_backup">Varmuuskopion palautus epäonnistui.</string>
+ <string name="unable_to_decrypt_backup">Varmuuskopion salauksen purku epäonnistui. Onko salasana oikein?</string>
+ <string name="backup_channel_name">Varmuuskopiointi & palautus</string>
+ <string name="enter_jabber_id">Syötä XMPP-osoite</string>
+ <string name="create_group_chat">Luo ryhmäkeskustelu</string>
+ <string name="join_public_channel">Liity julkiselle kanavalle</string>
+ <string name="create_private_group_chat">Luo yksityinen ryhmäkeskustelu</string>
+ <string name="create_public_channel">Luo julkinen kanava</string>
+ <string name="create_dialog_channel_name">Kanavan nimi</string>
+ <string name="xmpp_address">XMPP-osoite</string>
+ <string name="please_enter_name">Anna kanavalle nimi</string>
+ <string name="please_enter_xmpp_address">Anna XMPP-osoite</string>
+ <string name="this_is_an_xmpp_address">Tämä on XMPP-osoite. Anna nimi sen sijaan.</string>
+ <string name="creating_channel">Luodaan julkista kanavaa...</string>
+ <string name="channel_already_exists">Kanava on jo olemassa</string>
+ <string name="joined_an_existing_channel">Liityit olemassa olevalle kanavalle</string>
+ <string name="unable_to_set_channel_configuration">Kanavan asetuksia ei saatu tallennettua</string>
+ <string name="allow_participants_to_edit_subject">Salli kenen tahansa vaihtaa aihetta</string>
+ <string name="allow_participants_to_invite_others">Salli kenen tahansa kutsua ryhmään</string>
+ <string name="anyone_can_edit_subject">Kuka tahansa voi muokata aihetta.</string>
+ <string name="owners_can_edit_subject">Omistajat voivat muokata aihetta.</string>
+ <string name="admins_can_edit_subject">Ylläpitäjät voivat muokata aihetta.</string>
+ <string name="owners_can_invite_others">Omistajat voivat kutsua muita.</string>
+ <string name="anyone_can_invite_others">Kuka tahansa voi kutsua muita.</string>
+ <string name="jabber_ids_are_visible_to_admins">XMPP-osoitteet ovat näkyvillä ylläpitäjille.</string>
+ <string name="jabber_ids_are_visible_to_anyone">XMPP-osoiteet ovat näkyllä kaikille.</string>
+ <string name="no_users_hint_channel">Tällä julkisella kanavalla ei ole osallistujia. Kutsu yhteystietojasi tai käytä jakopainiketta kanavan XMPP-osoitteen levittämiseen.</string>
+ <string name="no_users_hint_group_chat">Tässä yksityisessä ryhmässä ei ole ketään.</string>
+ <string name="manage_permission">Hallitse oikeuksia</string>
+ <string name="file_too_large">Tiedosto on liian iso</string>
+ <string name="attach">Liitä</string>
+ <string name="discover_channels">Löydä kanavia</string>
+ <string name="search_channels">Hae kanavia</string>
+ <string name="channel_discovery_opt_in_title">Mahdollinen yksityisyyden loukkaus!</string>
+ <string name="channel_discover_opt_in_message"><![CDATA[Kanavan etsintätoiminto käyttää kolmannen osapuolen palvelua nimeltä <a href=\"https://search.jabber.network\">search.jabber.network</a>.<br><br>Sen käyttö lähettää IP-osoitteesi ja hakusanat palvelulle. Katso heidän <a href=\"https://search.jabber.network/privacy\">yksityisyyskäytännöstään</a> lisätietoa.]]></string>
+ <string name="i_already_have_an_account">Minulla on jo tili</string>
+ <string name="add_existing_account">Lisää olemassa oleva tili</string>
+ <string name="register_new_account">Rekisteröi uusi tili</string>
+ <string name="this_looks_like_a_domain">Tämä vaikuttaa verkkotunnukselta</string>
+ <string name="add_anway">Lisää silti</string>
+ <string name="this_looks_like_channel">Tämä vaikuttaa kanavan osoitteelta</string>
+ <string name="share_backup_files">Jaa varmuuskopiotiedostot</string>
+ <string name="event">Tapahtuma</string>
+ <string name="open_backup">Avaa varmuuskopio</string>
+ <string name="not_a_backup_file">Valitsemasi tiedosto ei ole Conversationsin varmuuskopio</string>
+ <string name="account_already_setup">Tämä tili on jo asennettu</string>
+ <string name="please_enter_password">Syötä tämän tilin salasana</string>
+ <string name="unable_to_perform_this_action">Toiminnon suorittaminen epäonnistui</string>
+ <string name="open_join_dialog">Liity julkiselle kanavalle...</string>
+ <string name="sharing_application_not_grant_permission">Jakava sovellus ei antanut tarvittavaa lupaa lukea tiedostoa.</string>
+ <string name="group_chats_and_channels"><![CDATA[Ryhmäkeskustelut ja kanavat]]></string>
+ <string name="jabber_network">jabber.network</string>
+ <string name="local_server">Paikallinen palvelin</string>
+ <string name="pref_channel_discovery_summary">Suurimman osan käyttäjistä kannattaa valita \'jabber.network\' sillä se tarjoaa parempia ehdotuksia koko julkisesta XMPP-ekosysteemistä.</string>
+ <string name="pref_channel_discovery">Kanavien löytötapa</string>
+ <string name="backup">Varmuuskopio</string>
+ <string name="category_about">Tietoa</string>
+ <string name="please_enable_an_account">Ota jokin tili käyttöön</string>
+ <string name="make_call">Soita puhelu</string>
+ <string name="rtp_state_incoming_call">Saapuva puhelu</string>
+ <string name="rtp_state_incoming_video_call">Saapuva videopuhelu</string>
+ <string name="rtp_state_connecting">Yhdistetään</string>
+ <string name="rtp_state_connected">Yhdistetty</string>
+ <string name="rtp_state_accepting_call">Hyväksytään puhelua</string>
+ <string name="rtp_state_ending_call">Päätetään puhelua</string>
+ <string name="answer_call">Vastaa</string>
+ <string name="dismiss_call">Hylkää</string>
+ <string name="rtp_state_finding_device">Etsitään laitteita</string>
+ <string name="rtp_state_ringing">Soi</string>
+ <string name="rtp_state_declined_or_busy">Kiireinen</string>
+ <string name="rtp_state_connectivity_error">Puhelun yhdistäminen epäonnistui</string>
+ <string name="rtp_state_connectivity_lost_error">Yhteys katkesi</string>
+ <string name="rtp_state_retracted">Puhelu peruttu</string>
+ <string name="rtp_state_application_failure">Sovelluksen virhe</string>
+ <string name="rtp_state_security_error">Varmennusvirhe</string>
+ <string name="hang_up">Päätä puhelu</string>
+ <string name="ongoing_call">Puhelu kesken</string>
+ <string name="ongoing_video_call">Videopuhelu kesken</string>
+ <string name="disable_tor_to_make_call">Poista Tor käytöstä soittaaksesi puhelun</string>
+ <string name="incoming_call">Saapuva puhelu</string>
+ <string name="incoming_call_duration">Saapuva puhelu · %s</string>
+ <string name="missed_call_timestamp">Vastaamaton puhelu · %s</string>
+ <string name="outgoing_call">Lähtevä puhelu</string>
+ <string name="outgoing_call_duration">Lähtevä puhelu · %s</string>
+ <string name="missed_call">Vastaamaton puhelu</string>
+ <string name="audio_call">Äänipuhelu</string>
+ <string name="video_call">Videopuhelu</string>
+ <string name="help">Apua</string>
+ <string name="switch_to_conversation">Vaihda keskusteluun</string>
+ <string name="microphone_unavailable">Mikrofoni ei käytettävissä</string>
+ <string name="only_one_call_at_a_time">Voit osallistua vain yhteen puheluun kerrallaan.</string>
+ <string name="return_to_ongoing_call">Takaisin keskeneräiseen puheluun</string>
+ <string name="could_not_switch_camera">Kameran vaihtaminen epäonnistui</string>
+ <string name="add_to_favorites">Kiinnitä</string>
+ <string name="remove_from_favorites">Irrota</string>
+ <string name="gpx_track">GPX-reitti</string>
+ <string name="could_not_correct_message">Viestin korjaaminen epäonnistui</string>
+ <string name="search_all_conversations">Kaikki keskustelut</string>
+ <string name="search_this_conversation">Tämä keskustelu</string>
+ <string name="your_avatar">Profiilikuvasi</string>
+ <string name="avatar_for_x">%s:n profiilikuva</string>
+ <string name="encrypted_with_omemo">OMEMO-salattu</string>
+ <string name="encrypted_with_openpgp">OpenPGP-salattu</string>
+ <string name="not_encrypted">Salaamaton</string>
+ <string name="exit">Poistu</string>
+ <string name="record_voice_mail">Jätä viesti vastaajaan</string>
+ <string name="play_audio">Toista ääni</string>
+ <string name="pause_audio">Keskeytä ääni</string>
+ <string name="add_contact_or_create_or_join_group_chat">Lisää yhteystieto, luo tai liity ryhmäkeskusteluun, tai etsi kanava</string>
+ <plurals name="view_users">
+ <item quantity="one">Näytä %1$d osallistuja</item>
+ <item quantity="other">Näytä %1$d osallistujaa</item>
+ </plurals>
+ <plurals name="some_messages_could_not_be_delivered">
+ <item quantity="one">Viestiä ei saatu toimitettua</item>
+ <item quantity="other">Joitain viestejä ei saatu toimitettua</item>
+ </plurals>
+ <string name="failed_deliveries">Toimitusvirheet</string>
+ <string name="more_options">Lisää vaihtoehtoja</string>
+ <string name="no_application_found">Sovellusta ei löytynyt</string>
+ <string name="invite_to_app">Kutsu Conversationsiin</string>
+ <string name="unable_to_parse_invite">Kutsun jäsentäminen epäonnistui</string>
+ <string name="server_does_not_support_easy_onboarding_invites">Palvelin ei tue kutsujen luomista</string>
+ <string name="no_active_accounts_support_this">Yksikään aktiivinen tili ei tue tätä toimintoa</string>
+ <string name="backup_started_message">Varmuuskopion teko aloitettu. Saat ilmoituksen kun se on valmis.</string>
+ <string name="unable_to_enable_video">Videon käyttöönotto epäonnistui</string>
+ <string name="plain_text_document">Perustekstiasiakirja</string>
+
+</resources>
@@ -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 d’inscription 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>
@@ -47,8 +47,8 @@
<string name="participant">Participante</string>
<string name="visitor">Visitante</string>
<string name="remove_contact_text">Desexas eliminar a %s da túa lista de contactos? As conversas con este contacto non se eliminarán.</string>
- <string name="block_contact_text">Desexa evitar que %s lle envíe mensaxes?</string>
- <string name="unblock_contact_text">Desexa desbloquear %s e permitirlles que lle envíen mensaxes?</string>
+ <string name="block_contact_text">Queres evitar que %s che envíe mensaxes?</string>
+ <string name="unblock_contact_text">Queres desbloquear a %s e permitirlle que che envíe mensaxes?</string>
<string name="block_domain_text">Bloquear todos os contactos desde 1%s?</string>
<string name="unblock_domain_text">Desbloquear todos os contactos desde 1%s?</string>
<string name="contact_blocked">Contacto bloqueado</string>
@@ -58,7 +58,7 @@
<string name="change_password_on_server">Cambiar o contrasinal no servidor</string>
<string name="share_with">Compartir con</string>
<string name="start_conversation">Comezar conversa</string>
- <string name="invite_contact">Invitar contacto</string>
+ <string name="invite_contact">Convidar contacto</string>
<string name="invite">Convidar</string>
<string name="contacts">Contactos</string>
<string name="contact">Contacto</string>
@@ -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="delete_file_dialog_msg">Tes a certeza de querer eliminar este ficheiro?\n\n<b>Aviso:</b> Esto non eliminará as copias de este ficheiro que están gardadas noutros dispositivos ou servidores.</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>
@@ -242,7 +244,7 @@
<string name="delete_bookmark">Eliminar marcador</string>
<string name="destroy_room">Destruír a conversa en grupo</string>
<string name="destroy_channel">Eliminar canle</string>
- <string name="destroy_room_dialog">Está segura de querer destruír esta conversa en grupo?\n\n<b>Aviso:</b> A conversa en grupo será totalmente eliminada do servidor.</string>
+ <string name="destroy_room_dialog">Tes a certeza de querer destruír esta conversa en grupo?\n\n<b>Aviso:</b> A conversa en grupo será totalmente eliminada do servidor.</string>
<string name="destroy_channel_dialog">Tes a certeza de querer eliminar a canle?\n\n<b>Aviso:</b> A canle será eliminada completamente do servidor.</string>
<string name="could_not_destroy_room">Non se desfixo a conversa en grupo</string>
<string name="could_not_destroy_channel">Non se puido eliminar a canle</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>
@@ -489,7 +492,7 @@
<string name="action_renew_certificate">Anovar certificado</string>
<string name="error_fetching_omemo_key">Fallo obtendo a chave OMEMO!</string>
<string name="verified_omemo_key_with_certificate">Comprobouse a chave OMEMO co certificado!</string>
- <string name="device_does_not_support_certificates">O seu dispositivo non admite a selección de certificados do cliente!</string>
+ <string name="device_does_not_support_certificates">O teu dispositivo non admite a selección de certificados do cliente!</string>
<string name="pref_connection_options">Conexión</string>
<string name="pref_use_tor">Conectar vía Tor</string>
<string name="pref_use_tor_summary">Facer pasar todas as conexións a través da rede Tor. Require Orbot</string>
@@ -538,7 +541,7 @@
<string name="welcome_text_quicksy"><![CDATA[Quicksy é un derivado do popular cliente XMPP Conversations con descubrimento automático de contactos.<br><br>Podes rexistrarte co teu número de teléfono e Quicksy suxerirache automáticamente —tomando os números da túa libreta de enderezos como referencia— posibles contactos para ti.<br><br>Ao rexistrarte aceptas a nosa <a href="https://quicksy.im/#privacy">política de privacidade</a>.]]></string>
<string name="agree_and_continue">Aceptar e continuar</string>
<string name="magic_create_text">Tes unha guía para crear unha conta en conversations.im¹\nAo escoller conversations.im como provedor poderás comunicarte con outras usuarias de outros provedores con só darlles o teu enderezo XMPP completo.</string>
- <string name="your_full_jid_will_be">O seu enderezo XMPP completo será: %s</string>
+ <string name="your_full_jid_will_be">O teu enderezo XMPP completo será: %s</string>
<string name="create_account">Crear conta</string>
<string name="use_own_provider">Utilizar o meu propio proveedor</string>
<string name="pick_your_username">Elixe un identificador</string>
@@ -551,12 +554,12 @@
<string name="presence_xa">Non dipoñible</string>
<string name="presence_dnd">Ocupada</string>
<string name="secure_password_generated">Xerouse un contrasinal seguro</string>
- <string name="device_does_not_support_battery_op">O seu dispositivo non permite non optimizar a batería</string>
+ <string name="device_does_not_support_battery_op">O teu dispositivo non permite evitar a optimización a batería</string>
<string name="registration_please_wait">Fallo no rexistro: inténteo de novo</string>
<string name="registration_password_too_weak">Fallo no rexistro: contrasinal moi feble</string>
<string name="choose_participants">Escoller participantes</string>
<string name="creating_conference">Creando unha conversa en grupo...</string>
- <string name="invite_again">Invitar de novo</string>
+ <string name="invite_again">Convidar de novo</string>
<string name="gp_disable">Desactivar</string>
<string name="gp_short">Breve</string>
<string name="gp_medium">Medio</string>
@@ -591,7 +594,7 @@
<string name="pref_delete_omemo_identities">Borrar identidades OMEMO</string>
<string name="pref_delete_omemo_identities_summary">Rexenerar chaves OMEMO. Todos os teus contactos terán que verificar a túa conta de novo. Utiliza esto só como último recurso.</string>
<string name="delete_selected_keys">Eliminar as chaves seleccionadas.</string>
- <string name="error_publish_avatar_offline">Precisa estar conectada para publicar o seu avatar.</string>
+ <string name="error_publish_avatar_offline">Debes ter conexión para publicar o teu avatar.</string>
<string name="show_error_message">Mostrar mensaxe do fallo</string>
<string name="error_message">Mensaxe de fallo</string>
<string name="data_saver_enabled">Aforrador de datos habilitado</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">
@@ -685,7 +688,7 @@
<string name="mtm_accept_cert">¿Aceptar certificado descoñecido?</string>
<string name="mtm_trust_anchor">O certificado do servidor non está asinado por unha autoridade de certificación coñecida.</string>
<string name="mtm_accept_servername">¿Aceptar un nome de servidor que non coincida?</string>
- <string name="mtm_hostname_mismatch">O servidor non pode autenticarse como \"%s\". O certificado só é válido para:</string>
+ <string name="mtm_hostname_mismatch">O servidor non pode autenticarse como \"%s\". O certificado só é válido para:</string>
<string name="mtm_connect_anyway">Queres conectarte de todos os xeitos?</string>
<string name="mtm_cert_details">Detalles do certificado:</string>
<string name="once">Unha vez</string>
@@ -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>
@@ -780,15 +783,15 @@
<string name="invalid_country_code">Código de país non válido</string>
<string name="choose_a_country">Indica un país</string>
<string name="phone_number">número de teléfono</string>
- <string name="verify_your_phone_number">Valide o seu número de teléfono</string>
- <string name="enter_country_code_and_phone_number">Quicsy enviaralle unha mensaxe SMS (poderíanse aplicar cargos) para validar o seu número de teléfono. Introduza o código de país e número de teléfono.</string>
+ <string name="verify_your_phone_number">Valida o teu número de teléfono</string>
+ <string name="enter_country_code_and_phone_number">Quicksy vaiche enviar unha mensaxe SMS (podería ter custos) para validar o teu número de teléfono. Escribe o código de país e número de teléfono.</string>
<string name="we_will_be_verifying"><![CDATA[Validaremos o número de teléfono<br/><br/><b>%s</b><br/><br/>É correcto, ou quere modificar o número?]]></string>
<string name="not_a_valid_phone_number">%s non é un número de teléfono válido.</string>
- <string name="please_enter_your_phone_number">Por favor introduza o seu número de teléfono.</string>
+ <string name="please_enter_your_phone_number">Por favor escribe o teu número de teléfono.</string>
<string name="search_countries">Buscar países</string>
<string name="verify_x">Validar %s</string>
- <string name="we_have_sent_you_an_sms_to_x"><![CDATA[Enviámoslle un SMS a <b>%s</b>.]]></string>
- <string name="we_have_sent_you_another_sms">Enviamoslle outro SMS con un código de 6 díxitos.</string>
+ <string name="we_have_sent_you_an_sms_to_x"><![CDATA[Enviámosche un SMS a <b>%s</b>.]]></string>
+ <string name="we_have_sent_you_another_sms">Enviamosche outro SMS cun código de 6 díxitos.</string>
<string name="please_enter_pin_below">Por favor, introduza o pin de 6 díxitos inferior.</string>
<string name="resend_sms">Reenviar SMS</string>
<string name="resend_sms_in">Reenviar SMS (%s)</string>
@@ -802,7 +805,7 @@
<string name="verifying">Validando...</string>
<string name="requesting_sms">Solicitando SMS...</string>
<string name="incorrect_pin">O pin introducido non é correcto.</string>
- <string name="pin_expired">O pin que lle enviamos caducou.</string>
+ <string name="pin_expired">O pin que che enviamos caducou.</string>
<string name="unknown_api_error_network">Fallo descoñecido na rede.</string>
<string name="unknown_api_error_response">Resposta descoñecida desde o servidor.</string>
<string name="unable_to_connect_to_server">Non se puido conectar co servidor.</string>
@@ -819,19 +822,19 @@
<string name="update">Actualizar</string>
<string name="logged_in_with_another_device">Este número de teléfono está actualmente ligado a outro dispositivo.</string>
<string name="enter_your_name_instructions">Por favor, escribe o teu nome para permitir que a xente que non te ten na axenda de enderezos sepa quen es.</string>
- <string name="your_name">O seu nome</string>
- <string name="enter_your_name">Introduza o seu nome</string>
- <string name="no_name_set_instructions">Utilice o botón editar para escribir o seu nome.</string>
+ <string name="your_name">O teu nome</string>
+ <string name="enter_your_name">Escribe o teu nome</string>
+ <string name="no_name_set_instructions">Establece o teu nome usando o botón editar.</string>
<string name="reject_request">Rexeitar solicitude</string>
<string name="install_orbot">Instalar Orbot</string>
<string name="start_orbot">Iniciar Orbot</string>
- <string name="no_market_app_installed">Non ten loxa de aplicacións instalada.</string>
+ <string name="no_market_app_installed">Non hai tenda de apps instalada.</string>
<string name="group_chat_will_make_your_jabber_id_public">Esta canle fará público o teu enderezo XMPP</string>
<string name="ebook">e-book</string>
<string name="video_original">Orixinal (non comprimido)</string>
<string name="open_with">Abrir con...</string>
<string name="set_profile_picture">Imaxe de perfil en Conversations</string>
- <string name="choose_account">Escoller conta</string>
+ <string name="choose_account">Elexir conta</string>
<string name="restore_backup">Restablecer copia de apoio</string>
<string name="restore">Restablecer</string>
<string name="enter_password_to_restore">Escribe o contrasinal da conta %s para restablecer a copia.</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>
@@ -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>
@@ -2,13 +2,13 @@
<resources>
<string name="action_settings">Impostazioni</string>
<string name="action_add">Nuova conversazione</string>
- <string name="action_accounts">Gestisci account</string>
- <string name="action_account">Gestisci account</string>
+ <string name="action_accounts">Gestisci profili</string>
+ <string name="action_account">Gestisci profilo</string>
<string name="action_end_conversation">Chiudi conversazione</string>
<string name="action_contact_details">Dettagli del contatto</string>
<string name="action_muc_details">Dettagli chat di gruppo</string>
<string name="channel_details">Dettagli canale</string>
- <string name="action_add_account">Aggiungi account</string>
+ <string name="action_add_account">Aggiungi profilo</string>
<string name="action_edit_contact">Modifica il nome</string>
<string name="action_add_phone_book">Aggiungi alla rubrica</string>
<string name="action_delete_contact">Cancella dalla lista</string>
@@ -18,7 +18,7 @@
<string name="action_unblock_domain">Sblocca dominio</string>
<string name="action_block_participant">Blocca partecipante</string>
<string name="action_unblock_participant">Sblocca partecipante</string>
- <string name="title_activity_manage_accounts">Gestisci account</string>
+ <string name="title_activity_manage_accounts">Gestisci profili</string>
<string name="title_activity_settings">Impostazioni</string>
<string name="title_activity_sharewith">Condividi con Conversation</string>
<string name="title_activity_start_conversation">Inizia una conversazione</string>
@@ -54,7 +54,7 @@
<string name="contact_blocked">Contatto bloccato</string>
<string name="blocked">Bloccato</string>
<string name="remove_bookmark_text">Vuoi rimuovere %s dai segnalibri? Le conversazioni con questo segnalibro non verranno rimosse.</string>
- <string name="register_account">Registra un nuovo account sul server</string>
+ <string name="register_account">Registra un nuovo profilo sul server</string>
<string name="change_password_on_server">Cambia la password sul server</string>
<string name="share_with">Condividi con</string>
<string name="start_conversation">Inizia conversazione</string>
@@ -72,12 +72,12 @@
<string name="save">Salva</string>
<string name="ok">OK</string>
<string name="crash_report_title">Errore di %1$s</string>
- <string name="crash_report_message">Usare il tuo account XMPP per inviare segnalazioni di errore aiuta lo sviluppo in corso di %1$s.</string>
+ <string name="crash_report_message">Usare il tuo profilo XMPP per inviare segnalazioni di errore aiuta lo sviluppo in corso di %1$s.</string>
<string name="send_now">Invia adesso</string>
<string name="send_never">Non chiedere più</string>
- <string name="problem_connecting_to_account">Impossibile connettersi all\'account</string>
- <string name="problem_connecting_to_accounts">Impossibile connettersi a più account</string>
- <string name="touch_to_fix">Tocca per gestire i tuoi account</string>
+ <string name="problem_connecting_to_account">Impossibile connettersi al profilo</string>
+ <string name="problem_connecting_to_accounts">Impossibile connettersi a più profili</string>
+ <string name="touch_to_fix">Tocca per gestire i tuoi profili</string>
<string name="attach_file">Allega file</string>
<string name="not_in_roster">Aggiungere questo contatto alla lista dei contatti?</string>
<string name="add_contact">Aggiungi contatto</string>
@@ -130,15 +130,17 @@
<string name="pref_advanced_options">Avanzate</string>
<string name="pref_never_send_crash">Non inviare mai segnalazioni di errore</string>
<string name="pref_never_send_crash_summary">Se scegli di inviare una segnalazione dell’errore aiuterai lo sviluppo</string>
- <string name="pref_confirm_messages">Conferma messaggi</string>
+ <string name="pref_confirm_messages">Conferma i 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 la cattura dello 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>
<string name="accept">Accetta</string>
<string name="error">Si è verificato un errore</string>
<string name="recording_error">Errore</string>
- <string name="your_account">Il tuo account</string>
+ <string name="your_account">Il tuo profilo</string>
<string name="send_presence_updates">Invia aggiornamenti della presenza</string>
<string name="receive_presence_updates">Ricevi aggiornamenti della presenza</string>
<string name="ask_for_presence_updates">Chiedi aggiornamenti della presenza</string>
@@ -150,9 +152,10 @@
<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">L’app 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 un’immagine</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>
+ <string name="account_status_online">In linea</string>
<string name="account_status_connecting">In connessione\u2026</string>
<string name="account_status_offline">Offline</string>
<string name="account_status_unauthorized">Non autorizzato</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>
@@ -172,16 +176,16 @@
<string name="encryption_choice_otr">OTR</string>
<string name="encryption_choice_pgp">OpenPGP</string>
<string name="encryption_choice_omemo">OMEMO</string>
- <string name="mgmt_account_delete">Elimina utente</string>
+ <string name="mgmt_account_delete">Elimina profilo</string>
<string name="mgmt_account_disable">Disattiva temporaneamente</string>
<string name="mgmt_account_publish_avatar">Pubblica avatar</string>
<string name="mgmt_account_publish_pgp">Pubblica chiave pubblica OpenPGP</string>
<string name="unpublish_pgp">Rimuovi chiave pubblica OpenPGP</string>
<string name="unpublish_pgp_message">Sei sicuro di volere rimuovere la tua chiave pubblica OpenPGP dalla dichiarazione di presenza?\nI tuoi contatti non potranno più inviarti messaggi cifrati con OpenPGP.</string>
<string name="openpgp_has_been_published">Chiave pubblica OpenPGP pubblicata.</string>
- <string name="mgmt_account_enable">Attiva utente</string>
+ <string name="mgmt_account_enable">Attiva profilo</string>
<string name="mgmt_account_are_you_sure">Sei sicuro?</string>
- <string name="mgmt_account_delete_confirm_text">L\'eliminazione del tuo account cancellerà tutta la cronologia dielle conversazioni</string>
+ <string name="mgmt_account_delete_confirm_text">L\'eliminazione del tuo profilo cancellerà tutta la cronologia dielle conversazioni</string>
<string name="attach_record_voice">Registra la voce</string>
<string name="account_settings_jabber_id">Indirizzo XMPP</string>
<string name="block_jabber_id">Blocca indirizzo XMPP</string>
@@ -215,9 +219,9 @@
<string name="openpgp_messages_found">Nuovi messaggi cifrati con OpenPGP trovati</string>
<string name="openpgp_key_id">ID chiave OpenPGP</string>
<string name="omemo_fingerprint">Impronta OMEMO</string>
- <string name="omemo_fingerprint_x509">v\\OMEMO impronta</string>
- <string name="omemo_fingerprint_selected_message">OMEMO fingerprint (messaggio originatore)</string>
- <string name="omemo_fingerprint_x509_selected_message">v\\OMEMO fingerprint (messaggio originatore)</string>
+ <string name="omemo_fingerprint_x509">v\\Impronta OMEMO</string>
+ <string name="omemo_fingerprint_selected_message">Impronta OMEMO (origine del messaggio)</string>
+ <string name="omemo_fingerprint_x509_selected_message">v\\Impronta OMEMO (origine del messaggio)</string>
<string name="other_devices">Altri dispositivi</string>
<string name="trust_omemo_fingerprints">Fidati delle impronte OMEMO</string>
<string name="fetching_keys">Ricezione chiavi...</string>
@@ -266,7 +270,7 @@
<string name="private_message_to">a %s</string>
<string name="send_private_message_to">Invia messaggio privato a %s</string>
<string name="connect">Connetti</string>
- <string name="account_already_exists">Questo utente esiste già</string>
+ <string name="account_already_exists">Questo profilo esiste già</string>
<string name="next">Successivo</string>
<string name="server_info_session_established">Sessione stabilita</string>
<string name="skip">Salta</string>
@@ -281,7 +285,7 @@
<string name="pref_security_settings">Sicurezza</string>
<string name="pref_allow_message_correction">Permetti correzione del messaggio</string>
<string name="pref_allow_message_correction_summary">Consenti ai tuoi contatti di modificare retroattivamente i loro messaggi</string>
- <string name="pref_expert_options">Impostazioni esperto</string>
+ <string name="pref_expert_options">Impostazioni per esperti</string>
<string name="pref_expert_options_summary">Fai attenzione con queste impostazioni</string>
<string name="title_activity_about_x">Informazioni su %s</string>
<string name="title_pref_quiet_hours">Ore di quiete</string>
@@ -299,7 +303,7 @@
<string name="conference_kicked">Sei stato buttato fuori da questa chat di gruppo</string>
<string name="conference_shutdown">La chat di gruppo è stata chiusa</string>
<string name="conference_unknown_error">Non sei più in questa chat di gruppo</string>
- <string name="using_account">usando l’account %s</string>
+ <string name="using_account">usando il profilo %s</string>
<string name="hosted_on">ospitato su %s</string>
<string name="checking_x">Controllo %s su host HTTP</string>
<string name="not_connected_try_again">Non sei connesso. Riprova più tardi</string>
@@ -318,19 +322,19 @@
<string name="scan_qr_code">Scansiona codice a barre 2D</string>
<string name="show_qr_code">Mostra codice a barre 2D</string>
<string name="show_block_list">Mostra la lista nera</string>
- <string name="account_details">Dettagli account</string>
+ <string name="account_details">Dettagli del profilo</string>
<string name="confirm">Conferma</string>
<string name="try_again">Prova di nuovo</string>
<string name="pref_keep_foreground_service">Servizio in primo piano</string>
<string name="pref_keep_foreground_service_summary">Evita che il sistema operativo chiuda la connessione</string>
- <string name="pref_create_backup">Crea backup</string>
+ <string name="pref_create_backup">Crea un backup</string>
<string name="pref_create_backup_summary">I file di backup verranno salvati in %s</string>
- <string name="notification_create_backup_title">Creazione file di backup</string>
+ <string name="notification_create_backup_title">Creazione dei file di backup</string>
<string name="notification_backup_created_title">Il tuo backup è stato creato</string>
<string name="notification_backup_created_subtitle">I file di backup sono stati salvati in %s</string>
<string name="restoring_backup">Ripristino backup</string>
<string name="notification_restored_backup_title">Il tuo backup è stato ripristinato</string>
- <string name="notification_restored_backup_subtitle">Non dimenticare di attivare l\'account.</string>
+ <string name="notification_restored_backup_subtitle">Non dimenticare di attivare il profilo.</string>
<string name="choose_file">Scegli un file</string>
<string name="receiving_x_file">Ricezione di %1$s file (%2$d%% completato)</string>
<string name="download_x_file">Scarica %s</string>
@@ -352,7 +356,7 @@
<string name="enable_notifications">Attiva le notifiche</string>
<string name="no_conference_server_found">Nessun server per chat di gruppo trovato</string>
<string name="conference_creation_failed">Impossibile creare la chat di gruppo</string>
- <string name="account_image_description">Avatar utente</string>
+ <string name="account_image_description">Avatar del profilo</string>
<string name="copy_omemo_clipboard_description">Copia impronta OMEMO negli appunti</string>
<string name="regenerate_omemo_key">Rigenera chiave OMEMO</string>
<string name="clear_other_devices">Pulisci dispositivi</string>
@@ -369,8 +373,8 @@
<string name="current_password">Password attuale</string>
<string name="new_password">Nuova password</string>
<string name="password_should_not_be_empty">La password non può essere vuota</string>
- <string name="enable_all_accounts">Attiva tutti gli account</string>
- <string name="disable_all_accounts">Disattiva tutti gli account</string>
+ <string name="enable_all_accounts">Attiva tutti i profili</string>
+ <string name="disable_all_accounts">Disattiva tutti i profili</string>
<string name="perform_action_with">Esegui azione con</string>
<string name="no_affiliation">Nessuna affiliazione</string>
<string name="no_role">Offline</string>
@@ -405,13 +409,14 @@
<string name="reply">Rispondi</string>
<string name="mark_as_read">Segna come già letto</string>
<string name="pref_input_options">Input</string>
- <string name="pref_enter_is_send">Invio spedisce</string>
+ <string name="pref_enter_is_send">Il tasto Invio spedisce</string>
<string name="pref_enter_is_send_summary">Usa il tasto Invio per spedire il messaggio. Puoi sempre usare Ctrl+Invio per spedire, anche se questa opzione è disattivata.</string>
<string name="pref_display_enter_key">Mostra il tasto invio</string>
<string name="pref_display_enter_key_summary">Cambia il tasto delle faccine nel tasto di invio</string>
<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>
@@ -472,7 +477,7 @@
<string name="pref_treat_vibrate_as_silent">Tratta vibrazione come modalità silenziosa</string>
<string name="pref_treat_vibrate_as_dnd_summary">Imposta come occupato quando il dispositivo è in modalità vibrazione</string>
<string name="pref_show_connection_options">Impostazioni estese di connessione</string>
- <string name="pref_show_connection_options_summary">Mostra nome host e impostazioni della porta quando configuri un account</string>
+ <string name="pref_show_connection_options_summary">Mostra nome host e impostazioni della porta quando configuri un profilo</string>
<string name="hostname_example">xmpp.esempio.it</string>
<string name="action_add_account_with_certificate">Accedi con certificato</string>
<string name="unable_to_parse_certificate">Impossibile analizzare il certificato</string>
@@ -496,7 +501,7 @@
<string name="hostname_or_onion">Indirizzo server o .onion</string>
<string name="not_a_valid_port">Questo non è un numero di porta valido</string>
<string name="not_valid_hostname">Questo non è un nome host valido</string>
- <string name="connected_accounts">%1$d su %2$d account connessi</string>
+ <string name="connected_accounts">%1$d su %2$d profili connessi</string>
<plurals name="x_messages">
<item quantity="one">%d messaggio</item>
<item quantity="other">%d messaggi</item>
@@ -515,7 +520,7 @@
<string name="notify_only_when_highlighted">Notifica solo quando menzionato</string>
<string name="notify_never">Notifiche disattivate</string>
<string name="notify_paused">Notifiche in pausa</string>
- <string name="pref_picture_compression">Compressione immagini</string>
+ <string name="pref_picture_compression">Compressione delle immagini</string>
<string name="pref_picture_compression_summary">Suggerimento: usa \"Scegli un file\" invece di \"Scegli un\'immagine\" per inviare singole immagini non compresse a prescindere da questa impostazione.</string>
<string name="always">Sempre</string>
<string name="large_images_only">Solo immagini grandi</string>
@@ -524,27 +529,27 @@
<string name="battery_optimizations_enabled_dialog">Il tuo dispositivo sta facendo delle ingenti ottimizzazioni della batteria per %1$s che potrebbero portare ritardi alle notifiche o anche perdita di messaggi.\n\nTi verrà ora chiesto di disattivarle.</string>
<string name="disable">Disattiva</string>
<string name="selection_too_large">L\'area selezionata è troppo grande</string>
- <string name="no_accounts">(Nessun account attivo)</string>
+ <string name="no_accounts">(Nessun profilo attivo)</string>
<string name="this_field_is_required">Questo campo è obbligatorio</string>
<string name="correct_message">Correggi messaggio</string>
<string name="send_corrected_message">Invia messaggio corretto</string>
<string name="no_keys_just_confirm">Hai già validato l\'impronta di questa persona in modo sicuro per confermarne la fiducia. Selezionando “Fatto” stai solo confermando che %s fa parte di questa chat di gruppo.</string>
- <string name="this_account_is_disabled">Hai disattivato questo account</string>
+ <string name="this_account_is_disabled">Hai disattivato questo profilo</string>
<string name="security_error_invalid_file_access">Errore di sicurezza: accesso file non valido!</string>
<string name="no_application_to_share_uri">Nessuna app trovata per condividere l\'URI</string>
<string name="share_uri_with">Condividi l\'URI con...</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy è una variante del popolare client XMPP Conversations con ricerca automatica dei contatti.<br><br>Ti registri con il tuo numero di telefono e Quicksy ti suggerirà—in base ai numeri di telefono nella tua rubrica—automaticamente i possibili contatti.<br><br>Registrandoti accetti la nostra <a href="https://quicksy.im/#privacy">politica sulla privacy</a>.]]></string>
<string name="agree_and_continue">Accetta e continua</string>
- <string name="magic_create_text">È disponibile una guida per la creazione di un account su conversations.im.¹\nQuando scegli conversations.im come fornitore potrai comunicare con utenti di altri fornitori dando il tuo indirizzo XMPP completo.</string>
+ <string name="magic_create_text">È disponibile una guida per la creazione di un profilo su conversations.im.¹\nQuando scegli conversations.im come fornitore potrai comunicare con utenti di altri fornitori dando il tuo indirizzo XMPP completo.</string>
<string name="your_full_jid_will_be">Il tuo indirizzo XMPP completo sarà: %s</string>
- <string name="create_account">Crea account</string>
- <string name="use_own_provider">Usa un altro provider</string>
+ <string name="create_account">Crea profilo</string>
+ <string name="use_own_provider">Usa un altro fornitore</string>
<string name="pick_your_username">Scegli il tuo nome utente</string>
<string name="pref_manually_change_presence">Gestisci manualmente la disponibilità</string>
<string name="pref_manually_change_presence_summary">Imposta la tua disponibilità quando modifichi il messaggio di stato.</string>
<string name="status_message">Messaggio di stato</string>
<string name="presence_chat">Disponibile a chattare</string>
- <string name="presence_online">Online</string>
+ <string name="presence_online">In linea</string>
<string name="presence_away">Assente</string>
<string name="presence_xa">Non disponibile</string>
<string name="presence_dnd">Occupato</string>
@@ -559,7 +564,7 @@
<string name="gp_short">Breve</string>
<string name="gp_medium">Medio</string>
<string name="gp_long">Lungo</string>
- <string name="pref_broadcast_last_activity">Trasmissione</string>
+ <string name="pref_broadcast_last_activity">Trasmetti l\'utilizzo</string>
<string name="pref_broadcast_last_activity_summary">Fa sapere ai tuoi contatti quando usi Conversations</string>
<string name="pref_privacy">Privacy</string>
<string name="pref_theme_options">Tema</string>
@@ -568,7 +573,7 @@
<string name="pref_theme_light">Chiaro</string>
<string name="pref_theme_dark">Scuro</string>
<string name="pref_use_green_background">Sfondo verde</string>
- <string name="pref_use_green_background_summary">Usa sfondo verde per messaggi ricevuti</string>
+ <string name="pref_use_green_background_summary">Usa uno sfondo verde per i messaggi ricevuti</string>
<string name="unable_to_connect_to_keychain">Impossibile connettersi a OpenKeychain</string>
<string name="this_device_is_no_longer_in_use">Questo dispositivo non è più in uso</string>
<string name="type_pc">Computer</string>
@@ -584,9 +589,9 @@
<string name="no_permission_to_access_x">Nessuna autorizzazione per accedere a %s</string>
<string name="remote_server_not_found">Server remoto non trovato</string>
<string name="remote_server_timeout">Scadenza server remoto</string>
- <string name="unable_to_update_account">Impossibile aggiornare l\'account</string>
+ <string name="unable_to_update_account">Impossibile aggiornare il profilo</string>
<string name="report_jid_as_spammer">Segnala questo indirizzo XMPP per spam.</string>
- <string name="pref_delete_omemo_identities">Elimina identità OMEMO</string>
+ <string name="pref_delete_omemo_identities">Elimina le identità OMEMO</string>
<string name="pref_delete_omemo_identities_summary">Rigenera le tue chiavi OMEMO. I tuoi contatti dovranno verificare un\'altra volta la tua identità. Usalo solo come ultima spiaggia.</string>
<string name="delete_selected_keys">Cancella le chiavi selezionate</string>
<string name="error_publish_avatar_offline">Devi essere connesso per pubblicare l\'avatar.</string>
@@ -646,7 +651,7 @@
<item quantity="one">%d mese</item>
<item quantity="other">%d mesi</item>
</plurals>
- <string name="pref_automatically_delete_messages">Eliminazione automatica messaggi</string>
+ <string name="pref_automatically_delete_messages">Eliminazione automatica dei messaggi</string>
<string name="pref_automatically_delete_messages_description">Elimina automaticamente da questo dispositivo i messaggi più vecchi del lasso di tempo configurato.</string>
<string name="encrypting_message">Cifratura del messaggio</string>
<string name="not_fetching_history_retention_period">Nessun recupero di messaggi a causa del periodo di conservazione locale.</string>
@@ -701,11 +706,11 @@
<string name="disable_now">Disattiva adesso</string>
<string name="draft">Bozza:</string>
<string name="pref_omemo_setting">Cifratura OMEMO</string>
- <string name="pref_omemo_setting_summary_always">OMEMO verrà sempre usato per chat singole e di gruppi privati.</string>
+ <string name="pref_omemo_setting_summary_always">OMEMO verrà sempre usato per chat singole e gruppi privati.</string>
<string name="pref_omemo_setting_summary_default_on">OMEMO verrà usato in modo predefinito nelle nuove conversazioni.</string>
<string name="pref_omemo_setting_summary_default_off">OMEMO dovrà essere attivato a mano nelle nuove conversazioni.</string>
<string name="create_shortcut">Crea scorciatoia</string>
- <string name="pref_font_size">Dimensione carattere</string>
+ <string name="pref_font_size">Dimensione dei caratteri</string>
<string name="pref_font_size_summary">La dimensione dei caratteri usata all\'interno dell\'app.</string>
<string name="default_on">On in modo predefinito</string>
<string name="default_off">Off in modo predefinito</string>
@@ -751,7 +756,7 @@
<string name="foreground_service_channel_description">Questa categoria di notifiche è usata per mostrare una notifica permanente per indicare che %1$s è in esecuzione.</string>
<string name="notification_group_status_information">Informazioni di stato</string>
<string name="error_channel_name">Problemi di connettività</string>
- <string name="error_channel_description">Questa categoria di notifiche è usata per mostrare un notifica in caso si verifichi un problema nella connessione ad un account.</string>
+ <string name="error_channel_description">Questa categoria di notifiche è usata per mostrare un notifica in caso si verifichi un problema nella connessione ad un profilo.</string>
<string name="notification_group_messages">Messaggi</string>
<string name="notification_group_calls">Chiamate</string>
<string name="messages_channel_name">Messaggi</string>
@@ -768,7 +773,7 @@
<string name="group_chat_members">Partecipanti</string>
<string name="media_browser">Browser multimediale</string>
<string name="security_violation_not_attaching_file">File omesso per violazione di sicurezza.</string>
- <string name="pref_video_compression">Qualità video</string>
+ <string name="pref_video_compression">Qualità dei video</string>
<string name="pref_video_compression_summary">Una qualità inferiore comporta file più piccoli</string>
<string name="video_360p">Media (360p)</string>
<string name="video_720p">Alta (720p)</string>
@@ -829,10 +834,10 @@
<string name="video_original">Originale (non compresso)</string>
<string name="open_with">Apri con…</string>
<string name="set_profile_picture">Immagine profilo di Conversations</string>
- <string name="choose_account">Scegli account</string>
+ <string name="choose_account">Scegli un profilo</string>
<string name="restore_backup">Ripristina backup</string>
<string name="restore">Ripristina</string>
- <string name="enter_password_to_restore">Inserisci la tua password per l\'account %s per ripristinare il backup.</string>
+ <string name="enter_password_to_restore">Inserisci la tua password per il profilo %s per ripristinare il backup.</string>
<string name="restore_warning">Non usare la funzione di ripristino del backup tentando di clonare (eseguire simultaneamente) un\'installazione. Il ripristino di un backup è inteso solo per migrazioni o in caso di smarrimento del dispositivo.</string>
<string name="unable_to_restore_backup">Impossibile ripristinare il backup.</string>
<string name="unable_to_decrypt_backup">Impossibile decifrare il backup. La password è giusta?</string>
@@ -869,10 +874,10 @@
<string name="discover_channels">Individua i canali</string>
<string name="search_channels">Cerca i canali</string>
<string name="channel_discovery_opt_in_title">Possibile violazione della privacy!</string>
- <string name="channel_discover_opt_in_message"><![CDATA[La scoperta dei canali usa un servizio di terze parti chiamato <a href="https://search.jabber.network">search.jabber.network</a>.<br><br>L\'uso di questa funzione trasmetterà il tuo indirizzo IP e i termini di ricerca a quel servizio. Vedi la loro <a href="https://search.jabber.network/privacy">Informativa sulla Privacy</a> per saperne di più.]]></string>
- <string name="i_already_have_an_account">Ho già un account</string>
- <string name="add_existing_account">Aggiungi un account pre-esistente</string>
- <string name="register_new_account">Registra un nuovo account</string>
+ <string name="channel_discover_opt_in_message"><![CDATA[La ricerca dei canali usa un servizio di terze parti chiamato <a href=\"https://search.jabber.network\">search.jabber.network</a>.<br><br>L\'uso di questa funzione trasmetterà il tuo indirizzo IP e i termini di ricerca a quel servizio. Vedi la loro <a href=\"https://search.jabber.network/privacy\">informativa sulla privacy</a> per maggiori informazioni.]]></string>
+ <string name="i_already_have_an_account">Ho già un profilo</string>
+ <string name="add_existing_account">Aggiungi un profilo esistente</string>
+ <string name="register_new_account">Registra un nuovo profilo</string>
<string name="this_looks_like_a_domain">Questo sembra un indirizzo di dominio</string>
<string name="add_anway">Aggiungere comunque</string>
<string name="this_looks_like_channel">Questo sembra un indirizzo di canale</string>
@@ -881,8 +886,8 @@
<string name="event">Evento</string>
<string name="open_backup">Apri backup</string>
<string name="not_a_backup_file">Il file selezionato non è un file di backup di Conversations</string>
- <string name="account_already_setup">Questo account è già stato configurato</string>
- <string name="please_enter_password">Inserisci la password per questo account</string>
+ <string name="account_already_setup">Questo profilo è già stato configurato</string>
+ <string name="please_enter_password">Inserisci la password per questo profilo</string>
<string name="unable_to_perform_this_action">Impossibile eseguire questa azione</string>
<string name="open_join_dialog">Entra in un canale pubblico...</string>
<string name="sharing_application_not_grant_permission">L\'app di condivisione non ha concesso l\'autorizzazione per accedere a questo file.</string>
@@ -890,10 +895,10 @@
<string name="jabber_network">jabber.network</string>
<string name="local_server">Server locale</string>
<string name="pref_channel_discovery_summary">La maggior parte degli utenti dovrebbe scegliere ‘jabber.network’ per migliori suggerimenti dall\'intero ecosistema XMPP pubblico.</string>
- <string name="pref_channel_discovery">Metodo di scoperta canali</string>
+ <string name="pref_channel_discovery">Metodo di scoperta dei canali</string>
<string name="backup">Backup</string>
<string name="category_about">Al riguardo</string>
- <string name="please_enable_an_account">Devi attivare un account</string>
+ <string name="please_enable_an_account">Devi attivare un profilo</string>
<string name="make_call">Chiama</string>
<string name="rtp_state_incoming_call">Chiamata in arrivo</string>
<string name="rtp_state_incoming_video_call">Chiamata video in arrivo</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>
@@ -958,6 +964,9 @@
<string name="invite_to_app">Invita su Conversations</string>
<string name="unable_to_parse_invite">Impossibile analizzare l\'invito</string>
<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="no_active_accounts_support_this">Nessun profilo attivo supporta questa funzione</string>
<string name="backup_started_message">Il backup è iniziato. Riceverai una notifica una volta completato.</string>
- </resources>
+ <string name="unable_to_enable_video">Impossibile attivare il video.</string>
+ <string name="plain_text_document">Documento di testo</string>
+
+</resources>
@@ -27,31 +27,31 @@
<string name="title_activity_share_via_account">アカウントで共有</string>
<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="minute_ago">1分前</string>
+ <string name="minutes_ago">%d分前</string>
<plurals name="x_unread_conversations">
- <item quantity="other">未読%d件</item>
+ <item quantity="other">%d件の未読の会話</item>
</plurals>
<string name="sending">送信中…</string>
<string name="message_decrypting">メッセージを復号しています。しばらくお待ちください…</string>
<string name="pgp_message">OpenPGP 暗号化メッセージ</string>
<string name="nick_in_use">ニックネームは既に使用されています</string>
- <string name="invalid_muc_nick">正しくないニックネームです</string>
+ <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>
<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="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="register_account">サーバーに新規アカウントを登録</string>
<string name="change_password_on_server">サーバーのパスワードを変更</string>
<string name="share_with">…で共有</string>
<string name="start_conversation">会話を始める</string>
@@ -59,7 +59,7 @@
<string name="invite">招待</string>
<string name="contacts">連絡先</string>
<string name="contact">連絡先</string>
- <string name="cancel">キャンセル</string>
+ <string name="cancel">中止</string>
<string name="set">設定</string>
<string name="add">追加</string>
<string name="edit">編集</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>
@@ -105,9 +105,9 @@
<string name="openkeychain_not_installed">OpenKeychain をインストールしてください</string>
<string name="offering">依頼中…</string>
<string name="waiting">待機中…</string>
- <string name="no_pgp_key">OpenPGP の鍵はありません</string>
+ <string name="no_pgp_key">OpenPGP 鍵が見つかりません</string>
<string name="contact_has_no_pgp_key">連絡先が公開鍵を通知しないため、あなたのメッセージを暗号化することができません。\n\n<small>連絡先に OpenPGP をセットアップするように依頼してください。</small></string>
- <string name="no_pgp_keys">OpenPGP の鍵はありません</string>
+ <string name="no_pgp_keys">OpenPGP 鍵が見つかりません</string>
<string name="contacts_have_no_pgp_keys">連絡先が公開鍵を通知しないため、あなたのメッセージを暗号化することができません。\n\n<small>連絡先に OpenPGP をセットアップするように依頼してください。</small></string>
<string name="pref_general">全般</string>
<string name="pref_accept_files">ファイルを受取</string>
@@ -115,9 +115,9 @@
<string name="pref_attachments">添付ファイル</string>
<string name="pref_notification_settings">通知</string>
<string name="pref_vibrate">振動</string>
- <string name="pref_vibrate_summary">新しいメッセージが届いたときに振動します</string>
+ <string name="pref_vibrate_summary">新着メッセージが届いたときに振動します</string>
<string name="pref_led">LED 通知</string>
- <string name="pref_led_summary">新しいメッセージが届いたときに通知ライトを点滅します</string>
+ <string name="pref_led_summary">新着メッセージが届いたときに通知ライトを点滅します</string>
<string name="pref_ringtone">着信音</string>
<string name="pref_notification_sound">通知音</string>
<string name="pref_notification_sound_summary">新着メッセージの通知音</string>
@@ -126,27 +126,30 @@
<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_confirm_messages_summary">あなたがメッセージを受信して読んだときに、連絡先に知らせます</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>
- <string name="accept">受付</string>
+ <string name="accept">受け入れる</string>
<string name="error">エラーが発生しました</string>
<string name="recording_error">エラー</string>
<string name="your_account">あなたのアカウント</string>
- <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="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_take_picture">写真を撮影</string>
- <string name="preemptively_grant">事前にサブスクリプション要求を許可する</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_invalid_token">トークンが無効です</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>
@@ -170,13 +174,13 @@
<string name="encryption_choice_pgp">OpenPGP</string>
<string name="encryption_choice_omemo">OMEMO</string>
<string name="mgmt_account_delete">アカウントを削除</string>
- <string name="mgmt_account_disable">一時的に無効にする</string>
+ <string name="mgmt_account_disable">一時的に無効化</string>
<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_enable">アカウントを有効化</string>
<string name="mgmt_account_are_you_sure">よろしいですか?</string>
<string name="mgmt_account_delete_confirm_text">アカウントを削除すると会話履歴がすべて消去されます</string>
<string name="attach_record_voice">音声を録音</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>
@@ -202,14 +206,14 @@
<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_mins">%d 分前に会いました</string>
- <string name="last_seen_hour">1 時間前に会いました</string>
- <string name="last_seen_hours">%d 時間前に会いました</string>
- <string name="last_seen_day">1 日前に会いました</string>
- <string name="last_seen_days">%d 日前に会いました</string>
+ <string name="last_seen_min">1分前に会いました</string>
+ <string name="last_seen_mins">%d分前に会いました</string>
+ <string name="last_seen_hour">1時間前に会いました</string>
+ <string name="last_seen_hours">%d時間前に会いました</string>
+ <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>
@@ -247,32 +251,32 @@
<string name="add_back">戻りを追加</string>
<string name="contact_has_read_up_to_this_point">%s はここまで読みました</string>
<string name="contacts_have_read_up_to_this_point">%s はここまで読みました</string>
- <string name="contacts_and_n_more_have_read_up_to_this_point">%1$s +%2$d 全員ここまで読みました</string>
+ <string name="contacts_and_n_more_have_read_up_to_this_point">%1$s +%2$d人がここまで読みました</string>
<string name="everyone_has_read_up_to_this_point">全員がここまで読みました</string>
<string name="publish">公開</string>
<string name="touch_to_choose_picture">アバターをタップしてギャラリーから画像を選択します</string>
<string name="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>
<string name="server_info_session_established">セッションが確立</string>
<string name="skip">スキップ</string>
- <string name="disable_notifications">通知を無効にする</string>
+ <string name="disable_notifications">通知を無効化</string>
<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_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="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>
@@ -280,42 +284,42 @@
<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_enable_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_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_resource_constraint">リソース制限</string>
<string name="conference_kicked">このグループチャットから蹴り出されています</string>
<string name="conference_shutdown">このグループチャットは閉鎖されました</string>
- <string name="conference_unknown_error">既にこのグループチャットに参加していません</string>
+ <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>
- <string name="try_again">再度実行してください</string>
+ <string name="try_again">再試行</string>
<string name="pref_keep_foreground_service">フォアグラウンドサービス</string>
<string name="pref_keep_foreground_service_summary">オペレーティングシステムが接続を切断するのを防止します</string>
<string name="pref_create_backup">バックアップを作成</string>
@@ -325,7 +329,7 @@
<string name="notification_backup_created_subtitle">バックアップファイルは %s に保存されました</string>
<string name="restoring_backup">バックアップを復元</string>
<string name="notification_restored_backup_title">バックアップを復元しました</string>
- <string name="notification_restored_backup_subtitle">アカウントを有効にしてください。</string>
+ <string name="notification_restored_backup_subtitle">アカウントを有効化してください。</string>
<string name="choose_file">ファイルを選択</string>
<string name="receiving_x_file">%1$s 受信中 (%2$d%% 完了)</string>
<string name="download_x_file">%s をダウンロード</string>
@@ -335,37 +339,37 @@
<string name="sending_file">送信中 (%1$d%% 完了)</string>
<string name="preparing_file">転送用ファイルの準備中</string>
<string name="x_file_offered_for_download">%s ダウンロード依頼中</string>
- <string name="cancel_transmission">転送をキャンセル</string>
+ <string name="cancel_transmission">転送を中止</string>
<string name="file_transmission_failed">ファイル転送に失敗しました</string>
- <string name="file_transmission_cancelled">転送をキャンセルしました</string>
+ <string name="file_transmission_cancelled">ファイル転送を中止しました</string>
<string name="file_deleted">ファイルを削除しました</string>
<string name="no_application_found_to_open_file">ファイルを開くアプリケーションが見つかりません</string>
<string name="no_application_found_to_open_link">リンクを開くアプリケーションが見つかりません</string>
<string name="no_application_found_to_view_contact">連絡先を表示するアプリケーションが見つかりません</string>
<string name="pref_show_dynamic_tags">ダイナミック タグ</string>
<string name="pref_show_dynamic_tags_summary">連絡先の下に、読み取り専用タグを表示します</string>
- <string name="enable_notifications">通知を有効にする</string>
+ <string name="enable_notifications">通知を有効化</string>
<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>
<string name="current_password">現在のパスワード</string>
<string name="new_password">新しいパスワード</string>
<string name="password_should_not_be_empty">パスワードは空にできません</string>
- <string name="enable_all_accounts">すべてのアカウントを有効にする</string>
- <string name="disable_all_accounts">すべてのアカウントを無効にする</string>
+ <string name="enable_all_accounts">すべてのアカウントを有効化</string>
+ <string name="disable_all_accounts">すべてのアカウントを無効化</string>
<string name="perform_action_with">アクションを実行...</string>
<string name="no_affiliation">所属なし</string>
<string name="no_role">オフライン</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>
@@ -392,21 +396,22 @@
<string name="non_anonymous">XMPPアドレスを誰でも見れるようにする</string>
<string name="moderated">談話室の調停をする</string>
<string name="you_are_not_participating">あなたは参加していません</string>
- <string name="modified_conference_options">グループチャットのオプションが変更されました!</string>
- <string name="could_not_modify_conference_options">グループチャットのオプションを変更できませんでした</string>
+ <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="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>
@@ -419,7 +424,7 @@
<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="pref_chat_states_summary">あなたがメッセージを書いているときに、連絡先に知らせる</string>
<string name="send_location">位置を送信</string>
<string name="show_location">位置を表示</string>
<string name="no_application_found_to_display_location">位置を表示するアプリケーションが見つかりません</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,27 +461,27 @@
<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="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>
+ <string name="certificate_chain_is_not_trusted">信頼できない証明書チェーン</string>
<string name="jid_does_not_match_certificate">XMPP アドレスが証明書と一致しません</string>
<string name="action_renew_certificate">証明書を更新</string>
<string name="error_fetching_omemo_key">OMEMO 鍵の取得中にエラー!</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,8 +518,8 @@
<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_dialog">お使いのデバイスは、%1$s で通知の遅延やメッセージの損失につながる可能性のある、重い電池の最適化を使用しています。\n\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>
<string name="no_accounts">(アクティベートしたアカウントはありません)</string>
@@ -522,28 +527,28 @@
<string name="correct_message">メッセージを修正</string>
<string name="send_corrected_message">修正したメッセージを送信</string>
<string name="no_keys_just_confirm">あなたは信頼を確認するために、この人の指紋を安全に検証しました。“完了”を選択すると、 %s がこのグループチャットの一員であることを確認したことになります。</string>
- <string name="this_account_is_disabled">このアカウントを無効にしました</string>
+ <string name="this_account_is_disabled">このアカウントを無効化しました</string>
<string name="security_error_invalid_file_access">セキュリティエラー: 不正なファイルアクセス!</string>
<string name="no_application_to_share_uri">URI を共有するアプリが見つかりません</string>
<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>
+ <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>
- <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>
- <string name="registration_please_wait">登録に失敗しました: 後でもう一度試してください</string>
+ <string name="registration_please_wait">登録に失敗しました: 後でもう一度お試しください</string>
<string name="registration_password_too_weak">登録に失敗しました: パスワードが弱すぎます</string>
<string name="choose_participants">参加者を選択</string>
<string name="creating_conference">グループチャットを作成しています…</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>
@@ -567,16 +572,16 @@
<string name="type_pc">コンピューター</string>
<string name="type_phone">携帯電話</string>
<string name="type_tablet">タブレット</string>
- <string name="type_web">Web ブラウザー</string>
+ <string name="type_web">ウェブブラウザ</string>
<string name="type_console">コンソール</string>
<string name="payment_required">支払が必要です</string>
<string name="missing_internet_permission">インターネット使用権限の付与</string>
<string name="me">自分</string>
- <string name="contact_asks_for_presence_subscription">連絡先が、参加サブスクリプションを問い合わせしています</string>
+ <string name="contact_asks_for_presence_subscription">連絡先が出席情報サブスクリプションを求めています</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>
+ <string name="remote_server_timeout">リモートサーバーがタイムアウト</string>
<string name="unable_to_update_account">アカウントを更新できません</string>
<string name="report_jid_as_spammer">この XMPP アドレスをスパムとして報告する。</string>
<string name="pref_delete_omemo_identities">OMEMO ID を削除</string>
@@ -585,29 +590,29 @@
<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="data_saver_enabled_explained">お使いのオペレーティングシステムは、%1$s がバックグラウンドのときにインターネットにアクセスすることを制限しています。新しいメッセージの通知を受信するには、“データセーバー”がオンになっているとき、%1$s に無制限のアクセスを許可する必要があります。\n%1$s は可能なときにデータを保存するための努力をします。</string>
+ <string name="data_saver_enabled">データセーバーを有効化しました</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="pref_blind_trust_before_verification">検証前に白紙信託する</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="not_trusted">信頼できない</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>
@@ -642,21 +647,21 @@
<string name="contact_blocked_past_tense">連絡先をブロックしました</string>
<string name="pref_notifications_from_strangers">見知らぬ人からの通知</string>
<string name="pref_notifications_from_strangers_summary">見知らぬ人から受信したメッセージと通話を通知します。</string>
- <string name="received_message_from_stranger">見知らぬ人からメッセージを受け取りました</string>
+ <string name="received_message_from_stranger">見知らぬ人からメッセージを受信しました</string>
<string name="block_stranger">見知らぬ人をブロック</string>
<string name="block_entire_domain">ドメイン全体をブロック</string>
<string name="online_right_now">今すぐオンライン</string>
<string name="retry_decryption">復号を再試行</string>
<string name="session_failure">セッション失敗</string>
<string name="sasl_downgrade">ダウングレードされた SASL メカニズム</string>
- <string name="account_status_regis_web">サーバーはWebサイトでの登録が必要です</string>
- <string name="open_website">Webサイトを開く</string>
- <string name="application_found_to_open_website">Webサイトを開くアプリが見つかりません</string>
+ <string name="account_status_regis_web">サーバーはウェブサイトでの登録が必要です</string>
+ <string name="open_website">ウェブサイトを開く</string>
+ <string name="application_found_to_open_website">ウェブサイトを開くアプリが見つかりません</string>
<string name="pref_headsup_notifications">Heads-up 通知</string>
<string name="pref_headsup_notifications_summary">Heads-up 通知を表示</string>
<string name="today">今日</string>
<string name="yesterday">昨日</string>
- <string name="pref_validate_hostname">DNSSEC でホスト名を検証</string>
+ <string name="pref_validate_hostname">DNSSEC でホスト名の妥当性を確認</string>
<string name="pref_validate_hostname_summary">検証されたホスト名を含むサーバー証明書は検証済みと見なされます</string>
<string name="certificate_does_not_contain_jid">証明書は XMPP アドレスを含みません</string>
<string name="server_info_partial">一時的</string>
@@ -664,12 +669,12 @@
<string name="copy_to_clipboard">クリップボードにコピー</string>
<string name="message_copied_to_clipboard">メッセージをクリップボードにコピーしました</string>
<string name="message">メッセージ</string>
- <string name="private_messages_are_disabled">非公開メッセージを無効にしました</string>
+ <string name="private_messages_are_disabled">非公開メッセージを無効化しました</string>
<string name="huawei_protected_apps">保護されたアプリ</string>
- <string name="huawei_protected_apps_summary">画面がオフになっている場合でも通知を受信し続けるには、保護されたアプリの一覧に Conversations を追加する必要があります。</string>
+ <string name="huawei_protected_apps_summary">画面がオフになっているときでも通知を受信し続けるには、保護されたアプリの一覧に Conversations を追加する必要があります。</string>
<string name="mtm_accept_cert">未知の証明書を受け入れますか?</string>
<string name="mtm_trust_anchor">サーバー証明書が既知の認証局によって署名されていません。</string>
- <string name="mtm_accept_servername">不一致なサーバー名を受け入れますか?</string>
+ <string name="mtm_accept_servername">不一致のサーバー名を受け入れますか?</string>
<string name="mtm_hostname_mismatch">サーバーは\"%s\"として認証できませんでした。証明書は次の場合にのみ有効です:</string>
<string name="mtm_connect_anyway">それでも接続を希望しますか?</string>
<string name="mtm_cert_details">証明書の詳細:</string>
@@ -679,12 +684,12 @@
<string name="pref_scroll_to_bottom_summary">メッセージ送信後に下へスクロール</string>
<string name="edit_status_message_title">ステータスメッセージを編集</string>
<string name="edit_status_message">ステータスメッセージを編集</string>
- <string name="disable_encryption">暗号化をしない</string>
+ <string name="disable_encryption">暗号化が無効</string>
<string name="error_trustkey_general">%1$s は %2$s に暗号化メッセージを送れません。連絡先が利用しているサーバーが古すぎるか、クライアントが OMEMO を扱えません。</string>
<string name="error_trustkey_device_list">デバイスの一覧を取得できません</string>
<string name="error_trustkey_bundle">暗号化の鍵を取得できません</string>
<string name="error_trustkey_hint_mutual">ヒント: お互いが連絡先名簿に加えれば解決するでしょう。</string>
- <string name="disable_encryption_message">この会話でOMEMOの暗号化を無効にしてよろしいですか?\n\nこれにより、サーバー管理者がメッセージを読むことが可能になりますが、時代遅れのクライアントを使っている人と連絡をとるには、この方法しかないかもしれません。</string>
+ <string name="disable_encryption_message">この会話で OMEMO の暗号化を無効化してもよろしいですか?\nこれにより、サーバー管理者がメッセージを読むことが可能になりますが、時代遅れのクライアントを使っている人と連絡をとるには、この方法しかないかもしれません。</string>
<string name="disable_now">今すぐ無効化</string>
<string name="draft">下書き:</string>
<string name="pref_omemo_setting">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>
@@ -732,20 +737,22 @@
<string name="providing_a_name_is_optional">名前の記入は任意です</string>
<string name="create_dialog_group_chat_name">グループチャット名</string>
<string name="conference_destroyed">このグループチャットは破棄されました</string>
+ <string name="unable_to_save_recording">録音を保存できません</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>
<string name="notification_group_messages">メッセージ</string>
<string name="notification_group_calls">通話</string>
<string name="messages_channel_name">メッセージ</string>
- <string name="incoming_calls_channel_name">着信</string>
- <string name="ongoing_calls_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>
- <string name="pref_incoming_call_notification_settings">通話着信の通知設定</string>
+ <string name="pref_incoming_call_notification_settings">着信通話の通知設定</string>
<string name="pref_more_notification_settings_summary">重要性、音、振動</string>
<string name="video_compression_channel_name">ビデオの圧縮</string>
<string name="view_media">メディアを表示</string>
@@ -756,8 +763,9 @@
<string name="pref_video_compression_summary">質が低い程、ファイルは小さくなります</string>
<string name="video_360p">中 (360p)</string>
<string name="video_720p">高 (720p)</string>
+ <string name="cancelled">中止しました</string>
<string name="already_drafting_message">あなたは既にメッセージを作成中です。</string>
- <string name="feature_not_implemented">未実装の機能</string>
+ <string name="feature_not_implemented">実装されてない機能</string>
<string name="invalid_country_code">不正な国コード</string>
<string name="choose_a_country">国を選択</string>
<string name="phone_number">電話番号</string>
@@ -777,7 +785,7 @@
<string name="back">戻る</string>
<string name="possible_pin">クリップボードから可能な pin を自動的に貼り付ける。</string>
<string name="please_enter_pin">6桁の pin を入力してください。</string>
- <string name="abort_registration_procedure">本当に登録手続きを中止してもよろしいのですか?</string>
+ <string name="abort_registration_procedure">登録手続きを中止してもよろしいのですか?</string>
<string name="yes">はい</string>
<string name="no">いいえ</string>
<string name="verifying">検証しています…</string>
@@ -791,7 +799,7 @@
<string name="unable_to_find_server">サーバーが見つかりません。</string>
<string name="something_went_wrong_processing_your_request">要求の処理中に、何か問題が発生しました。</string>
<string name="invalid_user_input">無効なユーザーの入力</string>
- <string name="temporarily_unavailable">一時的に入手不可能です。後でもう一度試してください。</string>
+ <string name="temporarily_unavailable">一時的に入手不可能です。後でもう一度お試しください。</string>
<string name="no_network_connection">ネットワーク接続なし。</string>
<string name="try_again_in_x">%s でもう一度お試しください。</string>
<string name="rate_limited">上限に到達しました</string>
@@ -807,11 +815,11 @@
<string name="install_orbot">Orbot をインストール</string>
<string name="start_orbot">Orbot を開始</string>
<string name="no_market_app_installed">マーケットアプリがインストールされていません。</string>
- <string name="group_chat_will_make_your_jabber_id_public">この談話室では、あなたのXMPPアドレスを公開します</string>
+ <string name="group_chat_will_make_your_jabber_id_public">この談話室では、あなたの XMPP アドレスを公開します</string>
<string name="ebook">電子書籍</string>
- <string name="video_original">原物 (非圧縮)</string>
+ <string name="video_original">そのまま (非圧縮)</string>
<string name="open_with">…で開く</string>
- <string name="set_profile_picture">Conversations プロフィール写真</string>
+ <string name="set_profile_picture">Conversations プロフィール画像</string>
<string name="choose_account">アカウントを選択</string>
<string name="restore_backup">バックアップを復元</string>
<string name="restore">復元</string>
@@ -843,19 +851,19 @@
<string name="anyone_can_invite_others">誰でも他の人を招待できます。</string>
<string name="jabber_ids_are_visible_to_admins">XMPP アドレスは管理者が見れます。</string>
<string name="jabber_ids_are_visible_to_anyone">XMPP アドレスは誰でも見れます。</string>
- <string name="no_users_hint_channel">この公開談話室には参加者がいません。連絡先を招待したり、共有ボタンを使って XMPP アドレスを配布できます。</string>
+ <string name="no_users_hint_channel">この公開談話室には参加者がいません。連絡先を招待したり、共有ボタンを使用して XMPP アドレスを配布できます。</string>
<string name="no_users_hint_group_chat">この非公開グループチャットには参加者がいません。</string>
<string name="manage_permission">権限を管理</string>
<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="add_existing_account">存在するアカウントを追加</string>
- <string name="register_new_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>
<string name="add_anway">とにかく追加</string>
<string name="this_looks_like_channel">これは談話室アドレスのようです</string>
@@ -868,16 +876,17 @@
<string name="please_enter_password">このアカウントのパスワートを入力してください</string>
<string name="unable_to_perform_this_action">この操作を実行できません</string>
<string name="open_join_dialog">公開談話室に参加…</string>
- <string name="sharing_application_not_grant_permission">共有アプリがこのファイルへのアクセスを許可していませんでした。</string>
+ <string name="sharing_application_not_grant_permission">共有アプリがこのファイルへのアクセス権限を付与していませんでした。</string>
<string name="group_chats_and_channels"><![CDATA[グループチャットと談話室]]></string>
<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="please_enable_an_account">アカウントを有効にしてください</string>
+ <string name="pref_channel_discovery">談話室の発見方法</string>
+ <string name="backup">バックアップ</string>
+ <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_incoming_call">着信通話</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>
@@ -890,21 +899,23 @@
<string name="rtp_state_connectivity_error">通話に接続できません</string>
<string name="rtp_state_connectivity_lost_error">接続切断</string>
<string name="rtp_state_retracted">撤回された通話</string>
- <string name="hang_up">ハングアップ</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>
- <string name="disable_tor_to_make_call">通話するのに Tor を無効にする</string>
- <string name="incoming_call">通話着信</string>
- <string name="incoming_call_duration">通話着信・%s</string>
- <string name="missed_call_timestamp">不在通話着信・%s</string>
- <string name="outgoing_call">通話発信</string>
- <string name="outgoing_call_duration">通話発信・%s</string>
- <string name="missed_call">不在通話着信</string>
+ <string name="disable_tor_to_make_call">通話するのに Tor を無効化</string>
+ <string name="incoming_call">着信通話</string>
+ <string name="incoming_call_duration">着信通話・%s</string>
+ <string name="missed_call_timestamp">不在着信通話・%s</string>
+ <string name="outgoing_call">発信通話</string>
+ <string name="outgoing_call_duration">発信通話・%s</string>
+ <string name="missed_call">不在着信通話</string>
<string name="audio_call">音声通話</string>
<string name="video_call">映像通話</string>
<string name="switch_to_conversation">会話に切り替え</string>
<string name="microphone_unavailable">マイクが利用できません</string>
- <string name="only_one_call_at_a_time">1回につき1回線のみ</string>
+ <string name="only_one_call_at_a_time">1度に1回線の通話のみ。</string>
<string name="return_to_ongoing_call">継続中の通話に戻る</string>
<string name="could_not_switch_camera">カメラを切り替えできません</string>
<string name="add_to_favorites">最上に留める</string>
@@ -934,7 +945,10 @@
<string name="no_application_found">アプリケーションが見つかりません</string>
<string name="invite_to_app">会話に招待</string>
<string name="unable_to_parse_invite">招待を解析できません</string>
- <string name="server_does_not_support_easy_onboarding_invites">サーバーは招待をサポートしていません</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>
- </resources>
+ <string name="unable_to_enable_video">映像を有効化できません。</string>
+ <string name="plain_text_document">プレーンテキスト文書</string>
+
+</resources>
@@ -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>
@@ -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>
- </resources>
+ <string name="unable_to_enable_video">Nie można włączyć wideo. </string>
+ <string name="plain_text_document">Dokument zwykłego tekstu</string>
+
+</resources>
@@ -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>
@@ -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>
- </resources>
+ <string name="unable_to_enable_video">Nu s-a putut activa camera video.</string>
+ <string name="plain_text_document">Document text</string>
+
+</resources>
@@ -138,6 +138,8 @@
<string name="pref_never_send_crash_summary">Отправляя отчеты об ошибках, вы помогаете разработке этого приложения</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>
@@ -156,6 +158,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 +173,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>
@@ -418,6 +422,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</string>
<string name="vcard">Контакт</string>
@@ -986,4 +991,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>
- </resources>
+ <string name="unable_to_enable_video">Невозможно включить видео.</string>
+ <string name="plain_text_document">Текстовые данные</string>
+
+</resources>
@@ -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,73 @@
<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_notification_grace_period_summary">Doba, počas ktorej budú oznámenia stíšené po detekcii aktivity na jednom z vašich ostatných zariadení.</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_prevent_screenshots">Zakázať snímok obrazovky</string>
+ <string name="pref_prevent_screenshots_summary">Skryje obsah aplikácie v posledných aplikáciách a zablokuje snímky obrazovky.</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 +152,11 @@
<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_during_image_copy">Aplikácia, ktorú ste použili pre výber obrázka neposkytla dostatočné oprávnenia na prečítanie súboru.\n\n<small>Skúste použiť iného správcu súborov pre výber obrázka</small></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 +168,11 @@
<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">Nadviazanie spojenia TLS zlyhalo</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>
@@ -103,11 +182,17 @@
<string name="mgmt_account_disable">Dočasne vypnúť</string>
<string name="mgmt_account_publish_avatar">Zverejniť avatar</string>
<string name="mgmt_account_publish_pgp">Zverejniť OpenPGP kľúč</string>
+ <string name="unpublish_pgp">Odstrániť OpenPGP verejný kľúč</string>
<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,82 +200,157 @@
<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="enter_contact">Vložiť Kontakt</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="destroy_room_dialog">Ste si istý, že chcete zrušiť tento skupinový rozhovor?\n\n<b>Upozornenie:</b> Skupinový rozhovor bude kompletne vymazaný zo servera.</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>
<string name="connect">Pripojiť </string>
<string name="account_already_exists">Tento účet už existuje</string>
<string name="next">Ďalší</string>
+ <string name="server_info_session_established">Spojenie naviazané</string>
<string name="skip">Preskočiť</string>
<string name="disable_notifications">Vypnúť upozornenia</string>
<string name="enable">Povoliť</string>
+ <string name="conference_requires_password">Skupinový rozhovor požaduje heslo</string>
<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_kicked">Boli ste vyhodení z tohto skupinového rozhovoru</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">Služba v popredí</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="pref_create_backup_summary">Súbory zálohy budú uložené v %s</string>
+ <string name="notification_create_backup_title">Vytváram súbor zálohy</string>
+ <string name="notification_backup_created_title">Vaša záloha bola vytvorená</string>
+ <string name="notification_backup_created_subtitle">Súbor zálohy bol uložený v %s</string>
+ <string name="restoring_backup">Obnovujem zálohu</string>
+ <string name="notification_restored_backup_title">Vaša záloha bola obnovená</string>
+ <string name="notification_restored_backup_subtitle">Nezabudnite si zapnúť konto.</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>
+ <string name="delete_x_file">Zmazať %s</string>
<string name="file">súbor</string>
<string name="open_x_file">Otvoriť %s</string>
<string name="sending_file">posielam (%1$d%% ukončený)</string>
+ <string name="preparing_file">Pripravuje sa zdieľanie súboru</string>
<string name="x_file_offered_for_download">%s ponúknutý na stiahnutie </string>
<string name="cancel_transmission">Zrušiť prenos</string>
- <string name="pref_show_dynamic_tags_summary">Zobraziť etikety na čítanie pod kontakty</string>
+ <string name="file_transmission_failed">Nedá sa zdieľať súbor</string>
+ <string name="file_transmission_cancelled">prenos súboru zrušený</string>
+ <string name="file_deleted">Súbor zmazaný</string>
+ <string name="no_application_found_to_open_file">Nebola nájdená aplikácia na otvorenie súboru</string>
+ <string name="no_application_found_to_open_link">Nebola nájdená aplikácia na otvorenie odkazu</string>
+ <string name="no_application_found_to_view_contact">Nebola nájdená aplikácia na prezretie kontaktu</string>
+ <string name="pref_show_dynamic_tags">Dynamické štítky</string>
+ <string name="pref_show_dynamic_tags_summary">Zobraziť štítky pod kontaktmi</string>
<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>
@@ -228,8 +388,12 @@
<string name="sending_x_file">Posielam %s</string>
<string name="offering_x_file">Ponúkam %s</string>
<string name="hide_offline">Skryť neprihlásených</string>
+ <string name="contact_is_typing">%s píše...</string>
<string name="contact_has_stopped_typing">%s prestal písať</string>
+ <string name="contacts_are_typing">%s píšu...</string>
+ <string name="contacts_have_stopped_typing">%s prestali písať</string>
<string name="pref_chat_states">Upozornenia pri písaní</string>
+ <string name="pref_chat_states_summary">Dajte svojim kontaktom vedieť že im práve píšete správu.</string>
<string name="send_location">Poslať polohu</string>
<string name="show_location">Zobraziť polohu</string>
<string name="location">Poloha</string>
@@ -256,8 +420,111 @@
<string name="username">Užívateľské meno</string>
<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_add_account_with_certificate">Prihlásiť sa s certifikátom</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>
+ <plurals name="x_messages">
+ <item quantity="one">%dspráva</item>
+ <item quantity="few">%dsprávy</item>
+ <item quantity="many">%d správ</item>
+ <item quantity="other">%dspráv</item>
+ </plurals>
+ <string name="load_more_messages">Načítať viac správ</string>
+ <string name="shared_file_with_x">Súbor zdieľaný s %s</string>
+ <string name="shared_image_with_x">Obrázok zdieľaný s %s</string>
+ <string name="shared_images_with_x">Obrázky zdieľané s %s</string>
+ <string name="shared_text_with_x">Text zdieľaný s %s</string>
+ <string name="notify_on_all_messages">Oznamovať na všetkých správach</string>
+ <string name="always">Vždy</string>
+ <string name="disable">Deaktivovať</string>
+ <string name="no_accounts">(Žiadne aktivované účty)</string>
+ <string name="no_application_to_share_uri">Nebola nájdená žiadna aplikácia na zdieľanie URI</string>
+ <string name="share_uri_with">Zdieľať URI s...</string>
+ <string name="pref_manually_change_presence_summary">Nastaviť vašu dostupnosť pri úprave vašej status správy.</string>
<string name="presence_online">Online</string>
+ <string name="presence_dnd">Zaneprázdnený</string>
+ <string name="gp_disable">Deaktivovať</string>
+ <string name="pref_broadcast_last_activity">Oznamovať používanie</string>
+ <string name="pref_privacy">Súkromie</string>
+ <string name="pref_theme_options">Téma</string>
+ <string name="pref_theme_options_summary">Zvoľte si farebnú schému</string>
+ <string name="pref_use_green_background">Zelené pozadie</string>
+ <string name="pref_use_green_background_summary">Použiť zelené pozadie pre prijaté správy</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="delete_selected_keys">Odstrániť označené kľúče</string>
+ <string name="all_omemo_keys_have_been_verified">Overili ste všetky kľúče OMEMO vo vašom vlastníctve.</string>
+ <string name="share_as_barcode">Zdieľať ako qr kód</string>
+ <string name="share_as_uri">Zdieľať ako XMPP URI</string>
+ <string name="share_as_http">Zdieľať ako HTTP odkaz</string>
+ <string name="verify_omemo_keys">Overiť kľúče OMEMO</string>
+ <string name="pref_automatically_delete_messages_description">Automaticky vymazávať správy z tohto zariadenia, ktoré sú staršie ako nastavené časové obdobie.</string>
+ <string name="online_right_now">online práve teraz</string>
+ <string name="attach_record_video">Nahrať video</string>
+ <string name="copy_to_clipboard">Kopírovať do schránky</string>
<string name="message_copied_to_clipboard">Správa skopírovaná do schránky</string>
+ <string name="edit_status_message_title">Upraviť status správu</string>
+ <string name="edit_status_message">Upraviť status správu</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="default_on">Predvolene zapnuté</string>
+ <string name="default_off">Predvolene vypnuté</string>
+ <string name="omemo_decryption_failed">Nepodarilo sa dešifrovať OMEMO správu.</string>
+ <string name="action_share_location">Zdieľať Polohu</string>
+ <string name="title_activity_share_location">Zdieľať polohu</string>
<string name="title_activity_show_location">Zobraziť polohu</string>
+ <string name="share">Zdieľať</string>
+ <string name="search_messages">Prehľadávať správy</string>
+ <string name="pref_use_share_location_plugin">Plugin na Zdieľanie Polohy</string>
+ <string name="pref_use_share_location_plugin_summary">Používať Plugin na Zdieľanie Polohy namiesto vstavanej mapy.</string>
+ <string name="foreground_service_channel_name">Služba v popredí</string>
+ <string name="notification_group_messages">Správy</string>
+ <string name="notification_group_calls">Hovory</string>
+ <string name="messages_channel_name">Správy</string>
+ <string name="incoming_calls_channel_name">Prichádzajúce hovory</string>
+ <string name="ongoing_calls_channel_name">Prebiehajúce hovory</string>
+ <string name="delivery_failed_channel_name">Zlyhané doručenia</string>
+ <string name="pref_incoming_call_notification_settings">Nastavenia oznámení prichádzajúcich hovorov</string>
+ <string name="enter_your_name_instructions">Prosím vložte vaše meno, aby ľudia, ktorí vás nemajú v adresári, vedeli kto ste.</string>
+ <string name="your_name">Vaše meno</string>
+ <string name="enter_your_name">Vložte vaše meno</string>
+ <string name="no_name_set_instructions">Pre nastavenie vášho mena, použite tlačidlo Upraviť.</string>
+ <string name="restore_backup">Obnoviť zálohu</string>
+ <string name="xmpp_address">XMPP adresa</string>
+ <string name="attach">Priložiť</string>
+ <string name="share_backup_files">Zdieľať súbory záloh</string>
+ <string name="backup">Zálohy</string>
+ <string name="category_about">O aplikácii</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="answer_call">Prijať</string>
+ <string name="dismiss_call">Odmietnuť</string>
+ <string name="rtp_state_finding_device">Vyhľadávanie zariadení</string>
+ <string name="rtp_state_ringing">Zvoní</string>
+ <string name="rtp_state_declined_or_busy">Zaneprázdnený</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="add_to_favorites">Pripnúť na vrch</string>
+ <string name="remove_from_favorites">Odopnúť z vrchu</string>
+ <string name="encrypted_with_omemo">Zašifrované s OMEMO</string>
+ <string name="failed_deliveries">Zlyhané doručenia</string>
+ <string name="more_options">Viac možnosťí</string>
</resources>
@@ -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>
@@ -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>
@@ -63,11 +71,15 @@
<string name="unblock">Avblockera</string>
<string name="save">Spara</string>
<string name="ok">Ok</string>
+ <string name="crash_report_title">%1$s har kraschat</string>
+ <string name="crash_report_message">Att använda ditt XMPP-konto för att skicka in \'stack traces\' hjälper den pågående utvecklingen av %1$s.</string>
<string name="send_now">Skicka nu</string>
<string name="send_never">Fråga aldrig igen</string>
<string name="problem_connecting_to_account">Kunde inte ansluta till konto</string>
<string name="problem_connecting_to_accounts">Kunde inte ansluta till flera konton</string>
+ <string name="touch_to_fix">Tryck för att hantera dina konton</string>
<string name="attach_file">Bifoga fil</string>
+ <string name="not_in_roster">Vill du lägga till den här saknade kontakten i din kontaktlista?</string>
<string name="add_contact">Lägg till kontakt</string>
<string name="send_failed">sändning misslyckades</string>
<string name="preparing_image">Förbereder att skicka bild</string>
@@ -75,7 +87,9 @@
<string name="sharing_files_please_wait">Delar filer. Vänta...</string>
<string name="action_clear_history">Rensa historik</string>
<string name="clear_conversation_history">Rensa konversationshistorik</string>
+ <string name="clear_histor_msg">Vill du radera alla meddelanden i den här konversationen?\n\n<b>Varning:</b> Det här påverkar inte meddelanden som finns lagrade på andra enheter eller servrar.</string>
<string name="delete_file_dialog">Ta bort fil</string>
+ <string name="delete_file_dialog_msg">Är du säker på att du vill ta bort den här filen?\n\n<b>Varning:</b> Den här åtgärden kommer inte att ta bort kopior av den här filen som finns lagrad på andra enheter eller servrar.</string>
<string name="also_end_conversation">Stäng denna konversation efteråt</string>
<string name="choose_presence">Välj enhet</string>
<string name="send_unencrypted_message">Skicka okrypterat meddelande</string>
@@ -88,13 +102,16 @@
<string name="send_unencrypted">Skicka okrypterat</string>
<string name="decryption_failed">Avkryptering misslyckades. Du har kanske kanske inte rätt privat nyckel.</string>
<string name="openkeychain_required">OpenKeychain</string>
+ <string name="openkeychain_required_long"><![CDATA[%1$s använder <b>OpenKeychain</b> för att kryptera och avkryptera dina publika nycklar.<br><br>Programmet är licensierat under GPLv3+ och finns tillgänglig via F-Droid and Google Play.<br><br><small>(Var god och starta om %1$s efter installationen.)</small>]]></string>
<string name="restart">Starta om</string>
<string name="install">Installera</string>
<string name="openkeychain_not_installed">Installera OpenKeychain</string>
<string name="offering">erbjuder…</string>
<string name="waiting">väntar…</string>
<string name="no_pgp_key">Ingen OpenPGP-nyckel funnen</string>
+ <string name="contact_has_no_pgp_key">Det gick inte att kryptera ditt meddelande eftersom att din kontakt inte har annonserat sin publika nyckel.\n\n<small>Vänligen be din kontakt att sätta upp OpenPGP.</small></string>
<string name="no_pgp_keys">Inga OpenPGP-nycklar funna</string>
+ <string name="contacts_have_no_pgp_keys">Det gick inte att kryptera ditt meddelande eftersom att din kontakt inte har annonserat sina publika nycklar.\n\n<small>Vänligen be din kontakt att sätta upp OpenPGP.</small></string>
<string name="pref_general">Generellt</string>
<string name="pref_accept_files">Acceptera filer</string>
<string name="pref_accept_files_summary">Acceptera automatiskt filer som är mindre än…</string>
@@ -105,12 +122,20 @@
<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_notification_grace_period_summary">Tidsgräns för hur länge notiser ska tystas efter att aktivitet har upptäckts på en av dina andra enheter.</string>
<string name="pref_advanced_options">Avancerat</string>
<string name="pref_never_send_crash">Skicka aldrig krasch-rapporter</string>
+ <string name="pref_never_send_crash_summary">Genom att skicka in stack traces hjälper du utvecklingen</string>
<string name="pref_confirm_messages">Bekräfta meddelanden</string>
<string name="pref_confirm_messages_summary">Låt dina kontakter veta när du har mottagit och läst deras meddelanden</string>
+ <string name="pref_prevent_screenshots">Förhindra skärmdumpar</string>
+ <string name="pref_prevent_screenshots_summary">Dölj innehållet från applikationen i applikationsväxlaren och blockera skärmdumpar</string>
<string name="pref_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>
@@ -123,8 +148,11 @@
<string name="attach_take_picture">Ta ny bild</string>
<string name="preemptively_grant">Tillåt abonnemangsbegäran i förväg</string>
<string name="error_not_an_image_file">Filen du valt är inte en bild</string>
+ <string name="error_compressing_image">Det gick inte att konvertera bildfilen</string>
<string name="error_file_not_found">Filen hittas ej</string>
<string name="error_io_exception">Generellt I/O-fel. Du kanske fick slut på plats?</string>
+ <string name="error_security_exception_during_image_copy">Applikationen som du använde för att välja den här bilden tillhandahöll inte tillräckligt med rättigheter för att läsa filen.\n\n<small>Var god och använd en annan filhanterare för att välja en bild.</small></string>
+ <string name="error_security_exception">Applikationen du använde för att dela den här filen tillhandahöll inte tillräckligt med behörigheter.</string>
<string name="account_status_unknown">Okänd</string>
<string name="account_status_disabled">Tillfälligt inaktiverad</string>
<string name="account_status_online">Online</string>
@@ -136,10 +164,14 @@
<string name="account_status_regis_fail">Registreringsfel</string>
<string name="account_status_regis_conflict">Användarnamn används redan</string>
<string name="account_status_regis_success">Registrering klar</string>
+ <string name="account_status_regis_not_sup">Registrering stöds ej av server</string>
+ <string name="account_status_regis_invalid_token">Ogiltigt registreringstoken</string>
<string name="account_status_tls_error">TLS-förhandling misslyckades</string>
+ <string name="account_status_tls_error_domain">Domänen kan inte verifieras</string>
<string name="account_status_policy_violation">Kränkning av policy</string>
<string name="account_status_incompatible_server">Inkompatibel server</string>
<string name="account_status_stream_error">Strömningsfel</string>
+ <string name="account_status_stream_opening_error">Fel vid öppning av ström</string>
<string name="encryption_choice_unencrypted">Okrypterat</string>
<string name="encryption_choice_otr">OTR</string>
<string name="encryption_choice_pgp">OpenPGP</string>
@@ -150,14 +182,17 @@
<string name="mgmt_account_publish_pgp">Publicera OpenPGP publik nyckel</string>
<string name="unpublish_pgp">Ta bort OpenPGP publik nyckel</string>
<string name="unpublish_pgp_message">Är du säker på att du vill ta bort din OpenPGP publik nyckel från din tillgänglighetsuppdatering?\nDina kontakter kommer inte längre att kunna skicka dig OpenPGP-krypterade meddelande.</string>
+ <string name="openpgp_has_been_published">OpenPGP-nyckel har publicerats.</string>
<string name="mgmt_account_enable">Aktivera konto</string>
<string name="mgmt_account_are_you_sure">Är du säker?</string>
+ <string name="mgmt_account_delete_confirm_text">Om du tar bort ditt konto raderas hela din konversationshistorik</string>
<string name="attach_record_voice">Spela in röst</string>
<string name="account_settings_jabber_id">XMPP-adress</string>
<string name="block_jabber_id">Blockera XMPP-adress</string>
<string name="account_settings_example_jabber_id">användarnamn@exempel.se</string>
<string name="password">Lösenord</string>
<string name="invalid_jid">Detta är inte en giltig XMPP-adress</string>
+ <string name="error_out_of_memory">Slut på minne. Bilden är för stor</string>
<string name="add_phone_book_text">Vill du lägga till %s i din enhets kontakter?</string>
<string name="server_info_show_more">Server-info</string>
<string name="server_info_mam">XEP-0313: Message Archive</string>
@@ -173,12 +208,19 @@
<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>
+ <string name="omemo_fingerprint_selected_message">OMEMO-fingeravtryck (meddelandets ursprung)</string>
+ <string name="omemo_fingerprint_x509_selected_message">v\\OMEMO-fingeravtryck (meddelandets ursprung)</string>
<string name="other_devices">Andra enheter</string>
<string name="trust_omemo_fingerprints">Lita på OMEMO-fingeravtryck</string>
<string name="fetching_keys">Hämtar nycklar...</string>
@@ -195,33 +237,50 @@
<string name="select">Välj</string>
<string name="contact_already_exists">Kontakten finns redan</string>
<string name="join">Gå med</string>
+ <string name="channel_full_jid_example">rum@konferens.exempel.se/användarnamn</string>
+ <string name="channel_bare_jid_example">rum@konferens.exempel.se</string>
<string name="save_as_bookmark">Spara som bokmärke</string>
<string name="delete_bookmark">Ta bort bokmärke</string>
+ <string name="destroy_room">Förstör gruppchat</string>
+ <string name="destroy_channel">Förstör kanal</string>
+ <string name="destroy_room_dialog">Är du säker på att du vill förstöra den här gruppchatten?\n\n<b>Varning:</b> Gruppchatten kommer att tas bort helt från servern.</string>
+ <string name="destroy_channel_dialog">Är du säker på att du vill förstöra den här publika chattgruppen?\n\n<b>Varning:</b> Den här gruppchatten kommer att tas bort helt från servern.</string>
+ <string name="could_not_destroy_room">Det gick inte att ta bort gruppchatten</string>
+ <string name="could_not_destroy_channel">Det gick inte att ta bort kanalen</string>
+ <string name="action_edit_subject">Redigera ämnet för gruppchatten</string>
<string name="topic">Ämne</string>
<string name="joining_conference">Går med i gruppchatt...</string>
<string name="leave">Lämna</string>
<string name="contact_added_you">Kontakten lade till dig i sin kontaktlista</string>
<string name="add_back">Addera tillbaka</string>
<string name="contact_has_read_up_to_this_point">%s har läst hit</string>
+ <string name="contacts_have_read_up_to_this_point">%s har läst till den här punkten</string>
+ <string name="contacts_and_n_more_have_read_up_to_this_point">%1$s +%2$d andra har läst till den här punkten</string>
<string name="everyone_has_read_up_to_this_point">Alla har läst fram till hit</string>
<string name="publish">Publicera</string>
+ <string name="touch_to_choose_picture">Tryck på visningsbilden för att välja en bild från galleriet</string>
<string name="publishing">Publicerar…</string>
<string name="error_publish_avatar_server_reject">Servern kunde inte publicera</string>
+ <string name="error_publish_avatar_converting">Det gick inte att konvertera din bild</string>
<string name="error_saving_avatar">Kunde inte spara avatarbild till disk</string>
<string name="or_long_press_for_default">(Eller tryck länge för att få tillbaks förvald)</string>
+ <string name="error_publish_avatar_no_server_support">Din server stödjer inte publicering av visningsbilder</string>
<string name="private_message">privat meddelande</string>
<string name="private_message_to">till %s</string>
<string name="send_private_message_to">Skicka privat meddelande till %s</string>
<string name="connect">Anslut</string>
<string name="account_already_exists">Detta konto finns redan</string>
<string name="next">Nästa</string>
+ <string name="server_info_session_established">Session etablerad</string>
<string name="skip">Hoppa över</string>
<string name="disable_notifications">Inaktivera notifieringar</string>
<string name="enable">Aktivera</string>
<string name="conference_requires_password">Gruppchatten kräver lösenord</string>
<string name="enter_password">Fyll i lösenord</string>
+ <string name="request_presence_updates">Var god begär närvarouppdateringar från din kontakt först.\n\n<small>Detta kommer att användas för att avgöra vilken chattapplikationen din kontakt använder</small>.</string>
<string name="request_now">Begär nu</string>
<string name="ignore">Ignorera</string>
+ <string name="without_mutual_presence_updates"><b>Varning:</b> Att skicka detta utan ömsesidiga närvarouppdateringar kan orsaka oväntade problem.\n\n<small>Gå till \"Kontaktuppgifter\" för att verifiera dina närvaroprenumerationer.</small></string>
<string name="pref_security_settings">Säkerhet</string>
<string name="pref_allow_message_correction">Tillåt korrigeringar av meddelanden</string>
<string name="pref_allow_message_correction_summary">Tillåt att dina kontakter kan ändra sina meddelanden i efterhand</string>
@@ -235,9 +294,16 @@
<string name="pref_quiet_hours_summary">Notifieringar kommer vara tysta under tysta timmar</string>
<string name="pref_expert_options_other">Annat</string>
<string name="pref_autojoin">Synkronisera med bokmärken</string>
+ <string name="pref_autojoin_summary">Gå med i gruppchattar automatiskt om bokmärket säger det</string>
+ <string name="toast_message_omemo_fingerprint">OMEMO-fingeravtryck kopierat till urklipp</string>
+ <string name="conference_banned">Du är avstängd från denna gruppchatt</string>
+ <string name="conference_members_only">Denna gruppchatt är endast för medlemmar</string>
<string name="conference_resource_constraint">Resursbegränsning</string>
+ <string name="conference_kicked">Du har blivit sparkad från den här gruppchatten</string>
<string name="conference_shutdown">Gruppchatten stängdes ner</string>
+ <string name="conference_unknown_error">Du är inte längre med i denna gruppchatt</string>
<string name="using_account">använder konto %s</string>
+ <string name="hosted_on">huseras hos %s</string>
<string name="checking_x">Kontrollerar %s på webbserver</string>
<string name="not_connected_try_again">Du är inte ansluten. Försök igen senare</string>
<string name="check_x_filesize">Kontrollera filstorleken på %s</string>
@@ -258,6 +324,7 @@
<string name="account_details">Kontodetaljer</string>
<string name="confirm">Bekräfta</string>
<string name="try_again">Försök igen</string>
+ <string name="pref_keep_foreground_service">Förgrundstjänst</string>
<string name="pref_keep_foreground_service_summary">Förehindrar operativsystemet att ta ner uppkopplingen</string>
<string name="pref_create_backup">Skapa säkerhetskopia</string>
<string name="pref_create_backup_summary">Säkerhetskopians filer lagras i %s</string>
@@ -274,14 +341,27 @@
<string name="file">fil</string>
<string name="open_x_file">Öppna %s</string>
<string name="sending_file">skickar (%1$d%% klart)</string>
+ <string name="preparing_file">Förbereder för delning av fil</string>
<string name="x_file_offered_for_download">%s erbjuden för nedladdning</string>
<string name="cancel_transmission">Avbryt överföring</string>
+ <string name="file_transmission_failed">det gick inte att dela fil</string>
+ <string name="file_transmission_cancelled">filöverföring avbruten</string>
+ <string name="file_deleted">Fil borttagen</string>
+ <string name="no_application_found_to_open_file">Ingen applikation som kunde öppna filen hittades</string>
+ <string name="no_application_found_to_open_link">Ingen applikation som kunde öppna länken hittades</string>
+ <string name="no_application_found_to_view_contact">Ingen applikation som kunde visa kontakten hittades</string>
+ <string name="pref_show_dynamic_tags">Dynamiska etiketter</string>
<string name="pref_show_dynamic_tags_summary">Visa skrivskyddade taggar under kontakter</string>
<string name="enable_notifications">Aktivera notifieringar</string>
+ <string name="no_conference_server_found">Ingen gruppchattserver hittades</string>
+ <string name="conference_creation_failed">Det gick inte att skapa gruppchatten</string>
<string name="account_image_description">Kontots avatarbild</string>
<string name="copy_omemo_clipboard_description">Kopiera OMEMO-fingeravtryck till urklipp</string>
<string name="regenerate_omemo_key">Regenerera OMEMO-nyckel</string>
<string name="clear_other_devices">Rensa enheter</string>
+ <string name="clear_other_devices_desc">Är du säker på att du vill ta bort alla andra enheter från OMEMO-tillkännagivandet? Nästa gång dina enheter ansluter, kommer de att tillkännage sig själva igen, men de kanske inte får meddelanden som skickas under tiden.</string>
+ <string name="error_no_keys_to_trust_server_error">Det finns inga användbara nycklar tillgängliga för den här kontakten.\nDet gick inte att hämta nya nycklar från servern. Kanske är det något fel på din kontakts server?</string>
+ <string name="error_no_keys_to_trust_presence">Det finns inga användbara nycklar tillgängliga för den här kontakten.\nSe till att ni båda har närvaroprenumeration.</string>
<string name="error_trustkeys_title">Något gick fel</string>
<string name="fetching_history_from_server">Hämtar historik från server</string>
<string name="no_more_history_on_server">Ingen mer historik på server</string>
@@ -291,6 +371,7 @@
<string name="change_password">Byt lösenord</string>
<string name="current_password">Nuvarande lösenord</string>
<string name="new_password">Nytt lösenord</string>
+ <string name="password_should_not_be_empty">Lösenord kan inte vara tomma</string>
<string name="enable_all_accounts">Aktivera alla konton</string>
<string name="disable_all_accounts">Deaktivera alla konton</string>
<string name="perform_action_with">Utför åtgärden med</string>
@@ -299,26 +380,42 @@
<string name="outcast">Utstött</string>
<string name="member">Medlem</string>
<string name="advanced_mode">Avancerat läge</string>
+ <string name="grant_membership">Bevilja medlemsprivilegier</string>
+ <string name="remove_membership">Återkalla medlemsprivilegier</string>
<string name="grant_admin_privileges">Bevilja administratörsbehörighet</string>
<string name="remove_admin_privileges">Återkalla administratörsbehörighet</string>
+ <string name="grant_owner_privileges">Bevilja ägarprivilegier</string>
+ <string name="remove_owner_privileges">Återkalla ägarprivilegier</string>
<string name="remove_from_room">Ta bort från gruppchatt</string>
<string name="remove_from_channel">Ta bort från kanal</string>
<string name="could_not_change_affiliation">Kunde inte ändra tillhörigheten för %s</string>
+ <string name="ban_from_conference">Förbjud från gruppchatt</string>
+ <string name="ban_from_channel">Förbjud från kanal</string>
+ <string name="removing_from_public_conference">Du försöker ta bort %s från en offentlig kanal. Det enda sättet att göra det är att förbjuda den användaren för alltid.</string>
<string name="ban_now">Bannlys nu</string>
<string name="could_not_change_role">Kunde inte ändra rollen för %s</string>
+ <string name="conference_options">Privat gruppchattskonfiguration</string>
+ <string name="channel_options">Publik kanalkonfiguration</string>
<string name="members_only">Privat, medlemsskap krävs</string>
<string name="non_anonymous">Gör XMPP-adresser synliga för alla</string>
+ <string name="moderated">Gör kanalen modererad</string>
<string name="you_are_not_participating">Du deltar ej</string>
+ <string name="modified_conference_options">Ändrade gruppchattalternativ!</string>
+ <string name="could_not_modify_conference_options">Det gick inte att ändra alternativ för gruppchatt</string>
<string name="never">Aldrig</string>
<string name="until_further_notice">Tills vidare</string>
+ <string name="snooze">Snooza</string>
+ <string name="reply">Svara</string>
<string name="mark_as_read">Läsmarkera</string>
<string name="pref_input_options">Input</string>
<string name="pref_enter_is_send">Skicka med enter</string>
+ <string name="pref_enter_is_send_summary">Använd Enter-tangenten för att skicka meddelandet. Du kan alltid använda Ctrl+Enter för att skicka meddelandet, även om det här alternativet är inaktiverat.</string>
<string name="pref_display_enter_key">Visa enter-knappen</string>
<string name="pref_display_enter_key_summary">Byt ut emoticons-tangenten mot en enter-tangent</string>
<string name="audio">ljud</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>
@@ -334,8 +431,11 @@
<string name="pref_chat_states_summary">Låt dina kontakter veta när du skriver meddelande till dem</string>
<string name="send_location">Skicka position</string>
<string name="show_location">Visa position</string>
+ <string name="no_application_found_to_display_location">Ingen applikation hittades för att visa platsdata</string>
<string name="location">Position</string>
<string name="title_undo_swipe_out_conversation">Konversation stängd</string>
+ <string name="title_undo_swipe_out_group_chat">Lämnade privat gruppchatt</string>
+ <string name="title_undo_swipe_out_channel">Lämnade publik kanal</string>
<string name="pref_dont_trust_system_cas_title">Lita inte på systemets CAs</string>
<string name="pref_dont_trust_system_cas_summary">Alla certifikat måste manuellt godkännas</string>
<string name="pref_remove_trusted_certificates_title">Ta bort certifikat</string>
@@ -348,11 +448,15 @@
<item quantity="one">%d certifikat borttaget</item>
<item quantity="other">%d certifikat borttagna</item>
</plurals>
+ <string name="pref_quick_action_summary">Ersätt \"Skicka\"-knappen med snabbåtgärd</string>
<string name="pref_quick_action">Snabbfunktion</string>
<string name="none">Ingen</string>
<string name="recently_used">Senast använd</string>
<string name="choose_quick_action">Välj snabbfunktion</string>
+ <string name="search_contacts">Sök kontakter</string>
+ <string name="search_bookmarks">Sök bokmärken</string>
<string name="send_private_message">Skicka privat meddelande</string>
+ <string name="user_has_left_conference">%1$s har lämnat gruppchatten</string>
<string name="username">Användarnamn</string>
<string name="username_hint">Användarnamn</string>
<string name="invalid_username">Inte ett giltigt användanamn</string>
@@ -362,16 +466,28 @@
<string name="download_failed_could_not_write_file">Nerladdning gick fel: Kunde inte skriva fil</string>
<string name="account_status_tor_unavailable">Tor-nätverk ej tillgängligt</string>
<string name="account_status_bind_failure">Bind-fel</string>
+ <string name="account_status_host_unknown">Den här servern ansvarar inte för den här domänen</string>
<string name="server_info_broken">Sönder</string>
<string name="pref_presence_settings">Tillgänglighet</string>
+ <string name="pref_away_when_screen_off">Frånvarande när enheten är låst</string>
+ <string name="pref_away_when_screen_off_summary">Visa som frånvarande när enheten är låst</string>
+ <string name="pref_dnd_on_silent_mode">Upptagen i ljudlöst läge</string>
+ <string name="pref_dnd_on_silent_mode_summary">Visa som Upptagen i ljudlöst läge</string>
<string name="pref_treat_vibrate_as_silent">Hantera vibrationsläge som tyst läge</string>
+ <string name="pref_treat_vibrate_as_dnd_summary">Visa som Upptagen när enheten är satt på att endast vibrera</string>
<string name="pref_show_connection_options">Utökade anslutningsinställningar</string>
<string name="pref_show_connection_options_summary">Visa val av servernamn och port vid inställning av konto</string>
<string name="hostname_example">xmpp.example.com</string>
+ <string name="action_add_account_with_certificate">Logga in med certifikat</string>
+ <string name="unable_to_parse_certificate">Det gick inte att analysera certifikatet</string>
<string name="mam_prefs">Arkiveringsinställningar</string>
<string name="server_side_mam_prefs">Arkiveringsinställningar på servern</string>
<string name="fetching_mam_prefs">Hämtar arkiveringsinställningar, vänta...</string>
+ <string name="unable_to_fetch_mam_prefs">Det gick inte att hämta arkiveringsinställningar</string>
+ <string name="captcha_required">CAPTCHA behövs</string>
<string name="captcha_hint">Skriv i texten från bilden ovan</string>
+ <string name="certificate_chain_is_not_trusted">Otillförlitlig certifikatkedja</string>
+ <string name="jid_does_not_match_certificate">XMPP-adressen matchar inte certifikatet</string>
<string name="action_renew_certificate">Förnya certifikat</string>
<string name="error_fetching_omemo_key">Misslyckades med att hämta OMEMO-nyckel!</string>
<string name="verified_omemo_key_with_certificate">Verifierade OMEMO-nyckel med certifikat!</string>
@@ -381,6 +497,7 @@
<string name="pref_use_tor_summary">Tunnla alla anslutningar genom Tor-nätverket. Kräver Orbot</string>
<string name="account_settings_hostname">Servernamn</string>
<string name="account_settings_port">Port</string>
+ <string name="hostname_or_onion">Server- eller .onion-adress</string>
<string name="not_a_valid_port">Inte ett giltigt portnummer</string>
<string name="not_valid_hostname">Inte ett giltigt servernamn</string>
<string name="connected_accounts">%1$d av %2$d konton anslutna</string>
@@ -389,20 +506,36 @@
<item quantity="other">%d meddelanden</item>
</plurals>
<string name="load_more_messages">Ladda fler meddelanden</string>
+ <string name="shared_file_with_x">Fil delad med %s</string>
+ <string name="shared_image_with_x">Bild delad med %s</string>
+ <string name="shared_images_with_x">Bilder som delats med %s</string>
+ <string name="shared_text_with_x">Text som delats med %s</string>
+ <string name="no_storage_permission">Ge %1$s åtkomst till extern lagring</string>
+ <string name="no_camera_permission">Ge %1$s åtkomst till kameran</string>
<string name="sync_with_contacts">Synkronisera med kontakter</string>
+ <string name="sync_with_contacts_long">%1$s vill ha behörighet att komma åt din adressbok för att matcha den med din XMPP-kontaktlista.\nDetta visar dina kontakters fullständiga namn och visningsbilder.\n\n%1$s kommer bara att läsa din adressbok och matcha den lokalt, utan att ladda upp något till din server.</string>
+ <string name="sync_with_contacts_quicksy"><![CDATA[Quicksy behöver åtkomst till dina kontakters telefonnummer för att ge förslag om möjliga kontakter som redan finns på Quicksy.<br><br>Vi kommer inte att lagra någon kopia av dessa telefonnummer.\n\nLäs vår <a href=\"https://quicksy.im/#privacy\">integritetspolicy</a> för mer information.<br><br>Du kommer nu bli ombedd att ge åtkomst till dina kontakter.]]></string>
<string name="notify_on_all_messages">Notifiera för alla meddelanden</string>
+ <string name="notify_only_when_highlighted">Notis endast vid omnämnande</string>
<string name="notify_never">Notifieringar deaktiverade</string>
<string name="notify_paused">Notifieringar pausade</string>
<string name="pref_picture_compression">Bildkomprimering</string>
+ <string name="pref_picture_compression_summary">Tips: Använd \"Välj fil\" istället för \"Välj bild\" för att skicka enskilda bilder okomprimerade, oavsett denna inställning.</string>
<string name="always">Alltid</string>
+ <string name="large_images_only">Endast stora bilder</string>
<string name="battery_optimizations_enabled">Batterioptimeringar aktiverade</string>
+ <string name="battery_optimizations_enabled_explained">Din enhet använder kraftiga batterioptimeringar för %1$s, vilket kan leda till försenade aviseringar eller till och med förlust av meddelanden.\nVi rekommenderar att du inaktiverar dem.</string>
+ <string name="battery_optimizations_enabled_dialog">Din enhet använder kraftiga batterioptimeringar för %1$s, vilket kan leda till försenade aviseringar eller till och med förlust av meddelanden.\nDu kommer nu att bli ombedd att inaktivera dem.</string>
<string name="disable">Deaktivera</string>
<string name="selection_too_large">The valda området är för stort</string>
<string name="no_accounts">(Inget konto aktiverat)</string>
<string name="this_field_is_required">Detta fält måste fyllas i</string>
<string name="correct_message">Korrigera meddelanden</string>
<string name="send_corrected_message">Skicka korrigerat meddelande</string>
+ <string name="no_keys_just_confirm">Du har redan validerat den här personens fingeravtryck säkert för att bekräfta förtroendet. Genom att välja \"Klar\" bekräftar du bara att %s är en del av den här gruppchatten.</string>
<string name="this_account_is_disabled">Du har deaktiverat detta konto</string>
+ <string name="security_error_invalid_file_access">Säkerhetsfel: Ogiltig filåtkomst!</string>
+ <string name="no_application_to_share_uri">Ingen applikation hittades för att dela URI</string>
<string name="share_uri_with">Dela URI med...</string>
<string name="your_full_jid_will_be">Din fullständiga XMPP-adress kommer att vara: %s</string>
<string name="create_account">Skapa konto</string>
@@ -429,8 +562,11 @@
<string name="pref_theme_options">Tema</string>
<string name="pref_theme_options_summary">Välj färgschema</string>
<string name="pref_theme_automatic">Automatisk</string>
+ <string name="pref_theme_light">Ljus</string>
+ <string name="pref_theme_dark">Mörk</string>
<string name="pref_use_green_background">Grön bakgrund</string>
<string name="pref_use_green_background_summary">Använd grön bakgrund för mottagna meddelanden</string>
+ <string name="unable_to_connect_to_keychain">Det gick inte att ansluta till OpenKeychain</string>
<string name="this_device_is_no_longer_in_use">Denna enhet används inte längre</string>
<string name="type_pc">Dator</string>
<string name="type_phone">Mobiltelefon</string>
@@ -438,20 +574,26 @@
<string name="type_web">Webbläsare</string>
<string name="type_console">Konsoll</string>
<string name="payment_required">Betalning krävs</string>
+ <string name="missing_internet_permission">Ge behörighet till att använda Internet</string>
<string name="me">Jag</string>
<string name="contact_asks_for_presence_subscription">Kontakt ber om tillgänglighetsuppdateringar</string>
<string name="allow">Tillåt</string>
<string name="no_permission_to_access_x">Saknar rättigheter för access till %s</string>
<string name="remote_server_not_found">Fjärrserver hittas inte</string>
+ <string name="remote_server_timeout">Timeout för fjärrserver</string>
<string name="unable_to_update_account">Kunde inte uppdatera konto</string>
+ <string name="report_jid_as_spammer">Rapportera den här XMPP-adressen för spam.</string>
<string name="pref_delete_omemo_identities">Ta bort OMEMO identiteter</string>
+ <string name="pref_delete_omemo_identities_summary">Återskapa dina OMEMO-nycklar. Alla dina kontakter måste verifiera dig igen. Använd endast det här som en sista utväg.</string>
<string name="delete_selected_keys">Ta bort valda nycklar</string>
<string name="error_publish_avatar_offline">Du måste vara ansluten för att publicera din avatarbild</string>
<string name="show_error_message">Visa felmeddelande</string>
<string name="error_message">Felmeddelande</string>
<string name="data_saver_enabled">Databesparing</string>
+ <string name="error_unable_to_create_temporary_file">Det gick inte att skapa en tillfällig fil</string>
<string name="this_device_has_been_verified">Denna enhet har verifierats</string>
<string name="copy_fingerprint">Kopiera fingeravtryck</string>
+ <string name="all_omemo_keys_have_been_verified">Du har verifierat alla OMEMO-nycklar i din ägo</string>
<string name="barcode_does_not_contain_fingerprints_for_this_conversation">Streckkoden innehåller inte fingeravtryck för denna konversation.</string>
<string name="verified_fingerprints">Verifierade fingeravtryck</string>
<string name="use_camera_icon_to_scan_barcode">Använd kameran för att scanna en kontakts streckkod</string>
@@ -460,8 +602,11 @@
<string name="share_as_uri">Dela som XMPP URI</string>
<string name="share_as_http">Dela som HTTP länk</string>
<string name="pref_blind_trust_before_verification">Blint förtroende före verifiering</string>
+ <string name="pref_blind_trust_before_verification_summary">Lita på nya enheter från icke-verifierade kontakter, men begär manuell bekräftelse av nya enheter för verifierade kontakter.</string>
+ <string name="blindly_trusted_omemo_keys">Att blint lita på OMEMO-nycklar, innebär att det skulle kunna vara någon annan eller att någon annan har fått åtkomst.</string>
<string name="not_trusted">Ej betrodd</string>
<string name="invalid_barcode">Ogiltig 2D-streckkod</string>
+ <string name="pref_clean_cache_summary">Töm cache-mapp (används av kameraapplikationen)</string>
<string name="pref_clean_cache">Rensa cache</string>
<string name="pref_clean_private_storage">Rensa private lagring</string>
<string name="pref_clean_private_storage_summary">Rensa privat lagring där filer lagras (De kan om-laddas från servern)</string>
@@ -500,7 +645,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>
@@ -509,6 +654,10 @@
<string name="online_right_now">online just nu</string>
<string name="retry_decryption">Försök dekryptera igen</string>
<string name="session_failure">Sessionsfel</string>
+ <string name="open_website">Öppna webbsida</string>
+ <string name="application_found_to_open_website">Ingen applikation hittades för att kunna öppna webbsidan</string>
+ <string name="pref_headsup_notifications">Se upp-notifikationer</string>
+ <string name="pref_headsup_notifications_summary">Visa se upp-notifikationer</string>
<string name="today">Idag</string>
<string name="yesterday">Igår</string>
<string name="pref_validate_hostname">Bekräfta värdnamn med DNSSEC</string>
@@ -531,6 +680,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 +707,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 +737,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>
@@ -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>
- </resources>
+ <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>
@@ -129,6 +129,8 @@
<string name="pref_never_send_crash_summary">Bằng việc gửi báo cáo hoạt động, bạn đang hỗ trợ sự phát triển</string>
<string name="pref_confirm_messages">Xác nhận tin nhắn</string>
<string name="pref_confirm_messages_summary">Báo cho liên hệ của bạn biết khi bạn đã nhận và đọc tin nhắn</string>
+ <string name="pref_prevent_screenshots">Ngăn chặn chụp màn hình</string>
+ <string name="pref_prevent_screenshots_summary">Ẩn nội dung ứng dụng trong màn hình chuyển ứng dụng và chặn chụp màn hình</string>
<string name="pref_ui_options">UI</string>
<string name="openpgp_error">OpenKeychain đã có lỗi.</string>
<string name="bad_key_for_encryption">Mã khoá mã hoá bị lỗi.</string>
@@ -411,6 +413,7 @@
<string name="audio">âm thanh</string>
<string name="video">video</string>
<string name="image">hình ảnh</string>
+ <string name="vector_graphic">hình ảnh véc tơ</string>
<string name="pdf_document">tài liệu PDF</string>
<string name="apk">Ứng dụng Android</string>
<string name="vcard">Liên hệ</string>
@@ -504,7 +507,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 +533,427 @@
<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 \"%s\". 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>
- </resources>
+ <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">Có</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 & 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="rtp_state_security_error">Vấn đề xác minh</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>
+ <string name="plain_text_document">Tài liệu văn bản thuần</string>
+
+</resources>
@@ -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>
@@ -44,4 +44,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>
@@ -45,12 +45,10 @@
<dimen name="local_video_preview_width">96dp</dimen>
<dimen name="rtp_session_duration_top_margin">24dp</dimen>
-
<dimen name="dialpad_text_size">30sp</dimen>
<dimen name="smaller_text_size">12sp</dimen>
<dimen name="medium_margin">8dp</dimen>
<dimen name="activity_margin">16dp</dimen>
<dimen name="small_margin">4dp</dimen>
<dimen name="actionbar_text_size">20sp</dimen>
-
</resources>
@@ -131,6 +131,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>
@@ -621,6 +623,8 @@
<string name="pref_clean_private_storage_summary">Clean private storage where files are kept (They can be re-downloaded from the server)</string>
<string name="i_followed_this_link_from_a_trusted_source">I followed this link from a trusted source</string>
<string name="verifying_omemo_keys_trusted_source">You are about to verify the OMEMO keys of %1$s after clicking a link. This is only secure if you followed this link from a trusted source where only %2$s could have published this link.</string>
+ <string name="verifying_omemo_keys_trusted_source_account">You are about to verify the OMEMO keys of your own account. This is only secure if you followed this link from a trusted source where only you could have published this link.</string>
+ <string name="continue_btn">Continue</string>
<string name="verify_omemo_keys">Verify OMEMO keys</string>
<string name="show_inactive_devices">Show inactive</string>
<string name="hide_inactive_devices">Hide inactive</string>
@@ -903,6 +907,7 @@
<string name="rtp_state_incoming_video_call">Incoming video call</string>
<string name="rtp_state_connecting">Connecting</string>
<string name="rtp_state_connected">Connected</string>
+ <string name="rtp_state_reconnecting">Reconnecting</string>
<string name="rtp_state_accepting_call">Accepting call</string>
<string name="rtp_state_ending_call">Ending call</string>
<string name="answer_call">Answer</string>
@@ -918,6 +923,8 @@
<string name="hang_up">Hang up</string>
<string name="ongoing_call">Ongoing call</string>
<string name="ongoing_video_call">Ongoing video call</string>
+ <string name="reconnecting_call">Reconnecting call</string>
+ <string name="reconnecting_video_call">Reconnecting video call</string>
<string name="disable_tor_to_make_call">Disable Tor to make calls</string>
<string name="incoming_call">Incoming call</string>
<string name="incoming_call_duration">Incoming call · %s</string>
@@ -967,4 +974,7 @@
<string name="backup_started_message">The backup has been started. You’ll 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>
+ <string name="account_registrations_are_not_supported">Account registrations are not supported</string>
+ <string name="no_xmpp_adddress_found">No XMPP address found</string>
+
</resources>
@@ -160,8 +160,6 @@
<item name="android:textColor">@color/white70</item>
</style>
-
-
<style name="DialpadNumberStyle">
<item name="android:includeFontPadding">false</item>
<item name="android:textSize">@dimen/dialpad_text_size</item>
@@ -171,5 +169,4 @@
<item name="android:textSize">@dimen/smaller_text_size</item>
<item name="android:alpha">0.8</item>
</style>
-
-</resources>
+</resources>
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<full-backup-content>
+ <include
+ domain="sharedpref"
+ path="."
+ requireFlags="clientSideEncryption" />
+ <include
+ domain="database"
+ path="."
+ requireFlags="clientSideEncryption" />
+</full-backup-content>
@@ -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"
@@ -46,30 +46,30 @@ public class PhoneNumberContact extends AbstractPhoneContact {
ContactsContract.Data.PHOTO_URI,
ContactsContract.Data.LOOKUP_KEY,
ContactsContract.CommonDataKinds.Phone.NUMBER};
- final Cursor cursor;
- try {
- cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null);
- } catch (final Exception e) {
- return ImmutableMap.of();
- }
final HashMap<String, PhoneNumberContact> contacts = new HashMap<>();
- while (cursor != null && cursor.moveToNext()) {
- try {
- final PhoneNumberContact contact = new PhoneNumberContact(context, cursor);
- final PhoneNumberContact preexisting = contacts.get(contact.getPhoneNumber());
- if (preexisting == null || preexisting.rating() < contact.rating()) {
- contacts.put(contact.getPhoneNumber(), contact);
+ try (final Cursor cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null)){
+ while (cursor != null && cursor.moveToNext()) {
+ try {
+ final PhoneNumberContact contact = new PhoneNumberContact(context, cursor);
+ final PhoneNumberContact preexisting = contacts.get(contact.getPhoneNumber());
+ if (preexisting == null || preexisting.rating() < contact.rating()) {
+ contacts.put(contact.getPhoneNumber(), contact);
+ }
+ } catch (final IllegalArgumentException ignored) {
+
}
- } catch (final IllegalArgumentException e) {
- Log.d(Config.LOGTAG, e.getMessage());
}
- }
- if (cursor != null) {
- cursor.close();
+ } catch (final Exception e) {
+ return ImmutableMap.of();
}
return ImmutableMap.copyOf(contacts);
}
+ public static PhoneNumberContact findByUriOrNumber(Collection<PhoneNumberContact> haystack, Uri uri, String number) {
+ final PhoneNumberContact byUri = findByUri(haystack, uri);
+ return byUri != null || number == null ? byUri : findByNumber(haystack, number);
+ }
+
public static PhoneNumberContact findByUri(Collection<PhoneNumberContact> haystack, Uri needle) {
for (PhoneNumberContact contact : haystack) {
if (needle.equals(contact.getLookupUri())) {
@@ -78,4 +78,13 @@ public class PhoneNumberContact extends AbstractPhoneContact {
}
return null;
}
+
+ private static PhoneNumberContact findByNumber(Collection<PhoneNumberContact> haystack, String needle) {
+ for (PhoneNumberContact contact : haystack) {
+ if (needle.equals(contact.getPhoneNumber())) {
+ return contact;
+ }
+ }
+ return null;
+ }
}
@@ -2,6 +2,9 @@ package eu.siacs.conversations.entities;
import android.util.Base64;
+import com.google.common.base.Charsets;
+import com.google.common.hash.Hashing;
+
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@@ -63,20 +66,15 @@ public class Entry implements Comparable<Entry> {
builder.append(jid.asBareJid().toEscapedString());
}
}
- MessageDigest md;
- try {
- md = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- return "";
- }
- byte[] sha1 = md.digest(builder.toString().getBytes());
+ @SuppressWarnings("deprecation")
+ final byte[] sha1 = Hashing.sha1().hashString(builder.toString(), Charsets.UTF_8).asBytes();
return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
}
private static List<Entry> ofPhoneNumberContactsAndContacts(final Collection<PhoneNumberContact> phoneNumberContacts, Collection<Contact> systemContacts) {
- ArrayList<Entry> entries = new ArrayList<>();
+ final ArrayList<Entry> entries = new ArrayList<>();
for(Contact contact : systemContacts) {
- PhoneNumberContact phoneNumberContact = PhoneNumberContact.findByUri(phoneNumberContacts, contact.getSystemAccount());
+ final PhoneNumberContact phoneNumberContact = PhoneNumberContact.findByUri(phoneNumberContacts, contact.getSystemAccount());
if (phoneNumberContact != null && phoneNumberContact.getPhoneNumber() != null) {
Entry entry = findOrCreateByPhoneNumber(entries, phoneNumberContact.getPhoneNumber());
entry.jids.add(contact.getJid().asBareJid());
@@ -382,9 +382,13 @@ public class QuickConversationsService extends AbstractQuickConversationsService
if (uri == null) {
continue;
}
- PhoneNumberContact phoneNumberContact = PhoneNumberContact.findByUri(contacts, uri);
+ final String number = getNumber(contact);
+ final PhoneNumberContact phoneNumberContact = PhoneNumberContact.findByUriOrNumber(contacts, uri, number);
final boolean needsCacheClean;
if (phoneNumberContact != null) {
+ if (!uri.equals(phoneNumberContact.getLookupUri())) {
+ Log.d(Config.LOGTAG, "lookupUri has changed from " + uri + " to " + phoneNumberContact.getLookupUri());
+ }
needsCacheClean = contact.setPhoneContact(phoneNumberContact);
} else {
needsCacheClean = contact.unsetPhoneContact(PhoneNumberContact.class);
@@ -396,6 +400,14 @@ public class QuickConversationsService extends AbstractQuickConversationsService
}
}
+ private static String getNumber(final Contact contact) {
+ final Jid jid = contact.getJid();
+ if (jid.getLocal() != null && Config.QUICKSY_DOMAIN.equals(jid.getDomain())) {
+ return jid.getLocal();
+ }
+ return null;
+ }
+
private boolean considerSync(final Account account, final Map<String, PhoneNumberContact> contacts, final boolean forced) {
final int hash = contacts.keySet().hashCode();
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": consider sync of " + hash);
@@ -2,10 +2,7 @@ package eu.siacs.conversations.ui;
import android.content.Context;
import android.content.Intent;
-import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.appcompat.widget.Toolbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
@@ -15,6 +12,10 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
+import androidx.appcompat.widget.Toolbar;
+import androidx.databinding.DataBindingUtil;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -25,6 +26,7 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityChooseCountryBinding;
import eu.siacs.conversations.ui.adapter.CountryAdapter;
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
+import eu.siacs.conversations.utils.ThemeHelper;
public class ChooseCountryActivity extends ActionBarActivity implements CountryAdapter.OnCountryClicked {
@@ -70,16 +72,17 @@ public class ChooseCountryActivity extends ActionBarActivity implements CountryA
return true;
}
};
- private TextView.OnEditorActionListener mSearchDone = (v, actionId, event) -> {
- if (countries.size() == 1) {
- onCountryClicked(countries.get(0));
- }
- return true;
- };
+ private TextView.OnEditorActionListener mSearchDone = (v, actionId, event) -> {
+ if (countries.size() == 1) {
+ onCountryClicked(countries.get(0));
+ }
+ return true;
+ };
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setTheme(ThemeHelper.find(this));
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_choose_country);
setSupportActionBar((Toolbar) this.binding.toolbar);
configureActionBar(getSupportActionBar());
@@ -115,9 +118,9 @@ public class ChooseCountryActivity extends ActionBarActivity implements CountryA
private void filterCountries(String needle) {
List<PhoneNumberUtilWrapper.Country> countries = PhoneNumberUtilWrapper.getCountries(this);
Iterator<PhoneNumberUtilWrapper.Country> iterator = countries.iterator();
- while(iterator.hasNext()) {
+ while (iterator.hasNext()) {
final PhoneNumberUtilWrapper.Country country = iterator.next();
- if(needle != null && !country.getName().toLowerCase(Locale.getDefault()).contains(needle.toLowerCase(Locale.getDefault()))) {
+ if (needle != null && !country.getName().toLowerCase(Locale.getDefault()).contains(needle.toLowerCase(Locale.getDefault()))) {
iterator.remove();
}
}
@@ -20,7 +20,7 @@ public class EnterNameActivity extends XmppActivity implements XmppConnectionSer
private Account account;
- private AtomicBoolean setNick = new AtomicBoolean(false);
+ private final AtomicBoolean setNick = new AtomicBoolean(false);
@Override
protected void onCreate(final Bundle savedInstanceState) {
@@ -14,6 +14,8 @@ import android.view.KeyEvent;
import android.view.View;
import android.widget.EditText;
+import org.jetbrains.annotations.NotNull;
+
import java.util.concurrent.atomic.AtomicBoolean;
import eu.siacs.conversations.Config;
@@ -126,7 +128,7 @@ public class EnterPhoneNumberActivity extends XmppActivity implements QuickConve
}
@Override
- public void onSaveInstanceState(Bundle savedInstanceState) {
+ public void onSaveInstanceState(@NotNull Bundle savedInstanceState) {
if (this.region != null) {
savedInstanceState.putString("region", this.region);
}
@@ -26,6 +26,8 @@ import android.widget.TextView;
import java.lang.ref.WeakReference;
+import eu.siacs.conversations.ui.util.StyledAttributes;
+
public class TextDrawable extends Drawable implements TextWatcher {
private WeakReference<TextView> ref;
private String mText;
@@ -62,6 +64,7 @@ public class TextDrawable extends Drawable implements TextWatcher {
*/
public TextDrawable(TextView tv, String initialText, boolean bindToViewsText, boolean bindToViewsPaint) {
this(tv.getPaint(), initialText);
+ mPaint.setColor(StyledAttributes.getColor(tv.getContext(), android.R.attr.textColorPrimary));
ref = new WeakReference<>(tv);
if (bindToViewsText || bindToViewsPaint) {
if (bindToViewsText) {
@@ -157,6 +160,10 @@ public class TextDrawable extends Drawable implements TextWatcher {
setBounds(bounds);
}
+ public Paint getPaint() {
+ return mPaint;
+ }
+
public void setPaint(Paint paint) {
mPaint = new Paint(paint);
//Since this can change the font used, we need to recalculate bounds.
@@ -168,8 +175,8 @@ public class TextDrawable extends Drawable implements TextWatcher {
invalidateSelf();
}
- public Paint getPaint() {
- return mPaint;
+ public String getText() {
+ return mText;
}
public void setText(String text) {
@@ -183,10 +190,6 @@ public class TextDrawable extends Drawable implements TextWatcher {
invalidateSelf();
}
- public String getText() {
- return mText;
- }
-
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android">
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
@@ -49,7 +50,7 @@
android:layout_height="wrap_content"
android:cursorVisible="false"
android:drawableEnd="@drawable/ic_arrow_drop_down_black_18dp"
- android:drawableRight="@drawable/ic_arrow_drop_down_black_18dp"
+ app:drawableTint="?android:attr/textColorPrimary"
android:focusable="false"
android:gravity="bottom|center_horizontal"
android:imeOptions="flagNoExtractUi"
@@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <string name="pref_notification_grace_period_summary">طول الوقت الذي يبقى فيه كويكسي صامتا بعد رؤية النشاط في جهاز آخر</string>
+ <string name="pref_never_send_crash_summary">عبر إرسال أثار الأخطاء تقوم بالمساعدة في تطوير برمجة كويكسي</string>
<string name="pref_broadcast_last_activity_summary">إجعل كلّ جهات إتصالك تعلم أنك تستعمل كويكسي</string>
+ <string name="huawei_protected_apps_summary">للمواصلة في إستقبال التنبيهات، حتى والشاشة مغلقة، يجب عليك أن تضيف كويكسي إلى قائمة التطبيقات المحميّة.</string>
<string name="set_profile_picture">صورة حساب كويكسي</string>
<string name="not_available_in_your_country">إن كويكسي Quicksy غير متوفر في بلدكم.</string>
- </resources>
+ <string name="unable_to_verify_server_identity">لا يمكن التأكد من خادم الهويّة.</string>
+ <string name="unknown_security_error">خطأ أمني مجهول.</string>
+ <string name="timeout_while_connecting_to_server">تجاوز الوقت أثناء الإتصال بالخادم.</string>
+</resources>
@@ -3,7 +3,7 @@
<string name="pref_notification_grace_period_summary">Времето, през което Quicksy няма да прави нищо, след като забележи дейност на друго устройство</string>
<string name="pref_never_send_crash_summary">Изпращайки проследявания на стека, Вие помагате за непрекъснатото развитие на Quicksy</string>
<string name="pref_broadcast_last_activity_summary">Така всичките Ви контакти ще знаят кога използвате Quicksy</string>
- <string name="huawei_protected_apps_summary">Ако искате да продължите да получавате известия дори когато екранът е заключен, трябва да добавите „Quicksy“ към списъка от защитени приложения.</string>
+ <string name="huawei_protected_apps_summary">Ако искате да продължите да получавате известия дори когато екранът е заключен, трябва да добавите Quicksy към списъка със защитени приложения.</string>
<string name="set_profile_picture">Профилна снимка за Quicksy</string>
<string name="not_available_in_your_country">Quicksy не може да се използва във Вашата страна.</string>
<string name="unable_to_verify_server_identity">Идентичността на сървъра не може да бъде потвърдена.</string>
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="pref_notification_grace_period_summary">Kuinka kauan Quicksy pysyy hiljaa nähtyään toisella laitteellasi toimintaa</string>
+ <string name="pref_never_send_crash_summary">Lähettämällä virheenkorjaustietoja autat Quicksyn kehittäjiä</string>
+ <string name="pref_broadcast_last_activity_summary">Kerro kaikille yhteystiedoillesi kun käytät Quicksya</string>
+ <string name="huawei_protected_apps_summary">Saadaksesi ilmoituksia silloinkin kun näyttö on sammutettu, Quicksy pitää lisätä suojattujen sovellusten luetteloon.</string>
+ <string name="set_profile_picture">Quicksy-profiilikuva</string>
+ <string name="not_available_in_your_country">Quicksy ei ole saatavilla maassasi.</string>
+ <string name="unable_to_verify_server_identity">Palvelimen identiteetin varmennus epäonnistui.</string>
+ <string name="unknown_security_error">Tuntematon turvallisuusvirhe.</string>
+ <string name="timeout_while_connecting_to_server">Palvelimeen yhdistäminen aikakatkaistiin.</string>
+</resources>
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="pref_notification_grace_period_summary">Doba, počas ktorej bude Quicksy stíšený po detekcii aktivity na inom zariadení.</string>
+ <string name="pref_never_send_crash_summary">Zaslaním detailov o dôvode zlyhania pomáhate ďalšiemu vývoju aplikácie Quicksy</string>
+ <string name="pref_broadcast_last_activity_summary">Dajte svojim kontaktom vedieť, keď používate Quicksy</string>
+ <string name="huawei_protected_apps_summary">Aby ste dostávali oznámenia aj pri vypnutej obrazovke, pridajte Quicksy medzi chránené aplikácie.</string>
+ <string name="set_profile_picture">Quicksy profilový obrázok</string>
+ <string name="not_available_in_your_country">Quicksy nie je dostupné vo vašej krajine.</string>
+ <string name="unable_to_verify_server_identity">Nemôžem overiť identitu servera.</string>
+ <string name="unknown_security_error">Neznáma bezpečnostná chyba.</string>
+ <string name="timeout_while_connecting_to_server">Vypršal časový limit pri pripájaní k serveru.</string>
+</resources>