diff --git a/CHANGELOG.md b/CHANGELOG.md
index 532d3c0b9..e58b08305 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+### Version 2.9.2
+
+* Offer Easy Invite generation on supporting servers
+* Display GIFs send from Movim
+* store avatars in cache
+
+### Version 2.9.1
+
+* fixed search on Android <= 5
+* optimize memory consumption
+
### Version 2.9.0
* Search individual conversations
@@ -419,10 +430,9 @@
* Icons for attach menu
### Version 1.16.2
-* change mam catchup strategie. support mam:1
+* change mam catchup strategy. support mam:1
* bug fixes
-
### Version 1.16.1
* UI performance fixes
* bug fixes
@@ -472,7 +482,7 @@
* bug fixes
### Version 1.14.6
-* make error notification dismissable
+* make error notification dismissible
* bug fixes
@@ -496,7 +506,7 @@
* bug fixes
### Version 1.14.0
-* Improvments for N
+* Improvements for N
* Quick Reply to Notifications on N
* Don't download avatars and files when data saver is on
* bug fixes
@@ -674,7 +684,7 @@
### Version 1.7.0
* CAPTCHA support
-* SASL EXTERNAL (client certifiates)
+* SASL EXTERNAL (client certificates)
* fetching MUC history via MAM
* redownload deleted files from HTTP hosts
* Expert setting to automatically set presence
@@ -782,7 +792,7 @@
* accept more ciphers
### Version 1.0
-* MUC controls (Affiliaton changes)
+* MUC controls (Affiliation changes)
* Added download button to notification
* Added check box to hide offline contacts
* Use Material theme and icons on Android L
@@ -888,7 +898,7 @@
* XEP-0333. Mark whether the other party has read your messages
* Delayed messages are now tagged properly
* Share images from the Gallery
-* Infinit history scrolling
+* Infinite history scrolling
* Mark the last used presence in presence selection dialog
### Version 0.3
diff --git a/README.md b/README.md
index da7166cb5..34185b185 100644
--- a/README.md
+++ b/README.md
@@ -141,7 +141,7 @@ Maybe you attempted to use the Jabber ID `test@b.tld` because `a.tld` doesn’t
### I get 'Stream opening error'. What does that mean?
-In most cases this error is caused by ejabberd advertising support for TLSv1.3 but not properly supporting it. This can happen if the openssl version on the server already supports TLSv1.3 but the fast\_tls wrapper library used by ejabberd not (properly) support it. Upgrading fast\_tls and ejabberd or - theoretically - downgrading openssl should fix the issue. A work around is to explicity disable TLSv1.3 support in the ejabberd configuration. More information can be found on [this issue on the ejabberd issue tracker](https://github.com/processone/ejabberd/issues/2614).
+In most cases this error is caused by ejabberd advertising support for TLSv1.3 but not properly supporting it. This can happen if the OpenSSL version on the server already supports TLSv1.3 but the fast\_tls wrapper library used by ejabberd not (properly) support it. Upgrading fast\_tls and ejabberd or - theoretically - downgrading OpenSSL should fix the issue. A work around is to explicitly disable TLSv1.3 support in the ejabberd configuration. More information can be found on [this issue on the ejabberd issue tracker](https://github.com/processone/ejabberd/issues/2614).
#### I’m getting this annoying permanent notification
@@ -149,7 +149,7 @@ Starting with Conversations 2.3.6 Conversations releases distributed over the Go
However you can disable the notification via settings of the operating system. (Not settings in Conversations.)
-**The battery consumption and the entire behaviour of Conversations will remain the same (as good or as bad as it was before). Why is Google doing this to you? We have no idea.**
+**The battery consumption and the entire behavior of Conversations will remain the same (as good or as bad as it was before). Why is Google doing this to you? We have no idea.**
##### Android <= 7.1 or Conversations from F-Droid (all Android versions)
The foreground notification is still controlled over the expert settings within Conversations as it always has been. Whether or not you need to enable it depends on how aggressive the non-standard 'power saving' features are that your phone vendor has built into the operating system.
@@ -173,7 +173,7 @@ You can find a detailed description of how your server, the app server and FCM a
#### But why do I need a permanent notification if I use Google Push?
-FCM (Google Push) allows an app to wake up from *Doze* which is (as the name suggests) a hibernation feature of the Android operating system that cuts the network connection and also reduces the number of times the app is allowed to wake up (to ping the server for example). The app can ask to be excluded from doze. Non push variants of the app (from F-Droid or if the server doesn’t support it) will do this on first start up. So if you get exemption from *Doze*, or if you get regular push events sent to you, Doze should not pose a threat to Conversatons working properly. But even with *Doze* the app is still open in the background (kept in memory); it is just limited in the actions it can do. Conversations needs to stay in memory to hold certain session state (online status of contacts, join status of group chats, …). However with Android 8 Google changed all of this again and now an App that wants to stay in memory needs to have a foreground service which is visible to the user via the annoying notification. But why does Conversations need to hold that state? XMPP is a stateful protocol that has a lot of per-session information; packets need to be counted, presence information needs to be held, some features like Message Carbons get activated once per session, MAM catchup happens once, service discovery happens only once; the list goes on. When Conversations was created in early 2014 none of this was a problem because apps were just allowed to stay in memory. Basically every XMPP client out there holds that information in memory because it would be a lot more complicated trying to persist it to disk. An entire rewrite of Conversations in the year 2019 would attempt to do that and would probably succeed however it would require exactly that; a complete rewrite which is not feasible right now. That’s by the way also the reason why it is difficult to write an XMPP client on iOS. Or more broadly put this is also the reason why other protocols are designed as or migrated to stateless protocols (often based on HTTP); take for example the migration of IMAP to [JMAP](https://jmap.io/).
+FCM (Google Push) allows an app to wake up from *Doze* which is (as the name suggests) a hibernation feature of the Android operating system that cuts the network connection and also reduces the number of times the app is allowed to wake up (to ping the server for example). The app can ask to be excluded from doze. Non push variants of the app (from F-Droid or if the server doesn’t support it) will do this on first start up. So if you get exemption from *Doze*, or if you get regular push events sent to you, Doze should not pose a threat to Conversatons working properly. But even with *Doze* the app is still open in the background (kept in memory); it is just limited in the actions it can do. Conversations needs to stay in memory to hold certain session state (online status of contacts, join status of group chats, …). However with Android 8 Google changed all of this again and now an App that wants to stay in memory needs to have a foreground service which is visible to the user via the annoying notification. But why does Conversations need to hold that state? XMPP is a statefull protocol that has a lot of per-session information; packets need to be counted, presence information needs to be held, some features like Message Carbons get activated once per session, MAM catch-up happens once, service discovery happens only once; the list goes on. When Conversations was created in early 2014 none of this was a problem because apps were just allowed to stay in memory. Basically every XMPP client out there holds that information in memory because it would be a lot more complicated trying to persist it to disk. An entire rewrite of Conversations in the year 2019 would attempt to do that and would probably succeed however it would require exactly that; a complete rewrite which is not feasible right now. That’s by the way also the reason why it is difficult to write an XMPP client on iOS. Or more broadly put this is also the reason why other protocols are designed as or migrated to stateless protocols (often based on HTTP); take for example the migration of IMAP to [JMAP](https://jmap.io/).
#### Conversations doesn’t work for me. Where can I get help?
@@ -267,11 +267,11 @@ and introduce yourself to `iNPUTmice` so he can approve your join request.
#### How do I backup / move Conversations to a new device?
On the one hand Conversations supports Message Archive Management to keep a server side history of your messages so when migrating to a new device that device can display your entire history. However that does not work if you enable OMEMO due to its forward secrecy. (Read [The State of Mobile XMPP in 2016](https://gultsch.de/xmpp_2016.html) especially the section on encryption.)
-As of version 2.4.0 an integrated Backup & Restore function will help with this, go to Settings and you’ll find a setting called Create backup. A notification will pop-up during the creation process that will announce you when it's ready. After the files, one for each account, are created, you can move the **Conversations** folder *(if you want your old media files too)* or only the **Conversations/Backup** folder *(for OMEMO keys and history only)* to your new device (or to a storage place) where a freshly installed Conversations can restore each account. Don't forget to enable the accounts after a succesful restore.
+As of version 2.4.0 an integrated Backup & Restore function will help with this, go to Settings and you’ll find a setting called Create backup. A notification will pop-up during the creation process that will announce you when it's ready. After the files, one for each account, are created, you can move the **Conversations** folder *(if you want your old media files too)* or only the **Conversations/Backup** folder *(for OMEMO keys and history only)* to your new device (or to a storage place) where a freshly installed Conversations can restore each account. Don't forget to enable the accounts after a successfull restore.
This backup method will include your OMEMO keys. Due to forward secrecy you will not be able to recover messages sent and received between creating the backup and restoring it. If you have a server side archive (MAM) those messages will be retrieved but displayed as *unable to decrypt*. For technical reasons you might also lose the first message you either sent or receive after the restore; for each conversation you have. This message will then also show up as *unable to decrypt*, but this will automatically recover itself as long as both participants are on Conversations 2.3.11+. Note that this doesn’t happen if you just transfer to a new phone and no messages have been exchanged between backup and restore.
-In the vast, vast majority of cases you won’t have to manually delete OMEMO keys or do anything like that. Conversations only introduced the offical backup feature in 2.4.0 after making sure the *OMEMO self healing* mechanism introduced in 2.3.11 works fine.
+In the vast, vast majority of cases you won’t have to manually delete OMEMO keys or do anything like that. Conversations only introduced the official backup feature in 2.4.0 after making sure the *OMEMO self healing* mechanism introduced in 2.3.11 works fine.
**WARNING**: Be sure to know your accounts passwords or find ways to reset them **before** doing the backup as the files are encrypted using those passwords and the Restore process will ask for them.
**WARNING**: Do not use the restore backup feature in an attempt to clone (run simultaneously) an installation. Restoring a backup is only meant for migrations or in case you’ve lost the original device.
@@ -327,7 +327,7 @@ OMEMO has two requirements: Your server and the server of your contact need to s
OMEMO encryption works only in private (members only) conferences that are non-anonymous. Non-anonymous (being able to discover the real JID of other participants) is a technical requirement to discover the key material. Members only is a sort of arbitrary requirement imposed by Conversations. (see 'OMEMO is grayed out')
The server of all participants need to pass the OMEMO [Compliance Test](https://conversations.im/compliance/).
-In other words they either need to run Ejabberd 18.01+ or Prosody 0.11+.
+In other words they either need to run ejabberd 18.01+ or Prosody 0.11+.
(Alternatively it would also work if all participants had each other in their contact list; But that rarely is the case in larger group chats.)
diff --git a/build.gradle b/build.gradle
index c63d1a14f..365173a6b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,7 +8,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.0.1'
+ classpath 'com.android.tools.build:gradle:4.1.1'
}
}
@@ -95,9 +95,9 @@ android {
defaultConfig {
minSdkVersion 21
- targetSdkVersion 28
- versionCode 399
- versionName "2.9.0"
+ targetSdkVersion 29
+ versionCode 402
+ versionName "2.9.2"
archivesBaseName += "-$versionName"
applicationId "eu.sum7.conversations"
resValue "string", "applicationId", applicationId
diff --git a/fastlane/metadata/android/en-US/changelogs/398.txt b/fastlane/metadata/android/en-US/changelogs/398.txt
new file mode 100644
index 000000000..95280ea88
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/398.txt
@@ -0,0 +1,4 @@
+* Search individual conversations
+* Notify user if message delivery fails
+* Remember display names (nicks) from Quicksy users across restarts
+* Add button to start Orbot (Tor) from notification if necessary
diff --git a/fastlane/metadata/android/en-US/changelogs/401.txt b/fastlane/metadata/android/en-US/changelogs/401.txt
new file mode 100644
index 000000000..0fba861d3
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/401.txt
@@ -0,0 +1,3 @@
+* fixed search on Android <= 5
+* optimize memory consumption
+
diff --git a/fastlane/metadata/android/en-US/changelogs/402.txt b/fastlane/metadata/android/en-US/changelogs/402.txt
new file mode 100644
index 000000000..53f461756
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/402.txt
@@ -0,0 +1,3 @@
+* Offer Easy Invite generation on supporting servers
+* Display GIFs send from Movim
+* store avatars in cache
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 9ad8d1f7c..02d7d70dd 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sun Jul 26 11:32:42 CEST 2020
+#Sat Nov 14 09:59:55 CET 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
diff --git a/src/conversations/AndroidManifest.xml b/src/conversations/AndroidManifest.xml
index 90b78ed4c..62396bed1 100644
--- a/src/conversations/AndroidManifest.xml
+++ b/src/conversations/AndroidManifest.xml
@@ -20,6 +20,10 @@
android:name=".ui.MagicCreateActivity"
android:label="@string/create_new_account"
android:launchMode="singleTask" />
+ share());
+ if (bundle != null && bundle.containsKey("invite")) {
+ this.easyOnboardingInvite = bundle.getParcelable("invite");
+ if (this.easyOnboardingInvite != null) {
+ showInvite(this.easyOnboardingInvite);
+ return;
+ }
+ }
+ this.showLoading();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.easy_onboarding_invite, menu);
+ final MenuItem share = menu.findItem(R.id.action_share);
+ share.setVisible(easyOnboardingInvite != null);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ if (menuItem.getItemId() == R.id.action_share) {
+ share();
+ return true;
+ } else {
+ return super.onOptionsItemSelected(menuItem);
+ }
+ }
+
+ private void share() {
+ final String shareText = getString(
+ R.string.easy_invite_share_text,
+ easyOnboardingInvite.getDomain(),
+ easyOnboardingInvite.getShareableLink()
+ );
+ final Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, shareText);
+ sendIntent.setType("text/plain");
+ startActivity(Intent.createChooser(sendIntent, getString(R.string.share_invite_with)));
+ }
+
+ @Override
+ protected void refreshUiReal() {
+ invalidateOptionsMenu();
+ if (easyOnboardingInvite != null) {
+ showInvite(easyOnboardingInvite);
+ } else {
+ showLoading();
+ }
+ }
+
+ private void showLoading() {
+ this.binding.inProgress.setVisibility(View.VISIBLE);
+ this.binding.invite.setVisibility(View.GONE);
+ }
+
+ private void showInvite(final EasyOnboardingInvite invite) {
+ this.binding.inProgress.setVisibility(View.GONE);
+ this.binding.invite.setVisibility(View.VISIBLE);
+ this.binding.tapToShare.setText(getString(R.string.tap_share_button_send_invite, invite.getDomain()));
+ final Point size = new Point();
+ getWindowManager().getDefaultDisplay().getSize(size);
+ final int width = Math.min(size.x, size.y);
+ final Bitmap bitmap = BarcodeProvider.create2dBarcodeBitmap(invite.getShareableLink(), width);
+ binding.qrCode.setImageBitmap(bitmap);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle bundle) {
+ super.onSaveInstanceState(bundle);
+ if (easyOnboardingInvite != null) {
+ bundle.putParcelable("invite", easyOnboardingInvite);
+ }
+ }
+
+ @Override
+ void onBackendConnected() {
+ if (easyOnboardingInvite != null) {
+ return;
+ }
+ final Intent launchIntent = getIntent();
+ final String accountExtra = launchIntent.getStringExtra(EXTRA_ACCOUNT);
+ final Jid jid = accountExtra == null ? null : Jid.ofEscaped(accountExtra);
+ if (jid == null) {
+ return;
+ }
+ final Account account = xmppConnectionService.findAccountByJid(jid);
+ xmppConnectionService.requestEasyOnboardingInvite(account, this);
+ }
+
+ public static void launch(final Account account, final Activity context) {
+ final Intent intent = new Intent(context, EasyOnboardingInviteActivity.class);
+ intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toEscapedString());
+ context.startActivity(intent);
+ }
+
+ @Override
+ public void inviteRequested(EasyOnboardingInvite invite) {
+ this.easyOnboardingInvite = invite;
+ Log.d(Config.LOGTAG, "invite requested");
+ refreshUi();
+ }
+
+ @Override
+ public void inviteRequestFailed(final String message) {
+ runOnUiThread(() -> {
+ if (!Strings.isNullOrEmpty(message)) {
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+ }
+ finish();
+ });
+ }
+}
diff --git a/src/conversations/res/layout/activity_easy_invite.xml b/src/conversations/res/layout/activity_easy_invite.xml
new file mode 100644
index 000000000..8bbf11c03
--- /dev/null
+++ b/src/conversations/res/layout/activity_easy_invite.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/conversations/res/menu/easy_onboarding_invite.xml b/src/conversations/res/menu/easy_onboarding_invite.xml
new file mode 100644
index 000000000..0e086b515
--- /dev/null
+++ b/src/conversations/res/menu/easy_onboarding_invite.xml
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/src/conversations/res/values-de/strings.xml b/src/conversations/res/values-de/strings.xml
index a1c056a7d..687c8e716 100644
--- a/src/conversations/res/values-de/strings.xml
+++ b/src/conversations/res/values-de/strings.xml
@@ -9,4 +9,8 @@
Du wurdest zu %1$seingeladen. Ein Benutzername ist bereits für dich ausgewählt worden. Wir führen dich durch den Prozess der Kontoerstellung.\nDu kannst mit Nutzern anderer Anbieter kommunizieren, indem du ihnen deine vollständige XMPP-Adresse gibst.Deine Einladung für den ServerFalsch formatierter Provisionierungscode
+ Tippe auf die \"Teilen\"-Schaltfläche, um deinem Kontakt eine Einladung an %1$s zu senden.
+ Wenn dein Kontakt in der Nähe ist, kann er auch den untenstehenden Code einscannen, um deine Einladung anzunehmen.
+ Komme zu %1$s und chatte mit mir: %2$s
+ Einladung teilen mit…
diff --git a/src/conversations/res/values-el/strings.xml b/src/conversations/res/values-el/strings.xml
index 60f8c66ae..274cc2087 100644
--- a/src/conversations/res/values-el/strings.xml
+++ b/src/conversations/res/values-el/strings.xml
@@ -5,7 +5,8 @@
Δημιουργία νέου λογαριασμούΈχετε ήδη λογαριασμό XMPP; Αυτό μπορεί να συμβαίνει αν ήδη χρησιμοποιείτε ένα άλλο πρόγραμμα XMPP ή έχετε χρησιμοποιήσει το Conversations παλιότερα. Αν όχι, μπορείτε να δημιουργήσετε ένα νέο λογαριασμό XMPP τώρα.\nΧρήσιμη πληροφορία: Κάποιοι πάροχοι e-mail παρέχουν επίσης και λογαριασμούς XMPP.Το XMPP είναι ένα δίκτυο άμεσης ανταλλαγής μηνυμάτων ανεξάρτητο παρόχου. Μπορείτε να χρησιμοποιήσετε αυτό το πρόγραμμα με όποιον διακομιστή XMPP επιθυμείτε.\nΓια διευκόλυνση πάντως μπορείτε να δημιουργήσετε έναν λογαριασμό στο chat.sum7.eu, έναν πάροχο ειδικά σχεδιασμένο για χρήση με το Conversations.
- Έχετε προσκληθεί στο %1$s. Ένα όνομα χρήστη έχει ήδη επιλεγεί για εσάς. Θα σας καθοδηγήσουμε στη διαδικασία δημιουργίας λογαριασμού.\nΕπιλέγοντας τον %1$s ως πάροχο θα μπορείτε να επικοινωνείτε με χρήστες άλλων παρόχων δίνοντάς τους την πλήρη διεύθυνση XMPP σας.
+ Έχετε προσκληθεί στο %1$s. Θα σας καθοδηγήσουμε στη διαδικασία δημιουργίας λογαριασμού.\nΕπιλέγοντας τον %1$s ως πάροχο θα μπορείτε να επικοινωνείτε με χρήστες άλλων παρόχων δίνοντάς τους την πλήρη διεύθυνση XMPP σας.Έχετε προσκληθεί στο %1$s. Ένα όνομα χρήστη έχει ήδη επιλεγεί για εσάς. Θα σας καθοδηγήσουμε στη διαδικασία δημιουργίας λογαριασμού.\nΘα μπορείτε να επικοινωνείτε με χρήστες άλλων παρόχων δίνοντάς τους την πλήρη διεύθυνση XMPP σας.Η πρόσκλησή σας στον διακομιστή
+ Λάθος μορφοποίηση κώδικα παροχής
diff --git a/src/conversations/res/values-es/strings.xml b/src/conversations/res/values-es/strings.xml
index 577ca14bd..28a26cde4 100644
--- a/src/conversations/res/values-es/strings.xml
+++ b/src/conversations/res/values-es/strings.xml
@@ -9,4 +9,4 @@
Has sido invitado a %1$s. Un nombre de usuario ya ha sido escogido para ti. Te guiaremos durante el proceso de creación de la cuenta.\nPodrás comunicarte con otros usuarios de otros servidores proporcionándoles tu dirección XMPP completa. Tu invitación al servidorCódigo de abastecimiento formateado incorrectamente
-
+
diff --git a/src/conversations/res/values-fr/strings.xml b/src/conversations/res/values-fr/strings.xml
index 4086a3c55..e25d38d1d 100644
--- a/src/conversations/res/values-fr/strings.xml
+++ b/src/conversations/res/values-fr/strings.xml
@@ -9,4 +9,4 @@
Vous avez été invité à %1$s. Un nom d’utilisateur a déjà été choisi pour vous. Nous allons vous guider à travers le processus de création d’un compte.\nVous pourrez communiquer avec les utilisateurs des autres fournisseurs en leur donnant votre adresse XMPP complète.Votre invitation au serveurCode de provisionnement mal formaté
-
+
diff --git a/src/conversations/res/values-gl/strings.xml b/src/conversations/res/values-gl/strings.xml
index bd187d53b..477448e3f 100644
--- a/src/conversations/res/values-gl/strings.xml
+++ b/src/conversations/res/values-gl/strings.xml
@@ -9,4 +9,8 @@
Convidáronte a %1$s. Escollemos un nome de usuaria por ti. Guiarémoste no proceso de crear unha conta.\nPoderás comunicarte con usuarias de outros provedores cando lles digas o teu enderezo XMPP completo.O convite do teu servidorCódigo de aprovisionamento con formato non válido
+ Toca no botón compartir para convidar ó teu contacto a %1$s.
+ Se o contacto está preto de ti, pode escanear o código inferior para aceptar o teu convite.
+ Únete a %1$s e conversa conmigo: %2$s
+ Enviar convite a...
diff --git a/src/conversations/res/values-hu/strings.xml b/src/conversations/res/values-hu/strings.xml
index 40641c62b..ff215490d 100644
--- a/src/conversations/res/values-hu/strings.xml
+++ b/src/conversations/res/values-hu/strings.xml
@@ -8,4 +8,5 @@
Meghívást kapott a(z) %1$s kiszolgálóra. Végig fogjuk vezetni egy fiók létrehozásának folyamatán.\nHa a(z) %1$s kiszolgálót választja szolgáltatóként, akkor képes lesz más szolgáltatók felhasználóival is kommunikálni, ha megadja nekik a teljes XMPP-címét.Meghívást kapott a(z) %1$s kiszolgálóra. Már kiválasztottak Önnek egy felhasználónevet. Végig fogjuk vezetni egy fiók létrehozásának folyamatán.\nKépes lesz más szolgáltatók felhasználóival is kommunikálni, ha megadja nekik a teljes XMPP-címét.Az Ön kiszolgálómeghívása
+ Helytelenül formázott kiépítési kód
diff --git a/src/conversations/res/values-it/strings.xml b/src/conversations/res/values-it/strings.xml
index aa9fc670c..7d638efdc 100644
--- a/src/conversations/res/values-it/strings.xml
+++ b/src/conversations/res/values-it/strings.xml
@@ -11,4 +11,4 @@ In ogni caso per facilitare puoi creare facilmente un account su chat.sum7.eu, u
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.Il tuo invito al serverCodice di approvvigionamento formattato male
-
+
diff --git a/src/conversations/res/values-ja/strings.xml b/src/conversations/res/values-ja/strings.xml
new file mode 100644
index 000000000..0f493a365
--- /dev/null
+++ b/src/conversations/res/values-ja/strings.xml
@@ -0,0 +1,12 @@
+
+
+ XMPPプロバイダーを選択してください
+ conversations.imを利用する
+ アカウントを作成
+ XMPPアカウントをお持ちですか?既にほかのXMPPクライアントを利用しているか、Conversationsを利用したことがある場合はこちら。初めての方は、今すぐ新しいXMPPアカウントを作成できます。\nヒント: eメールのプロバイダーがXMPPアカウントも提供している場合があります。
+ XMPPは、プロバイダーに依存しないインスタントメッセージのプロトコルです。XMPPサーバーならどこでも、このクライアントを使用することができます。\nよろしければ、Conversationsに最適化されたプロバイダーconversations.im¹で簡単にアカウントを作成することもできます。
+ %1$sへ招待されました。アカウント作成手順をご案内します。 \n%1$sをプロバイダーに選択してほかのプロバイダーのユーザーと会話するには、XMPPのフルアドレスを相手にお知らせください。
+ %1$sへ招待されました。ユーザーネームは既に選択されています。アカウント作成手順をご案内します。 \nほかのプロバイダーのユーザーと会話するには、XMPPのフルアドレスを相手にお知らせください。
+ サーバーの招待
+ 仮コードの書式が不正です
+
\ No newline at end of file
diff --git a/src/conversations/res/values-pl/strings.xml b/src/conversations/res/values-pl/strings.xml
index 9108588df..b30048fec 100644
--- a/src/conversations/res/values-pl/strings.xml
+++ b/src/conversations/res/values-pl/strings.xml
@@ -9,4 +9,4 @@
Zostałeś zaproszony do %1$s. Nazwa użytkownika została już dla ciebie wybrana. Poprowadzimy ciebie przez proces tworzenia konta.\nBęziesz mógł komunikować się z innymi użytkownikami podając swój adres XMPP.Zaproszenie twojego serweraNiepoprawnie sformatowany kod zaopatrywania
-
+
diff --git a/src/conversations/res/values-pt-rBR/strings.xml b/src/conversations/res/values-pt-rBR/strings.xml
index 3c2468f88..a2420fad9 100644
--- a/src/conversations/res/values-pt-rBR/strings.xml
+++ b/src/conversations/res/values-pt-rBR/strings.xml
@@ -9,4 +9,4 @@
Você foi convidado para %1$s. Um nome de usuário já foi escolhido para você. Nós iremos guiá-lo ao longo do processo de criação de uma conta.\nVocê conseguirá se comunicar com usuários de outros provedores dando a eles seu endereço XMPP completo.Seu convite do servidorCódigo de provisionamento formatado de maneira imprópria
-
+
diff --git a/src/conversations/res/values-ro-rRO/strings.xml b/src/conversations/res/values-ro-rRO/strings.xml
index 60a96740d..52841d9a7 100644
--- a/src/conversations/res/values-ro-rRO/strings.xml
+++ b/src/conversations/res/values-ro-rRO/strings.xml
@@ -9,4 +9,8 @@
Ați fost invitați la %1$s. Un nume de utilizator a fost deja ales pentru dumneavoastră. Vă vom ghida prin procesul de creare al unui cont.\nVeți putea comunica cu utilizatorii altor furnizori oferindu-le adresa dumneavoastră completă XMPP.Invitația serverului dumneavoastrăCod de acces formatat necorespunzător
+ Atingeți butonul de partajare pentru a trimite contactului o invitație la %1$s.
+ Dacă e în apropiere, contactul poate scana codul de mai jos pentru a vă accepta invitația.
+ Alătură-te %1$s și discută cu mine: %2$s
+ Partajează invitația cu…
diff --git a/src/conversations/res/values-ru/strings.xml b/src/conversations/res/values-ru/strings.xml
index db0eabbe7..708e06df9 100644
--- a/src/conversations/res/values-ru/strings.xml
+++ b/src/conversations/res/values-ru/strings.xml
@@ -9,4 +9,4 @@
Вас пригласили на %1$s. Вам уже назначили имя пользователя. Мы проведём вас через процесс создания аккаунта. Этот аккаунт позволит вам общаться с пользователями и на этом, и на других серверах, используя ваш полный XMPP-адрес.Ваше приглашениеНеправильный формат кода
-
+
diff --git a/src/conversations/res/values-sr/strings.xml b/src/conversations/res/values-sr/strings.xml
new file mode 100644
index 000000000..b9e417ed4
--- /dev/null
+++ b/src/conversations/res/values-sr/strings.xml
@@ -0,0 +1,9 @@
+
+
+ Одаберите вашег ИксМПП провајдера
+ Користи conversations.im
+ Направи нови налог
+ Да ли већ имате ИксМПП налог? Извесно је да га имате ако користите неки ИксМПП клијент или сте раније користили Конверзацију. Ако немате, сада можете направити нови ИксМПП налог.\nСавет: неки поштански провајдери такође омогућавају и ИксМПП налоге.
+ ИксМПП је мрежа брзих порука, независна од провајдера. Овај клијент можете користити уз било који сервер по вашем избору.\nДа бисмо вам олакшали, омогућили смо креирање налога на conversations.im¹; провајдеру специјално прилаг.ођеном за коришћење уз Конверзацију
+ Ваша серверска позивница
+
\ No newline at end of file
diff --git a/src/conversations/res/values-tr-rTR/strings.xml b/src/conversations/res/values-tr-rTR/strings.xml
new file mode 100644
index 000000000..43b327ef1
--- /dev/null
+++ b/src/conversations/res/values-tr-rTR/strings.xml
@@ -0,0 +1,16 @@
+
+
+ XMPP sağlayıcınızı seçin
+ conversations.im kullan
+ Yeni hesap oluştur
+ Zaten bir XMPP hesabınız var mı? Bunun sebebi, zaten başka bir XMPP istemcisi kullanıyor oluşunuz veya Conversations\'ı önceden kullanmış olmanız olabilir. Eğer durum bu değilse şimdi yeni bir XMPP hesabı oluşturabilirsiniz.\nİpucu: Bağzı e-posta sağlayıcıları da XMPP hesapları kullanabilir.
+ XMPP; anlık yazışmalar için bağımsız bir sağlayıcıdır. Bu istemciyi istediğiniz herhangi bir XMPP sunucusu ile birlikte kullanabilirsiniz.\nAncak kullanım rahatlığı adına sizin için conversations.im¹; Conversations için özellikle tasarlanmış bir sağlayıcıda hesap açmanızı kolaylaştırdık.
+ %1$s sağlayıcısına davet edildiniz. Sizi hesap oluşturulması konusunda yönlendireceğiz.\n%1$s bir sağlayıcı olark seçildiğinde, başka sağlayıcılar kullanan kullanıcılarla, onlara tam XMPP adresinizi vererek iletişim kurabileceksiniz.
+ %1$s sağlayıcısına davet edildiniz. Sizin için zaten bir kullanıcı adı seçildi. Sizi hesap oluşturulması konusunda yönlendireceğiz.\nBaşka sağlayıcılar kullanan kullanıcılarla, onlara tam XMPP adresinizi vererek iletişim kurabileceksiniz.
+ Sunucu davetiyeniz
+ Yanlış ayarlanmış düzenleme kodu
+ Kişinize, %1$s grubuna davet etmek için Paylaş düğmesine basın.
+ Kişiniz yakınınızda ise, aşağıdaki kodu tarayak daveti kabul edebilirler.
+ %1$s grubuna katıl ve benimle sohpet et: %2$s
+ Daveti şununla paylaş...
+
\ No newline at end of file
diff --git a/src/conversations/res/values-uk/strings.xml b/src/conversations/res/values-uk/strings.xml
index 7023bf3ec..b2e36f42c 100644
--- a/src/conversations/res/values-uk/strings.xml
+++ b/src/conversations/res/values-uk/strings.xml
@@ -9,4 +9,4 @@
Вас запросили до %1$s. Для вас створено ім\'я користувача. Ми проведемо вас крок за кроком, щоб створити обліковий запис.\nВи зможете спілкуватися з користувачами інших постачальників, для цього повідомите їм свою повну адресу XMPP.Ваше запрошення до сервераНеправильно відформатований код забезпечення
-
+
diff --git a/src/conversations/res/values-zh-rCN/strings.xml b/src/conversations/res/values-zh-rCN/strings.xml
index 989810dec..e423d7219 100644
--- a/src/conversations/res/values-zh-rCN/strings.xml
+++ b/src/conversations/res/values-zh-rCN/strings.xml
@@ -9,4 +9,8 @@
您已受邀参加%1$s。 已经为您选择了一个用户名。 我们将指导您完成创建帐户的过程。\n您可以通过向其他提供商的用户提供完整的XMPP地址来与他们进行交流。你的服务器邀请格式不正确的配置代码
+ 点击分享按钮向您的联系人发送加入 %1$s 的邀请。
+ 如果你的联系人在附近,他们也可以扫描下面的代码来接受你的邀请。
+ 加入 %1$s 和我聊天:%2$s
+ 分享邀请
diff --git a/src/conversations/res/values/strings.xml b/src/conversations/res/values/strings.xml
index 23971f875..c2afd878a 100644
--- a/src/conversations/res/values/strings.xml
+++ b/src/conversations/res/values/strings.xml
@@ -9,4 +9,8 @@
You have been invited to %1$s. A username has already been picked for you. We will guide you through the process of creating an account.\nYou will be able to communicate with users of other providers by giving them your full XMPP address.Your server invitationImproperly formatted provisioning code
+ Tap the share button to send your contact an invitation to %1$s.
+ If your contact is nearby, they can also scan the code below to accept your invitation.
+ Join %1$s and chat with me: %2$s
+ Share invite with…
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index ffd06ec0f..16731574e 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -7,7 +7,9 @@
-
+
@@ -57,10 +59,12 @@
android:hardwareAccelerated="true"
android:icon="@mipmap/new_launcher"
android:label="@string/app_name"
+ android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_configuration"
+ android:requestLegacyExternalStorage="true"
android:theme="@style/ConversationsTheme"
tools:replace="android:label"
- tools:targetApi="o">
+ tools:targetApi="q">
getTags(Context context) {
- final ArrayList tags = new ArrayList<>();
- for (final String group : getGroups(true)) {
- tags.add(new Tag(group, UIHelper.getColorForName(group)));
- }
- Presence.Status status = getShownStatus();
- if (status != Presence.Status.OFFLINE) {
- tags.add(UIHelper.getTagForStatus(context, status));
- }
- if (isBlocked()) {
- tags.add(new Tag(context.getString(R.string.blocked), 0xff2e2f3b));
- }
- return tags;
- }
-
- public boolean match(Context context, String needle) {
- if (TextUtils.isEmpty(needle)) {
- return true;
- }
- needle = needle.toLowerCase(Locale.US).trim();
- String[] parts = needle.split("\\s+");
- if (parts.length > 1) {
- for (String part : parts) {
- if (!match(context, part)) {
- return false;
- }
- }
- return true;
- } else {
- return jid.toString().contains(needle) ||
- getDisplayName().toLowerCase(Locale.US).contains(needle) ||
- matchInTag(context, needle);
- }
- }
-
- private boolean matchInTag(Context context, String needle) {
- needle = needle.toLowerCase(Locale.US);
- for (Tag tag : getTags(context)) {
- if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
- return true;
- }
- }
- return false;
- }
-
- public ContentValues getContentValues() {
- synchronized (this.keys) {
- final ContentValues values = new ContentValues();
- values.put(ACCOUNT, accountUuid);
- values.put(SYSTEMNAME, systemName);
- values.put(SERVERNAME, serverName);
- values.put(PRESENCE_NAME, presenceName);
- values.put(JID, jid.toString());
- values.put(OPTIONS, subscription);
- values.put(SYSTEMACCOUNT, systemAccount != null ? systemAccount.toString() : null);
- values.put(PHOTOURI, photoUri);
- values.put(KEYS, keys.toString());
- values.put(AVATAR, avatar == null ? null : avatar.getFilename());
- values.put(LAST_PRESENCE, mLastPresence);
- values.put(LAST_TIME, mLastseen);
- values.put(GROUPS, groups.toString());
- return values;
- }
- }
-
- public Account getAccount() {
- return this.account;
- }
-
- public void setAccount(Account account) {
- this.account = account;
- this.accountUuid = account.getUuid();
- }
-
- public Presences getPresences() {
- return this.presences;
- }
-
- public void updatePresence(final String resource, final Presence presence) {
- this.presences.updatePresence(resource, presence);
- }
-
- public void removePresence(final String resource) {
- this.presences.removePresence(resource);
- }
-
- public void clearPresences() {
- this.presences.clearPresences();
- this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
- }
-
- public Presence.Status getShownStatus() {
- return this.presences.getShownStatus();
- }
-
- public boolean setPhotoUri(String uri) {
- if (uri != null && !uri.equals(this.photoUri)) {
- this.photoUri = uri;
- return true;
- } else if (this.photoUri != null && uri == null) {
- this.photoUri = null;
- return true;
- } else {
- return false;
- }
- }
-
- public void setServerName(String serverName) {
- this.serverName = serverName;
- }
-
- public boolean setSystemName(String systemName) {
- final String old = getDisplayName();
- this.systemName = systemName;
- return !old.equals(getDisplayName());
- }
-
- public boolean setPresenceName(String presenceName) {
- final String old = getDisplayName();
- this.presenceName = presenceName;
- return !old.equals(getDisplayName());
- }
-
- public Uri getSystemAccount() {
- return systemAccount;
- }
-
- public void setSystemAccount(Uri lookupUri) {
- this.systemAccount = lookupUri;
- }
-
- private Collection getGroups(final boolean unique) {
- final Collection groups = unique ? new HashSet<>() : new ArrayList<>();
- for (int i = 0; i < this.groups.length(); ++i) {
- try {
- groups.add(this.groups.getString(i));
- } catch (final JSONException ignored) {
- }
- }
- return groups;
- }
-
- public long getPgpKeyId() {
- synchronized (this.keys) {
- if (this.keys.has("pgp_keyid")) {
- try {
- return this.keys.getLong("pgp_keyid");
- } catch (JSONException e) {
- return 0;
- }
- } else {
- return 0;
- }
- }
- }
-
- public boolean setPgpKeyId(long keyId) {
- final long previousKeyId = getPgpKeyId();
- synchronized (this.keys) {
- try {
- this.keys.put("pgp_keyid", keyId);
- return previousKeyId != keyId;
- } catch (final JSONException ignored) {
- }
- }
- return false;
- }
-
- public void setOption(int option) {
- this.subscription |= 1 << option;
- }
-
- public void resetOption(int option) {
- this.subscription &= ~(1 << option);
- }
-
- public boolean getOption(int option) {
- return ((this.subscription & (1 << option)) != 0);
- }
-
- public boolean showInRoster() {
- return (this.getOption(Contact.Options.IN_ROSTER) && (!this
- .getOption(Contact.Options.DIRTY_DELETE)))
- || (this.getOption(Contact.Options.DIRTY_PUSH));
- }
-
- public boolean showInContactList() {
- return showInRoster()
- || getOption(Options.SYNCED_VIA_OTHER)
- || (QuickConversationsService.isQuicksy() && systemAccount != null);
- }
-
- public void parseSubscriptionFromElement(Element item) {
- String ask = item.getAttribute("ask");
- String subscription = item.getAttribute("subscription");
-
- if (subscription == null) {
- this.resetOption(Options.FROM);
- this.resetOption(Options.TO);
- } else {
- switch (subscription) {
- case "to":
- this.resetOption(Options.FROM);
- this.setOption(Options.TO);
- break;
- case "from":
- this.resetOption(Options.TO);
- this.setOption(Options.FROM);
- this.resetOption(Options.PREEMPTIVE_GRANT);
- this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
- break;
- case "both":
- this.setOption(Options.TO);
- this.setOption(Options.FROM);
- this.resetOption(Options.PREEMPTIVE_GRANT);
- this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
- break;
- case "none":
- this.resetOption(Options.FROM);
- this.resetOption(Options.TO);
- break;
- }
- }
-
- // do NOT override asking if pending push request
- if (!this.getOption(Contact.Options.DIRTY_PUSH)) {
- if ((ask != null) && (ask.equals("subscribe"))) {
- this.setOption(Contact.Options.ASKING);
- } else {
- this.resetOption(Contact.Options.ASKING);
- }
- }
- }
-
- public void parseGroupsFromElement(Element item) {
- this.groups = new JSONArray();
- for (Element element : item.getChildren()) {
- if (element.getName().equals("group") && element.getContent() != null) {
- this.groups.put(element.getContent());
- }
- }
- }
-
- public Element asElement() {
- final Element item = new Element("item");
- item.setAttribute("jid", this.jid);
- if (this.serverName != null) {
- item.setAttribute("name", this.serverName);
- }
- for (String group : getGroups(false)) {
- item.addChild("group").setContent(group);
- }
- return item;
- }
-
- @Override
- public int compareTo(@NonNull final ListItem another) {
- return this.getDisplayName().compareToIgnoreCase(
- another.getDisplayName());
- }
-
- public String getServer() {
- return getJid().getDomain().toEscapedString();
- }
-
- public boolean setAvatar(Avatar avatar) {
- return setAvatar(avatar, false);
- }
-
- public boolean setAvatar(Avatar avatar, boolean previouslyOmittedPepFetch) {
- if (this.avatar != null && this.avatar.equals(avatar)) {
- return false;
- } else {
- if (!previouslyOmittedPepFetch && this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) {
- return false;
- }
- this.avatar = avatar;
- return true;
- }
- }
-
- public String getAvatarFilename() {
- return avatar == null ? null : avatar.getFilename();
- }
-
- public Avatar getAvatar() {
- return avatar;
- }
-
- public boolean mutualPresenceSubscription() {
- return getOption(Options.FROM) && getOption(Options.TO);
- }
-
- @Override
- public boolean isBlocked() {
- return getAccount().isBlocked(this);
- }
-
- @Override
- public boolean isDomainBlocked() {
- return getAccount().isBlocked(this.getJid().getDomain());
- }
-
- @Override
- public Jid getBlockedJid() {
- if (isDomainBlocked()) {
- return getJid().getDomain();
- } else {
- return getJid();
- }
- }
-
- public boolean isSelf() {
- return account.getJid().asBareJid().equals(jid.asBareJid());
- }
-
- boolean isOwnServer() {
- return account.getJid().getDomain().equals(jid.asBareJid());
- }
-
- public void setCommonName(String cn) {
- this.commonName = cn;
- }
-
- public void flagActive() {
- this.mActive = true;
- }
-
- public void flagInactive() {
- this.mActive = false;
- }
-
- public boolean isActive() {
- return this.mActive;
- }
-
- public boolean setLastseen(long timestamp) {
- if (timestamp > this.mLastseen) {
- this.mLastseen = timestamp;
- return true;
- } else {
- return false;
- }
- }
-
- public long getLastseen() {
- return this.mLastseen;
- }
-
- public void setLastResource(String resource) {
- this.mLastPresence = resource;
- }
-
- public String getLastResource() {
- return this.mLastPresence;
- }
-
- public String getServerName() {
- return serverName;
- }
-
- public synchronized boolean setPhoneContact(AbstractPhoneContact phoneContact) {
- setOption(getOption(phoneContact.getClass()));
- setSystemAccount(phoneContact.getLookupUri());
- boolean changed = setSystemName(phoneContact.getDisplayName());
- changed |= setPhotoUri(phoneContact.getPhotoUri());
- return changed;
- }
-
- public synchronized boolean unsetPhoneContact(Class clazz) {
- resetOption(getOption(clazz));
- boolean changed = false;
- if (!getOption(Options.SYNCED_VIA_ADDRESSBOOK) && !getOption(Options.SYNCED_VIA_OTHER)) {
- setSystemAccount(null);
- changed |= setPhotoUri(null);
- changed |= setSystemName(null);
- }
- return changed;
- }
-
- public static int getOption(Class extends AbstractPhoneContact> clazz) {
- if (clazz == JabberIdContact.class) {
- return Options.SYNCED_VIA_ADDRESSBOOK;
- } else {
- return Options.SYNCED_VIA_OTHER;
- }
- }
-
- @Override
- public int getAvatarBackgroundColor() {
- return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName());
- }
-
- @Override
- public String getAvatarName() {
- return getDisplayName();
- }
-
- public boolean hasAvatarOrPresenceName() {
- return (avatar != null && avatar.getFilename() != null) || presenceName != null;
- }
-
- public final class Options {
- public static final int TO = 0;
- public static final int FROM = 1;
- public static final int ASKING = 2;
- public static final int PREEMPTIVE_GRANT = 3;
- public static final int IN_ROSTER = 4;
- public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
- public static final int DIRTY_PUSH = 6;
- public static final int DIRTY_DELETE = 7;
- private static final int SYNCED_VIA_ADDRESSBOOK = 8;
- public static final int SYNCED_VIA_OTHER = 9;
- }
+ public static final String TABLENAME = "contacts";
+
+ public static final String SYSTEMNAME = "systemname";
+ public static final String SERVERNAME = "servername";
+ public static final String PRESENCE_NAME = "presence_name";
+ public static final String JID = "jid";
+ public static final String OPTIONS = "options";
+ public static final String SYSTEMACCOUNT = "systemaccount";
+ public static final String PHOTOURI = "photouri";
+ public static final String KEYS = "pgpkey";
+ public static final String ACCOUNT = "accountUuid";
+ public static final String AVATAR = "avatar";
+ public static final String LAST_PRESENCE = "last_presence";
+ public static final String LAST_TIME = "last_time";
+ public static final String GROUPS = "groups";
+ private String accountUuid;
+ private String systemName;
+ private String serverName;
+ private String presenceName;
+ private String commonName;
+ protected Jid jid;
+ private int subscription = 0;
+ private Uri systemAccount;
+ private String photoUri;
+ private final JSONObject keys;
+ private JSONArray groups = new JSONArray();
+ private final Presences presences = new Presences();
+ protected Account account;
+ protected Avatar avatar;
+
+ private boolean mActive = false;
+ private long mLastseen = 0;
+ private String mLastPresence = null;
+
+ public Contact(final String account, final String systemName, final String serverName, final String presenceName,
+ final Jid jid, final int subscription, final String photoUri,
+ final Uri systemAccount, final String keys, final String avatar, final long lastseen,
+ final String presence, final String groups) {
+ this.accountUuid = account;
+ this.systemName = systemName;
+ this.serverName = serverName;
+ this.presenceName = presenceName;
+ this.jid = jid;
+ this.subscription = subscription;
+ this.photoUri = photoUri;
+ this.systemAccount = systemAccount;
+ JSONObject tmpJsonObject;
+ try {
+ tmpJsonObject = (keys == null ? new JSONObject("") : new JSONObject(keys));
+ } catch (JSONException e) {
+ tmpJsonObject = new JSONObject();
+ }
+ this.keys = tmpJsonObject;
+ if (avatar != null) {
+ this.avatar = new Avatar();
+ this.avatar.sha1sum = avatar;
+ this.avatar.origin = Avatar.Origin.VCARD; //always assume worst
+ }
+ try {
+ this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
+ } catch (JSONException e) {
+ this.groups = new JSONArray();
+ }
+ this.mLastseen = lastseen;
+ this.mLastPresence = presence;
+ }
+
+ public Contact(final Jid jid) {
+ this.jid = jid;
+ this.keys = new JSONObject();
+ }
+
+ public static Contact fromCursor(final Cursor cursor) {
+ final Jid jid;
+ try {
+ jid = Jid.of(cursor.getString(cursor.getColumnIndex(JID)));
+ } catch (final IllegalArgumentException e) {
+ // TODO: Borked DB... handle this somehow?
+ return null;
+ }
+ Uri systemAccount;
+ try {
+ systemAccount = Uri.parse(cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)));
+ } catch (Exception e) {
+ systemAccount = null;
+ }
+ return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)),
+ cursor.getString(cursor.getColumnIndex(SYSTEMNAME)),
+ cursor.getString(cursor.getColumnIndex(SERVERNAME)),
+ cursor.getString(cursor.getColumnIndex(PRESENCE_NAME)),
+ jid,
+ cursor.getInt(cursor.getColumnIndex(OPTIONS)),
+ cursor.getString(cursor.getColumnIndex(PHOTOURI)),
+ systemAccount,
+ cursor.getString(cursor.getColumnIndex(KEYS)),
+ cursor.getString(cursor.getColumnIndex(AVATAR)),
+ cursor.getLong(cursor.getColumnIndex(LAST_TIME)),
+ cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)),
+ cursor.getString(cursor.getColumnIndex(GROUPS)));
+ }
+
+ public String getDisplayName() {
+ if (Config.X509_VERIFICATION && !TextUtils.isEmpty(this.commonName)) {
+ return this.commonName;
+ } else if (!TextUtils.isEmpty(this.systemName)) {
+ return this.systemName;
+ } else if (!TextUtils.isEmpty(this.serverName)) {
+ return this.serverName;
+ } else if (!TextUtils.isEmpty(this.presenceName) && ((QuickConversationsService.isQuicksy() && JidHelper.isQuicksyDomain(jid.getDomain())) || mutualPresenceSubscription())) {
+ return this.presenceName;
+ } else if (jid.getLocal() != null) {
+ return JidHelper.localPartOrFallback(jid);
+ } else {
+ return jid.getDomain().toEscapedString();
+ }
+ }
+
+ public String getPublicDisplayName() {
+ if (!TextUtils.isEmpty(this.presenceName)) {
+ return this.presenceName;
+ } else if (jid.getLocal() != null) {
+ return JidHelper.localPartOrFallback(jid);
+ } else {
+ return jid.getDomain().toEscapedString();
+ }
+ }
+
+ public String getProfilePhoto() {
+ return this.photoUri;
+ }
+
+ public Jid getJid() {
+ return jid;
+ }
+
+ @Override
+ public List getTags(Context context) {
+ final ArrayList tags = new ArrayList<>();
+ for (final String group : getGroups(true)) {
+ tags.add(new Tag(group, UIHelper.getColorForName(group)));
+ }
+ Presence.Status status = getShownStatus();
+ if (status != Presence.Status.OFFLINE) {
+ tags.add(UIHelper.getTagForStatus(context, status));
+ }
+ if (isBlocked()) {
+ tags.add(new Tag(context.getString(R.string.blocked), 0xff2e2f3b));
+ }
+ return tags;
+ }
+
+ public boolean match(Context context, String needle) {
+ if (TextUtils.isEmpty(needle)) {
+ return true;
+ }
+ needle = needle.toLowerCase(Locale.US).trim();
+ String[] parts = needle.split("\\s+");
+ if (parts.length > 1) {
+ for (String part : parts) {
+ if (!match(context, part)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return jid.toString().contains(needle) ||
+ getDisplayName().toLowerCase(Locale.US).contains(needle) ||
+ matchInTag(context, needle);
+ }
+ }
+
+ private boolean matchInTag(Context context, String needle) {
+ needle = needle.toLowerCase(Locale.US);
+ for (Tag tag : getTags(context)) {
+ if (tag.getName().toLowerCase(Locale.US).contains(needle)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public ContentValues getContentValues() {
+ synchronized (this.keys) {
+ final ContentValues values = new ContentValues();
+ values.put(ACCOUNT, accountUuid);
+ values.put(SYSTEMNAME, systemName);
+ values.put(SERVERNAME, serverName);
+ values.put(PRESENCE_NAME, presenceName);
+ values.put(JID, jid.toString());
+ values.put(OPTIONS, subscription);
+ values.put(SYSTEMACCOUNT, systemAccount != null ? systemAccount.toString() : null);
+ values.put(PHOTOURI, photoUri);
+ values.put(KEYS, keys.toString());
+ values.put(AVATAR, avatar == null ? null : avatar.getFilename());
+ values.put(LAST_PRESENCE, mLastPresence);
+ values.put(LAST_TIME, mLastseen);
+ values.put(GROUPS, groups.toString());
+ return values;
+ }
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ public void setAccount(Account account) {
+ this.account = account;
+ this.accountUuid = account.getUuid();
+ }
+
+ public Presences getPresences() {
+ return this.presences;
+ }
+
+ public void updatePresence(final String resource, final Presence presence) {
+ this.presences.updatePresence(resource, presence);
+ }
+
+ public void removePresence(final String resource) {
+ this.presences.removePresence(resource);
+ }
+
+ public void clearPresences() {
+ this.presences.clearPresences();
+ this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
+ }
+
+ public Presence.Status getShownStatus() {
+ return this.presences.getShownStatus();
+ }
+
+ public boolean setPhotoUri(String uri) {
+ if (uri != null && !uri.equals(this.photoUri)) {
+ this.photoUri = uri;
+ return true;
+ } else if (this.photoUri != null && uri == null) {
+ this.photoUri = null;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void setServerName(String serverName) {
+ this.serverName = serverName;
+ }
+
+ public boolean setSystemName(String systemName) {
+ final String old = getDisplayName();
+ this.systemName = systemName;
+ return !old.equals(getDisplayName());
+ }
+
+ public boolean setPresenceName(String presenceName) {
+ final String old = getDisplayName();
+ this.presenceName = presenceName;
+ return !old.equals(getDisplayName());
+ }
+
+ public Uri getSystemAccount() {
+ return systemAccount;
+ }
+
+ public void setSystemAccount(Uri lookupUri) {
+ this.systemAccount = lookupUri;
+ }
+
+ private Collection getGroups(final boolean unique) {
+ final Collection groups = unique ? new HashSet<>() : new ArrayList<>();
+ for (int i = 0; i < this.groups.length(); ++i) {
+ try {
+ groups.add(this.groups.getString(i));
+ } catch (final JSONException ignored) {
+ }
+ }
+ return groups;
+ }
+
+ public long getPgpKeyId() {
+ synchronized (this.keys) {
+ if (this.keys.has("pgp_keyid")) {
+ try {
+ return this.keys.getLong("pgp_keyid");
+ } catch (JSONException e) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ public boolean setPgpKeyId(long keyId) {
+ final long previousKeyId = getPgpKeyId();
+ synchronized (this.keys) {
+ try {
+ this.keys.put("pgp_keyid", keyId);
+ return previousKeyId != keyId;
+ } catch (final JSONException ignored) {
+ }
+ }
+ return false;
+ }
+
+ public void setOption(int option) {
+ this.subscription |= 1 << option;
+ }
+
+ public void resetOption(int option) {
+ this.subscription &= ~(1 << option);
+ }
+
+ public boolean getOption(int option) {
+ return ((this.subscription & (1 << option)) != 0);
+ }
+
+ public boolean showInRoster() {
+ return (this.getOption(Contact.Options.IN_ROSTER) && (!this
+ .getOption(Contact.Options.DIRTY_DELETE)))
+ || (this.getOption(Contact.Options.DIRTY_PUSH));
+ }
+
+ public boolean showInContactList() {
+ return showInRoster()
+ || getOption(Options.SYNCED_VIA_OTHER)
+ || (QuickConversationsService.isQuicksy() && systemAccount != null);
+ }
+
+ public void parseSubscriptionFromElement(Element item) {
+ String ask = item.getAttribute("ask");
+ String subscription = item.getAttribute("subscription");
+
+ if (subscription == null) {
+ this.resetOption(Options.FROM);
+ this.resetOption(Options.TO);
+ } else {
+ switch (subscription) {
+ case "to":
+ this.resetOption(Options.FROM);
+ this.setOption(Options.TO);
+ break;
+ case "from":
+ this.resetOption(Options.TO);
+ this.setOption(Options.FROM);
+ this.resetOption(Options.PREEMPTIVE_GRANT);
+ this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
+ break;
+ case "both":
+ this.setOption(Options.TO);
+ this.setOption(Options.FROM);
+ this.resetOption(Options.PREEMPTIVE_GRANT);
+ this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
+ break;
+ case "none":
+ this.resetOption(Options.FROM);
+ this.resetOption(Options.TO);
+ break;
+ }
+ }
+
+ // do NOT override asking if pending push request
+ if (!this.getOption(Contact.Options.DIRTY_PUSH)) {
+ if ((ask != null) && (ask.equals("subscribe"))) {
+ this.setOption(Contact.Options.ASKING);
+ } else {
+ this.resetOption(Contact.Options.ASKING);
+ }
+ }
+ }
+
+ public void parseGroupsFromElement(Element item) {
+ this.groups = new JSONArray();
+ for (Element element : item.getChildren()) {
+ if (element.getName().equals("group") && element.getContent() != null) {
+ this.groups.put(element.getContent());
+ }
+ }
+ }
+
+ public Element asElement() {
+ final Element item = new Element("item");
+ item.setAttribute("jid", this.jid);
+ if (this.serverName != null) {
+ item.setAttribute("name", this.serverName);
+ }
+ for (String group : getGroups(false)) {
+ item.addChild("group").setContent(group);
+ }
+ return item;
+ }
+
+ @Override
+ public int compareTo(@NonNull final ListItem another) {
+ return this.getDisplayName().compareToIgnoreCase(
+ another.getDisplayName());
+ }
+
+ public String getServer() {
+ return getJid().getDomain().toEscapedString();
+ }
+
+ public void setAvatar(Avatar avatar) {
+ setAvatar(avatar, false);
+ }
+
+ public void setAvatar(Avatar avatar, boolean previouslyOmittedPepFetch) {
+ if (this.avatar != null && this.avatar.equals(avatar)) {
+ return;
+ }
+ if (!previouslyOmittedPepFetch && this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) {
+ return;
+ }
+ this.avatar = avatar;
+ }
+
+ public String getAvatarFilename() {
+ return avatar == null ? null : avatar.getFilename();
+ }
+
+ public Avatar getAvatar() {
+ return avatar;
+ }
+
+ public boolean mutualPresenceSubscription() {
+ return getOption(Options.FROM) && getOption(Options.TO);
+ }
+
+ @Override
+ public boolean isBlocked() {
+ return getAccount().isBlocked(this);
+ }
+
+ @Override
+ public boolean isDomainBlocked() {
+ return getAccount().isBlocked(this.getJid().getDomain());
+ }
+
+ @Override
+ public Jid getBlockedJid() {
+ if (isDomainBlocked()) {
+ return getJid().getDomain();
+ } else {
+ return getJid();
+ }
+ }
+
+ public boolean isSelf() {
+ return account.getJid().asBareJid().equals(jid.asBareJid());
+ }
+
+ boolean isOwnServer() {
+ return account.getJid().getDomain().equals(jid.asBareJid());
+ }
+
+ public void setCommonName(String cn) {
+ this.commonName = cn;
+ }
+
+ public void flagActive() {
+ this.mActive = true;
+ }
+
+ public void flagInactive() {
+ this.mActive = false;
+ }
+
+ public boolean isActive() {
+ return this.mActive;
+ }
+
+ public boolean setLastseen(long timestamp) {
+ if (timestamp > this.mLastseen) {
+ this.mLastseen = timestamp;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public long getLastseen() {
+ return this.mLastseen;
+ }
+
+ public void setLastResource(String resource) {
+ this.mLastPresence = resource;
+ }
+
+ public String getLastResource() {
+ return this.mLastPresence;
+ }
+
+ public String getServerName() {
+ return serverName;
+ }
+
+ public synchronized boolean setPhoneContact(AbstractPhoneContact phoneContact) {
+ setOption(getOption(phoneContact.getClass()));
+ setSystemAccount(phoneContact.getLookupUri());
+ boolean changed = setSystemName(phoneContact.getDisplayName());
+ changed |= setPhotoUri(phoneContact.getPhotoUri());
+ return changed;
+ }
+
+ public synchronized boolean unsetPhoneContact(Class extends AbstractPhoneContact> clazz) {
+ resetOption(getOption(clazz));
+ boolean changed = false;
+ if (!getOption(Options.SYNCED_VIA_ADDRESSBOOK) && !getOption(Options.SYNCED_VIA_OTHER)) {
+ setSystemAccount(null);
+ changed |= setPhotoUri(null);
+ changed |= setSystemName(null);
+ }
+ return changed;
+ }
+
+ public static int getOption(Class extends AbstractPhoneContact> clazz) {
+ if (clazz == JabberIdContact.class) {
+ return Options.SYNCED_VIA_ADDRESSBOOK;
+ } else {
+ return Options.SYNCED_VIA_OTHER;
+ }
+ }
+
+ @Override
+ public int getAvatarBackgroundColor() {
+ return UIHelper.getColorForName(jid != null ? jid.asBareJid().toString() : getDisplayName());
+ }
+
+ @Override
+ public String getAvatarName() {
+ return getDisplayName();
+ }
+
+ public boolean hasAvatarOrPresenceName() {
+ return (avatar != null && avatar.getFilename() != null) || presenceName != null;
+ }
+
+ public final class Options {
+ public static final int TO = 0;
+ public static final int FROM = 1;
+ public static final int ASKING = 2;
+ public static final int PREEMPTIVE_GRANT = 3;
+ public static final int IN_ROSTER = 4;
+ public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
+ public static final int DIRTY_PUSH = 6;
+ public static final int DIRTY_DELETE = 7;
+ private static final int SYNCED_VIA_ADDRESSBOOK = 8;
+ public static final int SYNCED_VIA_OTHER = 9;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index cdd891966..702873fe2 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -550,7 +550,15 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
}
public boolean isRead() {
- return (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead();
+ synchronized (this.messages) {
+ for(final Message message : Lists.reverse(this.messages)) {
+ if (message.isRead() && message.getType() == Message.TYPE_RTP_SESSION) {
+ continue;
+ }
+ return message.isRead();
+ }
+ return true;
+ }
}
public List markRead(String upToUuid) {
@@ -1010,8 +1018,11 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public int unreadCount() {
synchronized (this.messages) {
int count = 0;
- for (int i = this.messages.size() - 1; i >= 0; --i) {
- if (this.messages.get(i).isRead()) {
+ for(final Message message : Lists.reverse(this.messages)) {
+ if (message.isRead()) {
+ if (message.getType() == Message.TYPE_RTP_SESSION) {
+ continue;
+ }
return count;
}
++count;
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index 51a8438db..ab9c64940 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -15,10 +15,10 @@ import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
@@ -113,7 +113,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
private Message mPreviousMessage = null;
private String axolotlFingerprint = null;
private String errorMessage = null;
- private Set readByMarkers = new HashSet<>();
+ private Set readByMarkers = new CopyOnWriteArraySet<>();
private Boolean isGeoUri = null;
private Boolean isEmojisOnly = null;
@@ -206,7 +206,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
this.edits = Edit.fromJson(edited);
this.oob = oob;
this.errorMessage = errorMessage;
- this.readByMarkers = readByMarkers == null ? new HashSet<>() : readByMarkers;
+ this.readByMarkers = readByMarkers == null ? new CopyOnWriteArraySet<>() : readByMarkers;
this.markable = markable;
this.deleted = deleted;
this.bodyLanguage = bodyLanguage;
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index 10caa4620..1072eeeb9 100644
--- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
@@ -211,6 +211,10 @@ public class MucOptions {
return conversation.getBooleanAttribute(Conversation.ATTRIBUTE_MODERATED, false);
}
+ public boolean stableId() {
+ return getFeatures().contains("http://jabber.org/protocol/muc#stable_id");
+ }
+
public User deleteUser(Jid jid) {
User user = findUserByFullJid(jid);
if (user != null) {
diff --git a/src/main/java/eu/siacs/conversations/entities/ReadByMarker.java b/src/main/java/eu/siacs/conversations/entities/ReadByMarker.java
index 9e1aaa5c7..4b1254f07 100644
--- a/src/main/java/eu/siacs/conversations/entities/ReadByMarker.java
+++ b/src/main/java/eu/siacs/conversations/entities/ReadByMarker.java
@@ -5,8 +5,8 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.util.Collection;
-import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
import eu.siacs.conversations.xmpp.Jid;
@@ -66,8 +66,8 @@ public class ReadByMarker {
return jsonObject;
}
- public static Set fromJson(JSONArray jsonArray) {
- HashSet readByMarkers = new HashSet<>();
+ public static Set fromJson(final JSONArray jsonArray) {
+ final Set readByMarkers = new CopyOnWriteArraySet<>();
for(int i = 0; i < jsonArray.length(); ++i) {
try {
readByMarkers.add(fromJson(jsonArray.getJSONObject(i)));
@@ -100,7 +100,7 @@ public class ReadByMarker {
}
public static Set from(Collection users) {
- final HashSet markers = new HashSet<>();
+ final Set markers = new CopyOnWriteArraySet<>();
for(MucOptions.User user : users) {
markers.add(from(user));
}
@@ -125,21 +125,21 @@ public class ReadByMarker {
public static Set fromJsonString(String json) {
try {
return fromJson(new JSONArray(json));
- } catch (JSONException | NullPointerException e) {
- return new HashSet<>();
+ } catch (final JSONException | NullPointerException e) {
+ return new CopyOnWriteArraySet<>();
}
}
- public static JSONArray toJson(Set readByMarkers) {
- JSONArray jsonArray = new JSONArray();
- for(ReadByMarker marker : readByMarkers) {
+ public static JSONArray toJson(final Set readByMarkers) {
+ final JSONArray jsonArray = new JSONArray();
+ for(final ReadByMarker marker : readByMarkers) {
jsonArray.put(marker.toJson());
}
return jsonArray;
}
- public static boolean contains(ReadByMarker needle, Set readByMarkers) {
- for(ReadByMarker marker : readByMarkers) {
+ public static boolean contains(ReadByMarker needle, final Set readByMarkers) {
+ for(final ReadByMarker marker : readByMarkers) {
if (marker.realJid != null && needle.realJid != null) {
if (marker.realJid.asBareJid().equals(needle.realJid.asBareJid())) {
return true;
@@ -163,7 +163,7 @@ public class ReadByMarker {
}
public static boolean allUsersRepresented(Collection users, Set markers, ReadByMarker marker) {
- HashSet markersCopy = new HashSet<>(markers);
+ final Set markersCopy = new CopyOnWriteArraySet<>(markers);
markersCopy.add(marker);
return allUsersRepresented(users, markersCopy);
}
diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
index e05cbae71..3f1c5aa49 100644
--- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
@@ -18,12 +18,12 @@ import eu.siacs.conversations.http.P1S3UrlStreamHandler;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
+import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
-import eu.siacs.conversations.xmpp.Jid;
public class MessageGenerator extends AbstractGenerator {
private static final String OMEMO_FALLBACK_MESSAGE = "I sent you an OMEMO encrypted message but your client doesn’t seem to support that. Find more information on https://conversations.im/omemo";
@@ -58,7 +58,9 @@ public class MessageGenerator extends AbstractGenerator {
}
packet.setFrom(account.getJid());
packet.setId(message.getUuid());
- packet.addChild("origin-id", Namespace.STANZA_IDS).setAttribute("id", message.getUuid());
+ if (conversation.getMode() == Conversational.MODE_SINGLE || message.isPrivateMessage() || !conversation.getMucOptions().stableId()) {
+ packet.addChild("origin-id", Namespace.STANZA_IDS).setAttribute("id", message.getUuid());
+ }
if (message.edited()) {
packet.addChild("replace", "urn:xmpp:message-correct:0").setAttribute("id", message.getEditedIdWireFormat());
}
@@ -223,13 +225,13 @@ public class MessageGenerator extends AbstractGenerator {
return packet;
}
- public MessagePacket received(Account account, MessagePacket originalMessage, ArrayList namespaces, int type) {
- MessagePacket receivedPacket = new MessagePacket();
+ public MessagePacket received(Account account, final Jid from, final String id, ArrayList namespaces, int type) {
+ final MessagePacket receivedPacket = new MessagePacket();
receivedPacket.setType(type);
- receivedPacket.setTo(originalMessage.getFrom());
+ receivedPacket.setTo(from);
receivedPacket.setFrom(account.getJid());
- for (String namespace : namespaces) {
- receivedPacket.addChild("received", namespace).setAttribute("id", originalMessage.getId());
+ for (final String namespace : namespaces) {
+ receivedPacket.addChild("received", namespace).setAttribute("id", id);
}
receivedPacket.addChild("store", "urn:xmpp:hints");
return receivedPacket;
diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
index e01eb2578..476ea2d20 100644
--- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
+++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java
@@ -4,6 +4,7 @@ import android.os.PowerManager;
import android.support.annotation.Nullable;
import android.util.Log;
+import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import java.io.BufferedInputStream;
@@ -30,16 +31,16 @@ import eu.siacs.conversations.services.AbstractConnectionManager;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.FileWriterException;
+import eu.siacs.conversations.utils.MimeUtils;
import eu.siacs.conversations.utils.WakeLockHelper;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
-import eu.siacs.conversations.xmpp.Jid;
public class HttpDownloadConnection implements Transferable {
private final Message message;
private final boolean mUseTor;
- private HttpConnectionManager mHttpConnectionManager;
- private XmppConnectionService mXmppConnectionService;
+ private final HttpConnectionManager mHttpConnectionManager;
+ private final XmppConnectionService mXmppConnectionService;
private URL mUrl;
private DownloadableFile file;
private int mStatus = Transferable.STATUS_UNKNOWN;
@@ -97,23 +98,13 @@ public class HttpDownloadConnection implements Transferable {
&& message.getEncryption() != Message.ENCRYPTION_AXOLOTL) {
this.message.setEncryption(Message.ENCRYPTION_NONE);
}
- final String ext;
- if (VALID_CRYPTO_EXTENSIONS.contains(extension.main)) {
- ext = extension.secondary;
- } else {
- ext = extension.main;
+ final String ext = extension.getExtension();
+ if (ext != null) {
+ message.setRelativeFilePath(String.format("%s.%s", message.getUuid(), ext));
+ } else if (Strings.isNullOrEmpty(message.getRelativeFilePath())) {
+ message.setRelativeFilePath(message.getUuid());
}
- message.setRelativeFilePath(message.getUuid() + (ext != null ? ("." + ext) : ""));
- final String reference = mUrl.getRef();
- if (reference != null && AesGcmURLStreamHandler.IV_KEY.matcher(reference).matches()) {
- this.file = new DownloadableFile(mXmppConnectionService.getCacheDir().getAbsolutePath() + "/" + message.getUuid());
- this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
- Log.d(Config.LOGTAG, "create temporary OMEMO encrypted file: " + this.file.getAbsolutePath() + "(" + message.getMimeType() + ")");
- } else {
- this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
- }
-
-
+ setupFile();
if (this.message.getEncryption() == Message.ENCRYPTION_AXOLOTL && this.file.getKey() == null) {
this.message.setEncryption(Message.ENCRYPTION_NONE);
}
@@ -130,6 +121,17 @@ public class HttpDownloadConnection implements Transferable {
}
}
+ private void setupFile() {
+ final String reference = mUrl.getRef();
+ if (reference != null && AesGcmURLStreamHandler.IV_KEY.matcher(reference).matches()) {
+ this.file = new DownloadableFile(mXmppConnectionService.getCacheDir().getAbsolutePath() + "/" + message.getUuid());
+ this.file.setKeyAndIv(CryptoHelper.hexToBytes(reference));
+ Log.d(Config.LOGTAG, "create temporary OMEMO encrypted file: " + this.file.getAbsolutePath() + "(" + message.getMimeType() + ")");
+ } else {
+ this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
+ }
+ }
+
private void download(boolean interactive) {
new Thread(new FileDownloader(interactive)).start();
}
@@ -363,6 +365,16 @@ public class HttpDownloadConnection implements Transferable {
} else {
contentLength = connection.getHeaderField("Content-Length");
}
+ final String contentType = connection.getContentType();
+ final AbstractConnectionManager.Extension extension = AbstractConnectionManager.Extension.of(mUrl.getPath());
+ if (Strings.isNullOrEmpty(extension.getExtension()) && contentType != null) {
+ final String fileExtension = MimeUtils.guessExtensionFromMimeType(contentType);
+ if (fileExtension != null) {
+ message.setRelativeFilePath(String.format("%s.%s", message.getUuid(), fileExtension));
+ Log.d(Config.LOGTAG, "rewriting name after not finding extension in url but in content type");
+ setupFile();
+ }
+ }
connection.disconnect();
if (contentLength == null) {
throw new IOException("no content-length found in HEAD response");
diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
index 6068af5ad..80390be43 100644
--- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
@@ -141,6 +141,20 @@ public abstract class AbstractParser {
return null;
}
+ public static String errorMessage(Element packet) {
+ final Element error = packet.findChild("error");
+ if (error != null && error.getChildren().size() > 0) {
+ final List errorNames = orderedElementNames(error.getChildren());
+ final String text = error.findChildContent("text");
+ if (text != null && !text.trim().isEmpty()) {
+ return text;
+ } else if (errorNames.size() > 0){
+ return errorNames.get(0).replace("-"," ");
+ }
+ }
+ return null;
+ }
+
private static String prefixError(List errorNames) {
if (errorNames.size() > 0) {
return errorNames.get(0)+'\u001f';
diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java
index 120c80b9a..537f36058 100644
--- a/src/main/java/eu/siacs/conversations/parser/IqParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java
@@ -75,7 +75,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
}
final Element identity = query.findChild("identity");
Data data = Data.parse(x);
- String address = packet.getFrom().toEscapedString();
+ String address = packet.getFrom().toString();
String name = identity == null ? null : identity.getAttribute("name");
String roomName = data.getValue("muc#roomconfig_roomname");
String description = data.getValue("muc#roominfo_description");
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index 07541e7cb..d2e35fc4f 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -100,10 +100,13 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
ChatState state = ChatState.parse(packet);
if (state != null && c != null) {
final Account account = c.getAccount();
- Jid from = packet.getFrom();
+ final Jid from = packet.getFrom();
if (from.asBareJid().equals(account.getJid().asBareJid())) {
c.setOutgoingChatState(state);
if (state == ChatState.ACTIVE || state == ChatState.COMPOSING) {
+ if (c.getContact().isSelf()) {
+ return false;
+ }
mXmppConnectionService.markRead(c);
activateGracePeriod(account);
}
@@ -211,13 +214,12 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateAccountUi();
} else {
- Contact contact = account.getRoster().getContact(from);
- if (contact.setAvatar(avatar)) {
- mXmppConnectionService.syncRoster(account);
- mXmppConnectionService.getAvatarService().clear(contact);
- mXmppConnectionService.updateConversationUi();
- mXmppConnectionService.updateRosterUi();
- }
+ final Contact contact = account.getRoster().getContact(from);
+ contact.setAvatar(avatar);
+ mXmppConnectionService.syncRoster(account);
+ mXmppConnectionService.getAvatarService().clear(contact);
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.updateRosterUi();
}
} else if (mXmppConnectionService.isDataSaverDisabled()) {
mXmppConnectionService.fetchAvatar(account, avatar);
@@ -367,7 +369,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
return;
}
final Element result = MessageArchiveService.Version.findResult(original);
- final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid"));
+ final String queryId = result == null ? null : result.getAttribute("queryid");
+ final MessageArchiveService.Query query = queryId == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(queryId);
if (query != null && query.validFrom(original.getFrom())) {
final Pair f = original.getForwardedMessagePacket("result", query.version.namespace);
if (f == null) {
@@ -381,7 +384,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
return;
}
} else if (query != null) {
- Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received mam result from invalid sender");
+ Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received mam result with invalid from ("+original.getFrom()+") or queryId ("+queryId+")");
return;
} else if (original.fromServer(account)) {
Pair f;
@@ -656,7 +659,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
&& remoteMsgId != null
&& !selfAddressed
&& !isTypeGroupChat) {
- processMessageReceipts(account, packet, query);
+ processMessageReceipts(account, packet, remoteMsgId, query);
}
if (replacedMessage.getEncryption() == Message.ENCRYPTION_PGP) {
conversation.getAccount().getPgpDecryptionService().discard(replacedMessage);
@@ -742,7 +745,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
&& remoteMsgId != null
&& !selfAddressed
&& !isTypeGroupChat) {
- processMessageReceipts(account, packet, query);
+ processMessageReceipts(account, packet, remoteMsgId, query);
}
mXmppConnectionService.databaseBackend.createMessage(message);
@@ -855,8 +858,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
serverMsgId = extractStanzaId(account, packet);
}
mXmppConnectionService.getJingleConnectionManager().deliverMessage(account, packet.getTo(), packet.getFrom(), child, remoteMsgId, serverMsgId, timestamp);
- if (!account.getJid().asBareJid().equals(from.asBareJid())) {
- processMessageReceipts(account, packet, query);
+ if (!account.getJid().asBareJid().equals(from.asBareJid()) && remoteMsgId != null) {
+ processMessageReceipts(account, packet, remoteMsgId, query);
}
} else if (query.isCatchup()) {
if ("propose".equals(action)) {
@@ -1041,7 +1044,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
}
}
- private void processMessageReceipts(Account account, MessagePacket packet, MessageArchiveService.Query query) {
+ private void processMessageReceipts(final Account account, final MessagePacket packet, final String remoteMsgId, MessageArchiveService.Query query) {
final boolean markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0");
final boolean request = packet.hasChild("request", "urn:xmpp:receipts");
if (query == null) {
@@ -1053,15 +1056,16 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
receiptsNamespaces.add("urn:xmpp:receipts");
}
if (receiptsNamespaces.size() > 0) {
- MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account,
- packet,
+ final MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account,
+ packet.getFrom(),
+ remoteMsgId,
receiptsNamespaces,
packet.getType());
mXmppConnectionService.sendMessagePacket(account, receipt);
}
} else if (query.isCatchup()) {
if (request) {
- query.addPendingReceiptRequest(new ReceiptRequest(packet.getFrom(), packet.getId()));
+ query.addPendingReceiptRequest(new ReceiptRequest(packet.getFrom(), remoteMsgId));
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
index 7f57ef733..6d9937766 100644
--- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
@@ -118,12 +118,11 @@ public class PresenceParser extends AbstractParser implements
mXmppConnectionService.getAvatarService().clear(user);
}
if (user.getRealJid() != null) {
- Contact c = conversation.getAccount().getRoster().getContact(user.getRealJid());
- if (c.setAvatar(avatar)) {
- mXmppConnectionService.syncRoster(conversation.getAccount());
- mXmppConnectionService.getAvatarService().clear(c);
- mXmppConnectionService.updateRosterUi();
- }
+ final Contact c = conversation.getAccount().getRoster().getContact(user.getRealJid());
+ c.setAvatar(avatar);
+ mXmppConnectionService.syncRoster(conversation.getAccount());
+ mXmppConnectionService.getAvatarService().clear(c);
+ mXmppConnectionService.updateRosterUi();
}
} else if (mXmppConnectionService.isDataSaverDisabled()) {
mXmppConnectionService.fetchAvatar(mucOptions.getAccount(), avatar);
@@ -268,7 +267,8 @@ public class PresenceParser extends AbstractParser implements
mXmppConnectionService.getAvatarService().clear(account);
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateAccountUi();
- } else if (contact.setAvatar(avatar)) {
+ } else {
+ contact.setAvatar(avatar);
mXmppConnectionService.syncRoster(account);
mXmppConnectionService.getAvatarService().clear(contact);
mXmppConnectionService.updateConversationUi();
diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
index 98d0f3de8..977c9fc27 100644
--- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java
@@ -1071,7 +1071,7 @@ public class FileBackend {
return null;
}
Avatar avatar = new Avatar();
- File file = new File(getAvatarPath(hash));
+ final File file = getAvatarFile(hash);
FileInputStream is = null;
try {
avatar.size = file.length();
@@ -1104,14 +1104,14 @@ public class FileBackend {
}
public boolean isAvatarCached(Avatar avatar) {
- File file = new File(getAvatarPath(avatar.getFilename()));
+ final File file = getAvatarFile(avatar.getFilename());
return file.exists();
}
public boolean save(final Avatar avatar) {
File file;
if (isAvatarCached(avatar)) {
- file = new File(getAvatarPath(avatar.getFilename()));
+ file = getAvatarFile(avatar.getFilename());
avatar.size = file.length();
} else {
file = new File(mXmppConnectionService.getCacheDir().getAbsolutePath() + "/" + UUID.randomUUID().toString());
@@ -1133,12 +1133,12 @@ public class FileBackend {
mDigestOutputStream.close();
String sha1sum = CryptoHelper.bytesToHex(digest.digest());
if (sha1sum.equals(avatar.sha1sum)) {
- File outputFile = new File(getAvatarPath(avatar.getFilename()));
+ final File outputFile = getAvatarFile(avatar.getFilename());
if (outputFile.getParentFile().mkdirs()) {
Log.d(Config.LOGTAG, "created avatar directory");
}
- String filename = getAvatarPath(avatar.getFilename());
- if (!file.renameTo(new File(filename))) {
+ final File avatarFile = getAvatarFile(avatar.getFilename());
+ if (!file.renameTo(avatarFile)) {
Log.d(Config.LOGTAG, "unable to rename " + file.getAbsolutePath() + " to " + outputFile);
return false;
}
@@ -1159,12 +1159,34 @@ public class FileBackend {
return true;
}
- private String getAvatarPath(String avatar) {
- return mXmppConnectionService.getFilesDir().getAbsolutePath() + "/avatars/" + avatar;
+ public void deleteHistoricAvatarPath() {
+ delete(getHistoricAvatarPath());
+ }
+
+ private void delete(final File file) {
+ if (file.isDirectory()) {
+ final File[] files = file.listFiles();
+ if (files != null) {
+ for (final File f : files) {
+ delete(f);
+ }
+ }
+ }
+ if (file.delete()) {
+ Log.d(Config.LOGTAG,"deleted "+file.getAbsolutePath());
+ }
+ }
+
+ private File getHistoricAvatarPath() {
+ return new File(mXmppConnectionService.getFilesDir(), "/avatars/");
+ }
+
+ private File getAvatarFile(String avatar) {
+ return new File(mXmppConnectionService.getCacheDir(), "/avatars/" + avatar);
}
public Uri getAvatarUri(String avatar) {
- return Uri.parse("file:" + getAvatarPath(avatar));
+ return Uri.fromFile(getAvatarFile(avatar));
}
public Bitmap cropCenterSquare(Uri image, int size) {
diff --git a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
index 98721aaf4..87dffab29 100644
--- a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java
@@ -30,6 +30,8 @@ import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.utils.Compatibility;
+import static eu.siacs.conversations.entities.Transferable.VALID_CRYPTO_EXTENSIONS;
+
public class AbstractConnectionManager {
private static final int UI_REFRESH_THRESHOLD = 250;
@@ -106,6 +108,14 @@ public class AbstractConnectionManager {
this.secondary = secondary;
}
+ public String getExtension() {
+ if (VALID_CRYPTO_EXTENSIONS.contains(main)) {
+ return secondary;
+ } else {
+ return main;
+ }
+ }
+
public static Extension of(String path) {
final int pos = path.lastIndexOf('/');
final String filename = path.substring(pos + 1).toLowerCase();
diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java
index 1b29194e6..6a00e7440 100644
--- a/src/main/java/eu/siacs/conversations/services/AvatarService.java
+++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java
@@ -59,7 +59,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
private static final String CHANNEL_SYMBOL = "#";
- final private ArrayList sizes = new ArrayList<>();
+ final private Set sizes = new HashSet<>();
final private HashMap> conversationDependentKeys = new HashMap<>();
protected XmppConnectionService mXmppConnectionService = null;
@@ -224,9 +224,8 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
public void clear(Contact contact) {
synchronized (this.sizes) {
- for (Integer size : sizes) {
- this.mXmppConnectionService.getBitmapCache().remove(
- key(contact, size));
+ for (final Integer size : sizes) {
+ this.mXmppConnectionService.getBitmapCache().remove(key(contact, size));
}
}
for (Conversation conversation : mXmppConnectionService.findAllConferencesWith(contact)) {
@@ -240,9 +239,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
private String key(Contact contact, int size) {
synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
+ this.sizes.add(size);
}
return PREFIX_CONTACT +
'\0' +
@@ -255,9 +252,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
private String key(MucOptions.User user, int size) {
synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
+ this.sizes.add(size);
}
return PREFIX_CONTACT +
'\0' +
@@ -416,12 +411,9 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
private String key(final MucOptions options, int size) {
synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
+ this.sizes.add(size);
}
- return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid()
- + "_" + String.valueOf(size);
+ return PREFIX_CONVERSATION + "_" + options.getConversation().getUuid() + "_" + size;
}
private String key(List users, int size) {
@@ -524,9 +516,7 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
private String key(Account account, int size) {
synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
+ this.sizes.add(size);
}
return PREFIX_ACCOUNT + "_" + account.getUuid() + "_"
+ String.valueOf(size);
@@ -561,11 +551,9 @@ public class AvatarService implements OnAdvancedStreamFeaturesLoaded {
private String key(String name, int size) {
synchronized (this.sizes) {
- if (!this.sizes.contains(size)) {
- this.sizes.add(size);
- }
+ this.sizes.add(size);
}
- return PREFIX_GENERIC + "_" + name + "_" + String.valueOf(size);
+ return PREFIX_GENERIC + "_" + name + "_" + size;
}
private static boolean drawTile(Canvas canvas, String letter, int tileColor, int left, int top, int right, int bottom) {
diff --git a/src/main/java/eu/siacs/conversations/services/ExportBackupService.java b/src/main/java/eu/siacs/conversations/services/ExportBackupService.java
index 7c98db35d..1c6a53878 100644
--- a/src/main/java/eu/siacs/conversations/services/ExportBackupService.java
+++ b/src/main/java/eu/siacs/conversations/services/ExportBackupService.java
@@ -58,7 +58,7 @@ public class ExportBackupService extends Service {
private static final int NOTIFICATION_ID = 19;
private static final int PAGE_SIZE = 20;
- private static AtomicBoolean running = new AtomicBoolean(false);
+ private static final AtomicBoolean RUNNING = new AtomicBoolean(false);
private DatabaseBackend mDatabaseBackend;
private List mAccounts;
private NotificationManager notificationManager;
@@ -67,7 +67,7 @@ public class ExportBackupService extends Service {
//http://www.openintents.org/action/android-intent-action-view/file-directory
//do not use 'vnd.android.document/directory' since this will trigger system file manager
- Intent openIntent = new Intent(Intent.ACTION_VIEW);
+ final Intent openIntent = new Intent(Intent.ACTION_VIEW);
openIntent.addCategory(Intent.CATEGORY_DEFAULT);
if (Compatibility.runsAndTargetsTwentyFour(context)) {
openIntent.setType("resource/folder");
@@ -76,17 +76,15 @@ public class ExportBackupService extends Service {
}
openIntent.putExtra("org.openintents.extra.ABSOLUTE_PATH", path);
- Intent amazeIntent = new Intent(Intent.ACTION_VIEW);
+ final Intent amazeIntent = new Intent(Intent.ACTION_VIEW);
amazeIntent.setDataAndType(Uri.parse("com.amaze.filemanager:" + path), "resource/folder");
//will open a file manager at root and user can navigate themselves
- Intent systemFallBack = new Intent(Intent.ACTION_VIEW);
+ final Intent systemFallBack = new Intent(Intent.ACTION_VIEW);
systemFallBack.addCategory(Intent.CATEGORY_DEFAULT);
systemFallBack.setData(Uri.parse("content://com.android.externalstorage.documents/root/primary"));
return Arrays.asList(openIntent, amazeIntent, systemFallBack);
-
-
}
private static void accountExport(final SQLiteDatabase db, final String uuid, final PrintWriter writer) {
@@ -218,7 +216,7 @@ public class ExportBackupService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- if (running.compareAndSet(false, true)) {
+ if (RUNNING.compareAndSet(false, true)) {
new Thread(() -> {
boolean success;
List files;
@@ -231,7 +229,7 @@ public class ExportBackupService extends Service {
files = Collections.emptyList();
}
stopForeground(true);
- running.set(false);
+ RUNNING.set(false);
if (success) {
notifySuccess(files);
}
@@ -322,12 +320,19 @@ public class ExportBackupService extends Service {
}
writer.flush();
writer.close();
+ mediaScannerScanFile(file);
Log.d(Config.LOGTAG, "written backup to " + file.getAbsoluteFile());
count++;
}
return files;
}
+ private void mediaScannerScanFile(final File file) {
+ final Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ intent.setData(Uri.fromFile(file));
+ sendBroadcast(intent);
+ }
+
private void notifySuccess(final List files) {
final String path = FileBackend.getBackupDirectory(this);
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 3225be67d..0f2423f63 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -120,6 +120,7 @@ import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.ConversationsFileObserver;
import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.utils.EasyOnboardingInvite;
import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.MimeUtils;
import eu.siacs.conversations.utils.PhoneHelper;
@@ -1126,6 +1127,7 @@ 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);
if (Compatibility.hasStoragePermission(this)) {
Log.d(Config.LOGTAG, "starting file observer");
mFileAddingExecutor.execute(this.fileObserver::startWatching);
@@ -1619,6 +1621,43 @@ public class XmppConnectionService extends Service {
sendMessage(message, true, delay);
}
+ public void requestEasyOnboardingInvite(final Account account, final EasyOnboardingInvite.OnInviteRequested callback) {
+ final XmppConnection connection = account.getXmppConnection();
+ final Jid jid = connection == null ? null : connection.getJidForCommand(Namespace.EASY_ONBOARDING_INVITE);
+ if (jid == null) {
+ callback.inviteRequestFailed(getString(R.string.server_does_not_support_easy_onboarding_invites));
+ return;
+ }
+ final IqPacket request = new IqPacket(IqPacket.TYPE.SET);
+ request.setTo(jid);
+ final Element command = request.addChild("command", Namespace.COMMANDS);
+ command.setAttribute("node", Namespace.EASY_ONBOARDING_INVITE);
+ command.setAttribute("action", "execute");
+ sendIqPacket(account, request, (a, response) -> {
+ if (response.getType() == IqPacket.TYPE.RESULT) {
+ final Element resultCommand = response.findChild("command", Namespace.COMMANDS);
+ final Element x = resultCommand == null ? null : resultCommand.findChild("x", Namespace.DATA);
+ if (x != null) {
+ final Data data = Data.parse(x);
+ final String uri = data.getValue("uri");
+ final String landingUrl = data.getValue("landing-url");
+ if (uri != null) {
+ final EasyOnboardingInvite invite = new EasyOnboardingInvite(jid.getDomain().toEscapedString(), uri, landingUrl);
+ callback.inviteRequested(invite);
+ return;
+ }
+ }
+ callback.inviteRequestFailed(getString(R.string.unable_to_parse_invite));
+ Log.d(Config.LOGTAG, response.toString());
+ } else if (response.getType() == IqPacket.TYPE.ERROR) {
+ callback.inviteRequestFailed(IqParser.errorMessage(response));
+ } else {
+ callback.inviteRequestFailed(getString(R.string.remote_server_timeout));
+ }
+ });
+
+ }
+
public void fetchRosterFromServer(final Account account) {
final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET);
if (!"".equals(account.getRosterVersion())) {
@@ -3658,19 +3697,17 @@ public class XmppConnectionService extends Service {
updateConversationUi();
updateAccountUi();
} else {
- Contact contact = a.getRoster().getContact(avatar.owner);
- if (contact.setAvatar(avatar)) {
- syncRoster(account);
- getAvatarService().clear(contact);
- updateConversationUi();
- updateRosterUi();
- }
+ final Contact contact = a.getRoster().getContact(avatar.owner);
+ contact.setAvatar(avatar);
+ syncRoster(account);
+ getAvatarService().clear(contact);
+ updateConversationUi();
+ updateRosterUi();
}
if (callback != null) {
callback.success(avatar);
}
- Log.d(Config.LOGTAG, a.getJid().asBareJid()
- + ": successfully fetched pep avatar for " + avatar.owner);
+ Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": successfully fetched pep avatar for " + avatar.owner);
return;
}
} else {
@@ -3720,12 +3757,11 @@ public class XmppConnectionService extends Service {
getAvatarService().clear(account);
updateAccountUi();
} else {
- Contact contact = account.getRoster().getContact(avatar.owner);
- if (contact.setAvatar(avatar, previouslyOmittedPepFetch)) {
- syncRoster(account);
- getAvatarService().clear(contact);
- updateRosterUi();
- }
+ final Contact contact = account.getRoster().getContact(avatar.owner);
+ contact.setAvatar(avatar, previouslyOmittedPepFetch);
+ syncRoster(account);
+ getAvatarService().clear(contact);
+ updateRosterUi();
}
updateConversationUi();
} else {
@@ -3740,11 +3776,10 @@ public class XmppConnectionService extends Service {
}
if (user.getRealJid() != null) {
Contact contact = account.getRoster().getContact(user.getRealJid());
- if (contact.setAvatar(avatar)) {
- syncRoster(account);
- getAvatarService().clear(contact);
- updateRosterUi();
- }
+ contact.setAvatar(avatar);
+ syncRoster(account);
+ getAvatarService().clear(contact);
+ updateRosterUi();
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
index bdd19a623..3a507c519 100644
--- a/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java
@@ -21,6 +21,8 @@ import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
+import com.google.common.base.Strings;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -347,7 +349,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
private void handleActivityResult(ActivityResult activityResult) {
if (activityResult.resultCode == RESULT_OK && activityResult.requestCode == ScanActivity.REQUEST_SCAN_QR_CODE) {
String result = activityResult.data.getStringExtra(ScanActivity.INTENT_EXTRA_RESULT);
- XmppUri uri = new XmppUri(result == null ? "" : result);
+ XmppUri uri = new XmppUri(Strings.nullToEmpty(result));
if (uri.isValidJid()) {
showEnterJidDialog(uri);
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 845c601e1..db47cf29a 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -1049,6 +1049,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
return binding.getRoot();
}
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ Log.d(Config.LOGTAG, "ConversationFragment.onDestroyView()");
+ messageListAdapter.setOnContactPictureClicked(null);
+ messageListAdapter.setOnContactPictureLongClicked(null);
+ messageListAdapter.setOnQuoteListener(null);
+ }
+
private void quoteText(String text) {
if (binding.textinput.isEnabled()) {
binding.textinput.insertAsQuote(text);
@@ -1066,6 +1075,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ //This should cancel any remaining click events that would otherwise trigger links
+ v.dispatchTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0f, 0f, 0));
synchronized (this.messageList) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
@@ -1721,7 +1732,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
break;
}
final Context context = getActivity();
- if (context != null && intent.resolveActivity(context.getPackageManager()) != null) {
+ if (context == null) {
+ return;
+ }
+ if (intent.resolveActivity(context.getPackageManager()) != null) {
if (chooser) {
startActivityForResult(
Intent.createChooser(intent, getString(R.string.perform_action_with)),
@@ -1729,6 +1743,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
} else {
startActivityForResult(intent, attachmentChoice);
}
+ } else {
+ Toast.makeText(context, R.string.no_application_found, Toast.LENGTH_LONG).show();
}
}
@@ -2102,6 +2118,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
this.binding.textinput.setKeyboardListener(this);
refresh(false);
+ activity.invalidateOptionsMenu();
this.conversation.messagesLoaded.set(true);
Log.d(Config.LOGTAG, "scrolledToBottomAndNoPending=" + Boolean.toString(scrolledToBottomAndNoPending));
@@ -2382,7 +2399,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
updateSendButton();
updateEditablity();
- activity.invalidateOptionsMenu();
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java
index 049c0f27e..026a0cafa 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationsActivity.java
@@ -131,6 +131,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
@Override
protected void refreshUiReal() {
+ invalidateOptionsMenu();
for (@IdRes int id : FRAGMENT_ID_NOTIFICATION_ORDER) {
refreshFragment(id);
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java
index ffbf6f7d9..ae81874e1 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationsOverviewFragment.java
@@ -30,6 +30,7 @@
package eu.siacs.conversations.ui;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.Fragment;
import android.content.Intent;
import android.databinding.DataBindingUtil;
@@ -48,12 +49,16 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import com.google.common.collect.Collections2;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.FragmentConversationsOverviewBinding;
+import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.ui.adapter.ConversationAdapter;
@@ -65,6 +70,7 @@ import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.ScrollState;
import eu.siacs.conversations.ui.util.StyledAttributes;
import eu.siacs.conversations.utils.AccountUtils;
+import eu.siacs.conversations.utils.EasyOnboardingInvite;
import eu.siacs.conversations.utils.ThemeHelper;
import static android.support.v7.widget.helper.ItemTouchHelper.LEFT;
@@ -192,7 +198,7 @@ public class ConversationsOverviewFragment extends XmppFragment {
}
};
- private ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
+ private ItemTouchHelper touchHelper;
public static Conversation getSuggestion(Activity activity) {
final Conversation exception;
@@ -242,7 +248,20 @@ public class ConversationsOverviewFragment extends XmppFragment {
throw new IllegalStateException("Trying to attach fragment to activity that is not an XmppActivity");
}
}
+ @Override
+ public void onDestroyView() {
+ Log.d(Config.LOGTAG,"ConversationsOverviewFragment.onDestroyView()");
+ super.onDestroyView();
+ this.binding = null;
+ this.conversationsAdapter = null;
+ this.touchHelper = null;
+ }
+ @Override
+ public void onDestroy() {
+ Log.d(Config.LOGTAG,"ConversationsOverviewFragment.onDestroy()");
+ super.onDestroy();
+ }
@Override
public void onPause() {
Log.d(Config.LOGTAG,"ConversationsOverviewFragment.onPause()");
@@ -278,6 +297,7 @@ public class ConversationsOverviewFragment extends XmppFragment {
});
this.binding.list.setAdapter(this.conversationsAdapter);
this.binding.list.setLayoutManager(new LinearLayoutManager(getActivity(),LinearLayoutManager.VERTICAL,false));
+ this.touchHelper = new ItemTouchHelper(this.callback);
this.touchHelper.attachToRecyclerView(this.binding.list);
return binding.getRoot();
}
@@ -286,6 +306,8 @@ public class ConversationsOverviewFragment extends XmppFragment {
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
menuInflater.inflate(R.menu.fragment_conversations_overview, menu);
AccountUtils.showHideMenuItems(menu);
+ final MenuItem easyOnboardInvite = menu.findItem(R.id.action_easy_invite);
+ easyOnboardInvite.setVisible(EasyOnboardingInvite.anyHasSupport(activity == null ? null : activity.xmppConnectionService));
}
@Override
@@ -340,10 +362,33 @@ public class ConversationsOverviewFragment extends XmppFragment {
case R.id.action_search:
startActivity(new Intent(getActivity(), SearchActivity.class));
return true;
+ case R.id.action_easy_invite:
+ selectAccountToStartEasyInvite();
+ return true;
}
return super.onOptionsItemSelected(item);
}
+ private void selectAccountToStartEasyInvite() {
+ final List accounts = EasyOnboardingInvite.getSupportingAccounts(activity.xmppConnectionService);
+ if (accounts.size() == 1) {
+ openEasyInviteScreen(accounts.get(0));
+ } else {
+ final AtomicReference selectedAccount = new AtomicReference<>(accounts.get(0));
+ final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
+ alertDialogBuilder.setTitle(R.string.choose_account);
+ final String[] asStrings = Collections2.transform(accounts, a -> a.getJid().asBareJid().toEscapedString()).toArray(new String[0]);
+ alertDialogBuilder.setSingleChoiceItems(asStrings, 0, (dialog, which) -> selectedAccount.set(accounts.get(which)));
+ alertDialogBuilder.setNegativeButton(R.string.cancel, null);
+ alertDialogBuilder.setPositiveButton(R.string.ok, (dialog, which) -> openEasyInviteScreen(selectedAccount.get()));
+ alertDialogBuilder.create().show();
+ }
+ }
+
+ private void openEasyInviteScreen(final Account account) {
+ EasyOnboardingInviteActivity.launch(account, activity);
+ }
+
@Override
void refresh() {
if (this.binding == null || this.activity == null) {
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index dcccec104..d7eff28ec 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -9,6 +9,7 @@ import android.content.SharedPreferences;
import android.databinding.DataBindingUtil;
import android.graphics.Bitmap;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
@@ -64,6 +65,7 @@ import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
+import eu.siacs.conversations.utils.CharSequenceUtils;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.Resolver;
import eu.siacs.conversations.utils.SignupUtils;
@@ -964,6 +966,10 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
}
+ if (!mInitMode && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ this.binding.accountPassword.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
+ }
+
final boolean editable = !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY) && !mAccount.isOptionSet(Account.OPTION_FIXED_USERNAME) && QuickConversationsService.isConversations();
this.binding.accountJid.setEnabled(editable);
this.binding.accountJid.setFocusable(editable);
@@ -975,10 +981,10 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
updateDisplayName(displayName);
- final boolean tooglePassword = mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) || !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY);
+ final boolean togglePassword = mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) || !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY);
final boolean editPassword = !mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) || (!mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY) && QuickConversationsService.isConversations()) || mAccount.getLastErrorStatus() == Account.State.UNAUTHORIZED;
- this.binding.accountPasswordLayout.setPasswordVisibilityToggleEnabled(tooglePassword);
+ this.binding.accountPasswordLayout.setPasswordVisibilityToggleEnabled(togglePassword);
this.binding.accountPassword.setFocusable(editPassword);
this.binding.accountPassword.setFocusableInTouchMode(editPassword);
diff --git a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java
index fdebd6a56..30c330c4d 100644
--- a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java
@@ -246,6 +246,7 @@ public class UriHandlerActivity extends AppCompatActivity {
}
private static boolean looksLikeJsonObject(final String input) {
- return input.charAt(0) == '{' && input.charAt(input.length() - 1) == '}';
+ final String trimmed = Strings.emptyToNull(input).trim();
+ return trimmed.charAt(0) == '{' && trimmed.charAt(trimmed.length() - 1) == '}';
}
}
\ No newline at end of file
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
index ba171ce1f..d5e3b9de4 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -65,8 +65,6 @@ import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.MyLinkify;
import eu.siacs.conversations.ui.util.ViewUtil;
import eu.siacs.conversations.ui.widget.ClickableMovementMethod;
-import eu.siacs.conversations.ui.widget.CopyTextView;
-import eu.siacs.conversations.ui.widget.ListSelectionManager;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.EmojiWrapper;
import eu.siacs.conversations.utils.Emoticons;
@@ -78,7 +76,7 @@ import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.mam.MamReference;
-public class MessageAdapter extends ArrayAdapter implements CopyTextView.CopyHandler {
+public class MessageAdapter extends ArrayAdapter {
public static final String DATE_SEPARATOR_BODY = "DATE_SEPARATOR";
private static final int SENT = 0;
@@ -87,7 +85,6 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie
private static final int DATE_SEPARATOR = 3;
private static final int RTP_SESSION = 4;
private final XmppActivity activity;
- private final ListSelectionManager listSelectionManager = new ListSelectionManager();
private final AudioPlayer audioPlayer;
private List highlightedTerm = null;
private DisplayMetrics metrics;
@@ -501,9 +498,7 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie
MyLinkify.addLinks(body, true);
viewHolder.messageBody.setAutoLinkMask(0);
viewHolder.messageBody.setText(EmojiWrapper.transform(body));
- viewHolder.messageBody.setTextIsSelectable(true);
viewHolder.messageBody.setMovementMethod(ClickableMovementMethod.getInstance());
- listSelectionManager.onUpdate(viewHolder.messageBody, message);
} else {
viewHolder.messageBody.setText("");
viewHolder.messageBody.setTextIsSelectable(false);
@@ -673,11 +668,6 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie
default:
throw new AssertionError("Unknown view type");
}
- if (viewHolder.messageBody != null) {
- listSelectionManager.onCreate(viewHolder.messageBody,
- new MessageBodyActionModeCallback(viewHolder.messageBody));
- viewHolder.messageBody.setCopyHandler(this);
- }
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
@@ -866,45 +856,7 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie
private void promptOpenKeychainInstall(View view) {
activity.showInstallPgpDialog();
}
-
- @Override
- public void notifyDataSetChanged() {
- listSelectionManager.onBeforeNotifyDataSetChanged();
- super.notifyDataSetChanged();
- listSelectionManager.onAfterNotifyDataSetChanged();
- }
-
- private String transformText(CharSequence text, int start, int end, boolean forCopy) {
- SpannableStringBuilder builder = new SpannableStringBuilder(text);
- Object copySpan = new Object();
- builder.setSpan(copySpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- DividerSpan[] dividerSpans = builder.getSpans(0, builder.length(), DividerSpan.class);
- for (DividerSpan dividerSpan : dividerSpans) {
- builder.replace(builder.getSpanStart(dividerSpan), builder.getSpanEnd(dividerSpan),
- dividerSpan.isLarge() ? "\n\n" : "\n");
- }
- start = builder.getSpanStart(copySpan);
- end = builder.getSpanEnd(copySpan);
- if (start == -1 || end == -1) return "";
- builder = new SpannableStringBuilder(builder, start, end);
- if (forCopy) {
- QuoteSpan[] quoteSpans = builder.getSpans(0, builder.length(), QuoteSpan.class);
- for (QuoteSpan quoteSpan : quoteSpans) {
- builder.insert(builder.getSpanStart(quoteSpan), "> ");
- }
- }
- return builder.toString();
- }
-
- @Override
- public String transformTextForCopy(CharSequence text, int start, int end) {
- if (text instanceof Spanned) {
- return transformText(text, start, end, true);
- } else {
- return text.toString().substring(start, end);
- }
- }
-
+
public FileBackend getFileBackend() {
return activity.xmppConnectionService.getFileBackend();
}
@@ -969,56 +921,9 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie
protected ImageView indicator;
protected ImageView indicatorReceived;
protected TextView time;
- protected CopyTextView messageBody;
+ protected TextView messageBody;
protected ImageView contact_picture;
protected TextView status_message;
protected TextView encryption;
}
-
-
- private class MessageBodyActionModeCallback implements ActionMode.Callback {
-
- private final TextView textView;
-
- public MessageBodyActionModeCallback(TextView textView) {
- this.textView = textView;
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- if (onQuoteListener != null) {
- int quoteResId = activity.getThemeResource(R.attr.icon_quote, R.drawable.ic_action_reply);
- // 3rd item is placed after "copy" item
- menu.add(0, android.R.id.button1, 3, R.string.quote).setIcon(quoteResId)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- }
- return false;
- }
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- if (item.getItemId() == android.R.id.button1) {
- int start = textView.getSelectionStart();
- int end = textView.getSelectionEnd();
- if (end > start) {
- String text = transformText(textView.getText(), start, end, false);
- if (onQuoteListener != null) {
- onQuoteListener.onQuote(text);
- }
- mode.finish();
- }
- return true;
- }
- return false;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- }
- }
}
diff --git a/src/main/java/eu/siacs/conversations/ui/widget/CopyTextView.java b/src/main/java/eu/siacs/conversations/ui/widget/CopyTextView.java
deleted file mode 100644
index d22a2deae..000000000
--- a/src/main/java/eu/siacs/conversations/ui/widget/CopyTextView.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package eu.siacs.conversations.ui.widget;
-
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.support.v7.widget.AppCompatTextView;
-import android.util.AttributeSet;
-
-public class CopyTextView extends AppCompatTextView {
-
- public CopyTextView(Context context) {
- super(context);
- }
-
- public CopyTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public CopyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public interface CopyHandler {
- String transformTextForCopy(CharSequence text, int start, int end);
- }
-
- private CopyHandler copyHandler;
-
- public void setCopyHandler(CopyHandler copyHandler) {
- this.copyHandler = copyHandler;
- }
-
- @Override
- public boolean onTextContextMenuItem(int id) {
- final CharSequence text = getText();
- int min = 0;
- int max = text.length();
- if (isFocused()) {
- final int selStart = getSelectionStart();
- final int selEnd = getSelectionEnd();
- min = Math.max(0, Math.min(selStart, selEnd));
- max = Math.max(0, Math.max(selStart, selEnd));
- }
- String textForCopy = null;
- if (id == android.R.id.copy && copyHandler != null) {
- textForCopy = copyHandler.transformTextForCopy(getText(), min, max);
- }
- try {
- return super.onTextContextMenuItem(id);
- } finally {
- if (textForCopy != null) {
- ClipboardManager clipboard = (ClipboardManager) getContext().
- getSystemService(Context.CLIPBOARD_SERVICE);
- clipboard.setPrimaryClip(ClipData.newPlainText(null, textForCopy));
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/eu/siacs/conversations/ui/widget/ListSelectionManager.java b/src/main/java/eu/siacs/conversations/ui/widget/ListSelectionManager.java
deleted file mode 100644
index 24f1cac8d..000000000
--- a/src/main/java/eu/siacs/conversations/ui/widget/ListSelectionManager.java
+++ /dev/null
@@ -1,211 +0,0 @@
-package eu.siacs.conversations.ui.widget;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.text.Selection;
-import android.text.Spannable;
-import android.view.ActionMode;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.TextView;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-
-public class ListSelectionManager {
-
- private static final int MESSAGE_SEND_RESET = 1;
- private static final int MESSAGE_RESET = 2;
- private static final int MESSAGE_START_SELECTION = 3;
- private static final Field FIELD_EDITOR;
- private static final Method METHOD_START_SELECTION;
- private static final boolean SUPPORTED;
- private static final Handler HANDLER = new Handler(Looper.getMainLooper(), new Handler.Callback() {
-
- @Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_SEND_RESET: {
- // Skip one more message queue loop
- HANDLER.obtainMessage(MESSAGE_RESET, msg.obj).sendToTarget();
- return true;
- }
- case MESSAGE_RESET: {
- final ListSelectionManager listSelectionManager = (ListSelectionManager) msg.obj;
- listSelectionManager.futureSelectionIdentifier = null;
- return true;
- }
- case MESSAGE_START_SELECTION: {
- final StartSelectionHolder holder = (StartSelectionHolder) msg.obj;
- holder.listSelectionManager.futureSelectionIdentifier = null;
- startSelection(holder.textView, holder.start, holder.end);
- return true;
- }
- }
- return false;
- }
- });
-
- static {
- Field editor;
- try {
- editor = TextView.class.getDeclaredField("mEditor");
- editor.setAccessible(true);
- } catch (Exception e) {
- editor = null;
- }
- FIELD_EDITOR = editor;
- Method startSelection = null;
- if (editor != null) {
- String[] startSelectionNames = {"startSelectionActionMode", "startSelectionActionModeWithSelection"};
- for (String startSelectionName : startSelectionNames) {
- try {
- startSelection = editor.getType().getDeclaredMethod(startSelectionName);
- startSelection.setAccessible(true);
- break;
- } catch (Exception e) {
- startSelection = null;
- }
- }
- }
- METHOD_START_SELECTION = startSelection;
- SUPPORTED = FIELD_EDITOR != null && METHOD_START_SELECTION != null;
- }
-
- private ActionMode selectionActionMode;
- private Object selectionIdentifier;
- private TextView selectionTextView;
- private Object futureSelectionIdentifier;
- private int futureSelectionStart;
- private int futureSelectionEnd;
-
- public static boolean isSupported() {
- return SUPPORTED;
- }
-
- private static void startSelection(TextView textView, int start, int end) {
- final CharSequence text = textView.getText();
- if (SUPPORTED && start >= 0 && end > start && textView.isTextSelectable() && text instanceof Spannable) {
- final Spannable spannable = (Spannable) text;
- start = Math.min(start, spannable.length());
- end = Math.min(end, spannable.length());
- Selection.setSelection(spannable, start, end);
- try {
- final Object editor = FIELD_EDITOR != null ? FIELD_EDITOR.get(textView) : textView;
- METHOD_START_SELECTION.invoke(editor);
- } catch (Exception e) {
- }
- }
- }
-
- public void onCreate(TextView textView, ActionMode.Callback additionalCallback) {
- final CustomCallback callback = new CustomCallback(textView, additionalCallback);
- textView.setCustomSelectionActionModeCallback(callback);
- }
-
- public void onUpdate(TextView textView, Object identifier) {
- if (SUPPORTED) {
- final ActionMode.Callback callback = textView.getCustomSelectionActionModeCallback();
- if (callback instanceof CustomCallback) {
- final CustomCallback customCallback = (CustomCallback) textView.getCustomSelectionActionModeCallback();
- customCallback.identifier = identifier;
- if (futureSelectionIdentifier == identifier) {
- HANDLER.obtainMessage(MESSAGE_START_SELECTION, new StartSelectionHolder(this,
- textView, futureSelectionStart, futureSelectionEnd)).sendToTarget();
- }
- }
- }
- }
-
- public void onBeforeNotifyDataSetChanged() {
- if (SUPPORTED) {
- HANDLER.removeMessages(MESSAGE_SEND_RESET);
- HANDLER.removeMessages(MESSAGE_RESET);
- HANDLER.removeMessages(MESSAGE_START_SELECTION);
- if (selectionActionMode != null) {
- final CharSequence text = selectionTextView.getText();
- futureSelectionIdentifier = selectionIdentifier;
- futureSelectionStart = Selection.getSelectionStart(text);
- futureSelectionEnd = Selection.getSelectionEnd(text);
- selectionActionMode.finish();
- selectionActionMode = null;
- selectionIdentifier = null;
- selectionTextView = null;
- }
- }
- }
-
- public void onAfterNotifyDataSetChanged() {
- if (SUPPORTED && futureSelectionIdentifier != null) {
- HANDLER.obtainMessage(MESSAGE_SEND_RESET, this).sendToTarget();
- }
- }
-
- private static class StartSelectionHolder {
-
- final ListSelectionManager listSelectionManager;
- final TextView textView;
- public final int start;
- public final int end;
-
- StartSelectionHolder(ListSelectionManager listSelectionManager, TextView textView,
- int start, int end) {
- this.listSelectionManager = listSelectionManager;
- this.textView = textView;
- this.start = start;
- this.end = end;
- }
- }
-
- private class CustomCallback implements ActionMode.Callback {
-
- private final TextView textView;
- private final ActionMode.Callback additionalCallback;
- Object identifier;
-
- CustomCallback(TextView textView, ActionMode.Callback additionalCallback) {
- this.textView = textView;
- this.additionalCallback = additionalCallback;
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- selectionActionMode = mode;
- selectionIdentifier = identifier;
- selectionTextView = textView;
- if (additionalCallback != null) {
- additionalCallback.onCreateActionMode(mode, menu);
- }
- return true;
- }
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- if (additionalCallback != null) {
- additionalCallback.onPrepareActionMode(mode, menu);
- }
- return true;
- }
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- if (additionalCallback != null && additionalCallback.onActionItemClicked(mode, item)) {
- return true;
- }
- return false;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- if (additionalCallback != null) {
- additionalCallback.onDestroyActionMode(mode);
- }
- if (selectionActionMode == mode) {
- selectionActionMode = null;
- selectionIdentifier = null;
- selectionTextView = null;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/eu/siacs/conversations/utils/EasyOnboardingInvite.java b/src/main/java/eu/siacs/conversations/utils/EasyOnboardingInvite.java
new file mode 100644
index 000000000..e4ed8e1b5
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/utils/EasyOnboardingInvite.java
@@ -0,0 +1,93 @@
+package eu.siacs.conversations.utils;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+
+import java.util.Collections;
+import java.util.List;
+
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.services.QuickConversationsService;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.xmpp.XmppConnection;
+
+public class EasyOnboardingInvite implements Parcelable {
+
+ private String domain;
+ private String uri;
+ private String landingUrl;
+
+ protected EasyOnboardingInvite(Parcel in) {
+ domain = in.readString();
+ uri = in.readString();
+ landingUrl = in.readString();
+ }
+
+ public EasyOnboardingInvite(String domain, String uri, String landingUrl) {
+ this.domain = domain;
+ this.uri = uri;
+ this.landingUrl = landingUrl;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(domain);
+ dest.writeString(uri);
+ dest.writeString(landingUrl);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator CREATOR = new Creator() {
+ @Override
+ public EasyOnboardingInvite createFromParcel(Parcel in) {
+ return new EasyOnboardingInvite(in);
+ }
+
+ @Override
+ public EasyOnboardingInvite[] newArray(int size) {
+ return new EasyOnboardingInvite[size];
+ }
+ };
+
+ public static boolean anyHasSupport(final XmppConnectionService service) {
+ if (QuickConversationsService.isQuicksy()) {
+ return false;
+ }
+ return getSupportingAccounts(service).size() > 0;
+
+ }
+
+ public static List getSupportingAccounts(final XmppConnectionService service) {
+ final ImmutableList.Builder supportingAccountsBuilder = new ImmutableList.Builder<>();
+ final List accounts = service == null ? Collections.emptyList() : service.getAccounts();
+ for(Account account : accounts) {
+ final XmppConnection xmppConnection = account.getXmppConnection();
+ if (xmppConnection != null && xmppConnection.getFeatures().easyOnboardingInvites()) {
+ supportingAccountsBuilder.add(account);
+ }
+ }
+ return supportingAccountsBuilder.build();
+ }
+
+
+ public String getShareableLink() {
+ return Strings.isNullOrEmpty(landingUrl) ? uri : landingUrl;
+ }
+
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public interface OnInviteRequested {
+ void inviteRequested(EasyOnboardingInvite invite);
+ void inviteRequestFailed(String message);
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/utils/FtsUtils.java b/src/main/java/eu/siacs/conversations/utils/FtsUtils.java
index 4a6ccf399..88959c6b2 100644
--- a/src/main/java/eu/siacs/conversations/utils/FtsUtils.java
+++ b/src/main/java/eu/siacs/conversations/utils/FtsUtils.java
@@ -66,7 +66,7 @@ public class FtsUtils {
} else if (term.contains("*") || term.startsWith("-")) {
builder.append(term);
} else {
- builder.append('*').append(term).append('*');
+ builder.append(term).append('*');
}
}
return builder.toString();
diff --git a/src/main/java/eu/siacs/conversations/utils/XmppUri.java b/src/main/java/eu/siacs/conversations/utils/XmppUri.java
index 5db19ad05..52f84f24b 100644
--- a/src/main/java/eu/siacs/conversations/utils/XmppUri.java
+++ b/src/main/java/eu/siacs/conversations/utils/XmppUri.java
@@ -174,11 +174,7 @@ public class XmppUri {
jid = null;
}
} else {
- try {
- jid = Jid.ofEscaped(uri.toString()).asBareJid().toEscapedString();
- } catch (final IllegalArgumentException ignored) {
- jid = null;
- }
+ jid = null;
}
}
diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java
index 31b3420dd..b65076016 100644
--- a/src/main/java/eu/siacs/conversations/xml/Namespace.java
+++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java
@@ -52,4 +52,5 @@ public final class Namespace {
public static final String BOOKMARKS2_COMPAT = BOOKMARKS2 + "#compat";
public static final String INVITE = "urn:xmpp:invite";
public static final String PARS = "urn:xmpp:pars:0";
+ public static final String EASY_ONBOARDING_INVITE = "urn:xmpp:invite#invite";
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index 19fa4a431..1e4060665 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -137,6 +137,7 @@ public class XmppConnection implements Runnable {
protected final Account account;
private final Features features = new Features(this);
private final HashMap disco = new HashMap<>();
+ private final HashMap commands = new HashMap<>();
private final SparseArray mStanzaQueue = new SparseArray<>();
private final Hashtable> packetCallbacks = new Hashtable<>();
private final Set advancedStreamFeaturesLoadedListeners = new HashSet<>();
@@ -228,6 +229,12 @@ public class XmppConnection implements Runnable {
}
}
+ public Jid getJidForCommand(final String node) {
+ synchronized (this.commands) {
+ return this.commands.get(node);
+ }
+ }
+
public void prepareNewConnection() {
this.lastConnect = SystemClock.elapsedRealtime();
this.lastPingSent = SystemClock.elapsedRealtime();
@@ -1001,6 +1008,9 @@ public class XmppConnection implements Runnable {
synchronized (this.disco) {
disco.clear();
}
+ synchronized (this.commands) {
+ this.commands.clear();
+ }
}
private void sendBindRequest() {
@@ -1223,6 +1233,35 @@ public class XmppConnection implements Runnable {
});
}
+ private void discoverCommands() {
+ final IqPacket request = new IqPacket(IqPacket.TYPE.GET);
+ request.setTo(account.getDomain());
+ request.addChild("query", Namespace.DISCO_ITEMS).setAttribute("node", Namespace.COMMANDS);
+ sendIqPacket(request, (account, response) -> {
+ if (response.getType() == IqPacket.TYPE.RESULT) {
+ final Element query = response.findChild("query",Namespace.DISCO_ITEMS);
+ if (query == null) {
+ return;
+ }
+ final HashMap commands = new HashMap<>();
+ for(final Element child : query.getChildren()) {
+ if ("item".equals(child.getName())) {
+ final String node = child.getAttribute("node");
+ final Jid jid = child.getAttributeAsJid("jid");
+ if (node != null && jid != null) {
+ commands.put(node, jid);
+ }
+ }
+ }
+ Log.d(Config.LOGTAG,commands.toString());
+ synchronized (this.commands) {
+ this.commands.clear();
+ this.commands.putAll(commands);
+ }
+ }
+ });
+ }
+
public boolean isMamPreferenceAlways() {
return isMamPreferenceAlways;
}
@@ -1246,6 +1285,9 @@ public class XmppConnection implements Runnable {
if (getFeatures().carbons() && !features.carbonsEnabled) {
sendEnableCarbons();
}
+ if (getFeatures().commands()) {
+ discoverCommands();
+ }
}
private void sendServiceDiscoveryItems(final Jid server) {
@@ -1761,6 +1803,16 @@ public class XmppConnection implements Runnable {
return hasDiscoFeature(account.getDomain(), "urn:xmpp:carbons:2");
}
+ public boolean commands() {
+ return hasDiscoFeature(account.getDomain(), Namespace.COMMANDS);
+ }
+
+ public boolean easyOnboardingInvites() {
+ synchronized (commands) {
+ return commands.containsKey(Namespace.EASY_ONBOARDING_INVITE);
+ }
+ }
+
public boolean bookmarksConversion() {
return hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS_CONVERSION) && pepPublishOptions();
}
diff --git a/src/main/res/layout/activity_rtp_session.xml b/src/main/res/layout/activity_rtp_session.xml
index 6aac1a381..f38a823a6 100644
--- a/src/main/res/layout/activity_rtp_session.xml
+++ b/src/main/res/layout/activity_rtp_session.xml
@@ -174,7 +174,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
- android:layout_margin="12dp"
+ android:layout_margin="@dimen/in_call_fab_margin"
android:layout_toStartOf="@+id/end_call"
android:layout_toLeftOf="@+id/end_call"
android:visibility="gone"
@@ -188,7 +188,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
- android:layout_margin="16dp"
+ android:layout_margin="@dimen/in_call_fab_margin_center"
android:src="@drawable/ic_call_end_white_48dp"
android:visibility="visible"
app:backgroundTint="@color/red700"
@@ -201,7 +201,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
- android:layout_margin="12dp"
+ android:layout_margin="@dimen/in_call_fab_margin"
android:layout_toEndOf="@+id/end_call"
android:layout_toRightOf="@+id/end_call"
android:visibility="gone"
@@ -215,7 +215,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
- android:layout_margin="12dp"
+ android:layout_margin="@dimen/in_call_fab_margin"
android:layout_toEndOf="@+id/in_call_action_right"
android:layout_toRightOf="@+id/in_call_action_right"
android:visibility="gone"
diff --git a/src/main/res/layout/message_content.xml b/src/main/res/layout/message_content.xml
index f1f584f0c..05af4e42c 100644
--- a/src/main/res/layout/message_content.xml
+++ b/src/main/res/layout/message_content.xml
@@ -1,12 +1,12 @@
-
+ Fehlgeschlagene ZustellungenWeitere Optionen
+ Keine Anwendung gefunden
+ Einladung zu Conversations
+ Einladung kann nicht gelesen werden
+ Server unterstützt keine Generierung von Einladungen
diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml
index 640d82e2d..2f9c58e51 100644
--- a/src/main/res/values-el/strings.xml
+++ b/src/main/res/values-el/strings.xml
@@ -4,26 +4,27 @@
Νέα συζήτησηΔιαχείριση λογαριασμώνΔιαχείριση λογαριασμού
+ Τερματισμός συζήτησηςΛεπτομέρειες επαφήςΛεπτομέρειες ομαδικής συζήτησηςΛεπτομέρειες καναλιούΠροσθήκη λογαριασμούΕπεξεργασία ονόματος
- Προσθήκη στην ατζέντα
+ Προσθήκη στο βιβλίο διευθύνσεωνΔιαγραφή από τη λίστα επαφώνΑποκλεισμός επαφήςΆρση αποκλεισμού επαφήςΑποκλεισμός τομέαΆρση αποκλεισμού τομέαΑποκλεισμός συμμετέχοντα
- Αναίρεση αποκλεισμού συμμετέχοντα
+ Άρση αποκλεισμού συμμετέχονταΔιαχείριση ΛογαριασμώνΡυθμίσειςΔιαμοιρασμός με ΣυζήτησηΈναρξη Συζήτησης
- Επέλεξε επαφή
+ Επιλογή επαφήςΕπιλογή επαφών
- Μοιράσου μέσω του λογαριασμού
+ Διαμοιρασμός μέσω λογαριασμούΑποκλεισμός λίσταςμόλις τώραπριν από 1 λεπτό
@@ -31,25 +32,27 @@
%d αδιάβαστες συζητήσειςαποστολή...Αποκρυπτογράφηση μηνύματος. Παρακαλώ περιμένετε...
- OpenPGP κρυπτογραφημένο μήνυμα
+ Κρυπτογραφημένο μήνυμα OpenPGPΤο ψευδώνυμο είναι ήδη σε χρήση
- Άκυρο όνομα
+ Μη έγκυρο ψευδώνυμοΔιαχειριστήςΚάτοχοςΣυντονιστήςΣυμμετέχωνΕπισκέπτης
+ Θα θέλατε να αφαιρέσετε τον/την %s από τη λίστα επαφών σας; Οι Συζητήσεις με αυτή την επαφή δεν θα αφαιρεθούν.Θέλετε να αποκλείσετε την επαφή %s από το να σας στέλνει μηνύματα;
- Θέλετε να επιτρέψετε στην επαφή %s να σας στέλνει μηνύματα;
+ Θέλετε να κάνετε άρση αποκλεισμού και να επιτρέψετε στην επαφή %s να σας στέλνει μηνύματα;Αποκλεισμός όλων των επαφών από το %s;Άρση αποκλεισμού όλων των επαφών από το %s;Η επαφή αποκλείστηκεΑποκλεισμένος
+ Θέλετε να αφαιρέσετε το %s ως σελιδοδείκτη; Οι συζητήσεις που σχετίζονται με αυτόν τον σελιδοδείκτη δεν θα αφαιρεθούν.Εγγραφή νέου λογαριασμού στον διακομιστήΑλλαγή συνθηματικού στον διακομιστήΔιαμοιρασμός με...Έναρξη συζήτησης
- Προσκάλεσε επαφή
+ Πρόσκληση επαφήςΠρόσκλησηΕπαφέςΕπαφή
@@ -63,50 +66,68 @@
ΑποθήκευσηΕντάξειΤο Conversations έκλεισε απροσδόκητα
+ Η χρήση του XMPP λογαριασμού σας για την αποστολή ίχνους στοίβας προωθεί την συνεχή ανάπτυξη του Conversations.Αποστολή τώραΧωρίς ερώτηση την επόμενη φορά
+ Δεν ήταν δυνατή η σύνδεση στον λογαριασμό
+ Δεν ήταν δυνατή η σύνδεση σε πολλαπλούς λογαριασμούς
+ Επιλέξτε για διαχείριση των λογαριασμών σαςΕπισύναψη αρχείου
+ Προσθήκη της επαφής αυτής στη λίστα επαφών σας;Προσθήκη επαφήςη αποστολή απέτυχε
- Διαμοιρασμός φακέλων. Παρακαλώ περιμένετε...
+ Προετοιμασία εικόνας για αποστολή
+ Προετοιμασία εικόνων για μεταφορά
+ Διαμοιρασμός αρχείων. Παρακαλώ περιμένετε...Καθαρισμός ιστορικούΚαθαρισμός ιστορικού Συζήτησης
+ Θέλετε να διαγράψετε όλα τα μηνύματα αυτής της συζήτησης;\n\nΠροσοχή: Αυτή η ενέργεια δεν θα επηρεάσει μηνύματα που είναι αποθηκευμένα σε άλλες συσκευές ή εξυπηρετητές.Διαγραφή αρχείουΕίστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το αρχείο;\n\nΠροσοχή Αυτή η ενέργεια δεν θα διαγράψει αντίγραφα αυτού του αρχείου που είναι αποθηκευμένα σε άλλες συσκευές ή εξυπηρετητές.Κλείσιμο της συζήτησης αμέσως μετάΕπιλογή συσκευής
- Αποστολή κρυπτογραφημένου μηνύματος
+ Αποστολή μη κρυπτογραφημένου μηνύματοςΑποστολή μηνύματος
- Αποστολή μηνύματος %s
- Αποστολή OMEMO κρυπτογραφημένου μηνύματος
- Αποστολή v\\OMEMO κρυπτογραφημένου μηνύματος
+ Αποστολή μηνύματος στον/στην %s
+ Αποστολή κρυπτογραφημένου μηνύματος OMEMO
+ Αποστολή v\\κρυπτογραφημένου μηνύματος OMEMOΑποστολή κρυπτογραφημένου μηνύματος OpenPGP
+ Χρήση νέου ψευδωνύμουΑποστολή χωρίς κρυπτογράφησηΗ αποκρυπτογράφηση απέτυχε. Ίσως δεν κατέχετε το σωστό ιδιωτικό κλειδί.OpenKeychain
+ Το Conversations χρησιμοποιεί το OpenKeychain για να κρυπτογραφεί και να αποκρυπτογραφεί μηνύματα και να διαχειρίζεται τα δημόσια κλειδιά σας.\n\nΕίναι δημοσιευμένο με άδεια GPLv3+ και είναι διαθέσιμο στο F-Droid και το Google Play.\n\n(Παρακαλώ επανεκκινήστε το Conversations αμέσως μετά.)ΕπανεκκίνησηΕγκατάσταση
- Παρακαλώ εγκαταστήστε OpenKeychain
+ Παρακαλώ εγκαταστήστε το OpenKeychainπροσφορά...αναμονή...Δεν βρέθηκε κλειδί OpenPGP
+ Δεν ήταν δυνατή η κρυπτογράφηση του μηνύματός σας γιατί η επαφή σας δεν ανακοινώνει το δημόσιο κλειδί της.\n\nΠαρακαλώ ζητήστε από την επαφή σας να ρυθμίσει το OpenPGP.Δεν βρέθηκαν κλειδιά OpenPGP
+ Δεν ήταν δυνατή η κρυπτογράφηση του μηνύματός σας γιατί οι επαφές σας δεν ανακοινώνουν το δημόσιο κλειδί τους.\n\nΠαρακαλώ ζητήστε από τις επαφές σας να εγκαταστήσουν το OpenPGP.ΓενικάΑποδοχή αρχείωνΑυτόματη αποδοχή αρχείων μικρότερα από...
- Επισυναπτόμμενα
+ ΣυνημμέναΕιδοποίησηΔόνησηΔόνηση όταν δέχεστε νέο μήνυμαΕιδοποίηση LED
- Ειδοποίηση μέσω αναβοσβήματος όταν δέχεστε νέο μήνυμα
+ Ειδοποίηση μέσω εναλλαγής LED κατά τη λήψη νέου μηνύματος
+ Κουδούνισμα
+ Ήχος ειδοποίησης
+ Ήχος ειδοποίησης για νέα μηνύματα
+ Κουδούνισμα για εισερχόμενη κλήσηΠερίοδος ΧάριτοςΟ χρόνος σίγασης ειδοποιήσεων αφότου ανιχνευθεί δραστηριότητα σε μια από τις άλλες συσκευές σας.
- Προχωρημένος
+ Για προχωρημένουςΝα μην αποστέλλονται αναφορές λαθών
+ Στέλνοντας ίχνη στοίβας βοηθάτε την συνεχόμενη ανάπτυξηΕπιβεβαίωση μηνυμάτων
- Να επιτρέπεται οι επαφές σας να γνωρίζουν ότι δεχτήκατε διαβάσατε τα μηνύματα τους
- Προφίλ χρήστη
+ Επιτρέψτε στις επαφές σας να γνωρίζουν όταν έχετε λάβει και διαβάσει τα μηνύματά τους
+ Διεπαφή χρήστη
+ Το OpenKeychain ανέφερε κάποιο σφάλμα.Σφάλμα στο κλειδί κρυπτογράφησηςΑποδοχήΈχει συμβεί κάποιο σφάλμα
@@ -119,11 +140,13 @@
Λήψη εικόναςΕρήμην χορήγηση αίτησης συνδρομήςΤο αρχείο που επιλέξατε δεν είναι εικόνα
+ Σφάλμα κατά τη μετατροπή του αρχείου εικόναςΤο αρχείο δεν βρέθηκεΓενικό σφάλμα εισόδου/εξόδου. Ίσως δεν έχετε ελεύθερο χώρο αποθήκευσης;
+ Η εφαρμογή που χρησιμοποιήσατε για να επιλέξετε αυτή την εικόνα δεν παραχώρησε αρκετά δικαιώματα για την ανάγνωση του αρχείου.\n\nΧρησιμοποιήστε διαφορετικό διαχειριστή αρχείων για να επιλέξετε μια εικόναΆγνωστοΠροσωρινά απενεργοποιημένο
- Συνδεμένος
+ Σε σύνδεσηΣύνδεση\u2026Εκτός σύνδεσηςΧωρίς εξουσιοδότηση
@@ -132,9 +155,10 @@
Η εγγραφή απέτυχεΤο όνομα χρησιμοποιείται ήδηΟλοκλήρωση εγγραφής
+ Ο διακομιστής δεν υποστηρίζει εγγραφήΆκυρο κουπόνι εγγραφής
- TLS αποτυχία επικοινωνίας
- Πολιτική παραβίασης
+ Αποτυχία διαπραγμάτευσης TLS
+ Παραβίαση κανονισμούΜη συμβατός διακομιστήςΣφάλμα μετάδοσηςΣφάλμα στην εκκίνηση μετάδοσης
@@ -143,21 +167,23 @@
OpenPGPOMEMOΔιαγραφή λογαριασμού
- Προσωρινά μη διαθέσιμο
+ Προσωρινή απενεργοποίησηΔημοσίευση εικόνας προφίλΔημοσίευση του δημόσιου κλειδιού OpenPGP
- Αφαίρεση OpenPGP δημόσιου κλειδιού
- Είστε σίγουρος ότι θέλετε να αφαιρέσετε το OpenPGP δημόσιο κλειδί σας από την παρούσα ανακοίνωσή σας?
-Οι επαφές σας δεν θα είναι πλέον δυνατόν να σας στείλουν OpenPGP κρυπτογραφημένα μηνύματα.
+ Διαγραφή δημόσιου κλειδιού OpenPGP
+ Είστε βέβαιοι ότι θέλετε να αφαιρέσετε το δημόσιο κλειδί σας OpenPGP από την ανακοίνωση παρουσίας σας;\nΟι επαφές σας δεν θα είναι πλέον δυνατόν να σας στείλουν κρυπτογραφημένα μηνύματα με OpenPGP.
+ Το δημόσιο κλειδί OpenPGP έχει δημοσιευτεί.Ενεργοποίηση λογαριασμούΕίστε βέβαιοι;
+ Η διαγραφή του λογαριασμού σας διαγράφει όλο το ιστορικό συζητήσεών σαςΕγγραφή φωνήςΔιεύθυνση XMPPΑποκλεισμός διεύθυνσης XMPPusername@example.comΣυνθηματικόΜη έγκυρη διεύθυνση XMPP
- Θέλετε να προσθέσετε %s στην ατζέντα σας?
+ Πλήρης μνήμη. Η εικόνα είναι πολύ μεγάλη
+ Θέλετε να προσθέσετε τον/την %s στο βιβλίο διευθύνσεών σας?Πληροφορίες διακομιστήXEP-0313: Διαχείριση αρχείου μηνυμάτωνXEP-0280: Αντίγραφα μηνυμάτων
@@ -165,29 +191,35 @@
XEP-0191: Εντολή αποκλεισμούXEP-0237: Διατήρηση εκδόσεων λίστας επαφώνXEP-0198: Διαχείριση ροών
+ XEP-0215: Εύρεση εξωτερικών υπηρεσιώνXEP-0163: Πρωτόκολλο προσωπικών συμβάντων (εικόνες προφίλ / ΟΜΕΜΟ)
- XEP-0363: Ανέβασμα αρχείου με πρωτόκολλο HTTP
+ XEP-0363: Μεταφόρτωση αρχείου με πρωτόκολλο HTTPXEP-0357: Pushδιαθέσιμοςμη διαθέσιμοςΕλλειπείς ανακοινώσεις δημοσίων κλειδιώνσυνδέθηκε τελευταία φορά μόλις τώρα
+ τελευταία σύνδεση πριν από 1 λεπτότελευταία σύνδεση πριν από %d λεπτά
+ τελευταία σύνδεση πριν από 1 ώρατελευταία σύνδεση πριν από %d ώρες
+ τελευταία σύνδεση πριν από 1 μέρατελευταία σύνδεση πριν από %d μέρες
+ Κρυπτογραφημένο μήνυμα. Παρακαλώ εγκαταστήστε το OpenKeychain για αποκρυπτογράφηση.
+ Βρέθηκαν νέα μηνύματα κρυπτογραφημένα με OpenPGPΤαυτότητα κλειδιού OpenPGPΑποτύπωμα OMEMOv\\Αποτύπωμα OMEMOΑποτύπωμα OMEMO μηνύματοςv\\Αποτύπωμα OMEMO μηνύματος\'Αλλες συσκευές
- Εμπιστευτίτε τα OMEMO αποτυπώματα
- Ανάσυρση κλειδιών...
+ Επαλήθευση των αποτυπωμάτων OMEMO
+ Μεταφόρτωση κλειδιών...ΈγινεΑποκρυπτογράφησηΣελιδοδείκτεςΑναζήτηση
- Πρόσθεσε επαφή
+ Εισαγωγή επαφήςΔιαγραφή επαφήςΛεπτομέρειες επαφήςΑποκλεισμός επαφής
@@ -200,39 +232,46 @@
channel@conference.example.comΑποθήκευση σαν σελιδοδείκτηΔιαγραφή σελιδοδείκτη
- Καταστροφή ομαδικής συζήτησης
- Καταστροφή καναλιού
- Είστε βέβαιοι ότι θέλετε να καταστρέψετε αυτή την ομαδική συζήτηση;\n\nΠροσοχή:Η ομαδική συζήτηση θα διαγραφεί οριστικά από τον διακομιστή.
+ Διαγραφή ομαδικής συζήτησης
+ Διαγραφή καναλιού
+ Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτή την ομαδική συζήτηση;\n\nΠροσοχή:Η ομαδική συζήτηση θα διαγραφεί οριστικά από τον διακομιστή.Είστε βέβαιοι ότι θέλετε να καταστρέψετε αυτό το δημόσιο κανάλι;\n\nΠροσοχή:Το κανάλι θα διαγραφεί πλήρως από τον διακομιστή.
- Δεν ήταν δυνατή η καταστροφή της ομαδικής συζήτησης
- Δεν ήταν δυνατή η καταστροφή του καναλιού
+ Δεν ήταν δυνατή η διαγραφή της ομαδικής συζήτησης
+ Δεν ήταν δυνατή η διαγραφή του καναλιούΕπεξεργασία θέματος ομαδικής συζήτησηςΘέμα
- Συμμετοχή σε ομαδική συζήτηση...
+ Εισαγωγή σε ομαδική συζήτηση...Έξοδος
- Η επαφή σας πρόσθεσε στην λίστα επαφών
+ Η επαφή σάς πρόσθεσε στην λίστα επαφώνΠροσθήκη επίσης
- Η επαφή %s έχει διαβάσει μέχρι αυτό το σημείο
- %sέχει διαβαστεί μέχρι αυτό το σημείο
+ Ο/Η %s έχει διαβάσει μέχρι αυτό το σημείο
+ Οι χρήστες %s έχουν διαβάσει μέχρι αυτό το σημείο
+ Ο χρήστης %1$s +%2$d ακόμα έχουν διαβάσει μέχρι αυτό το σημείοΌλοι έχουν διαβάσει μέχρι αυτό το σημείοΔημοσίευση
+ Επιλέξτε την εικόνα προφίλ για να διαλέξετε εικόνα από την έκθεσηΔημοσίευση...Ο διακομιστής απέρριψε την δημοσίευσή σας
+ Δεν ήταν δυνατή η μετατροπή της εικόνας σαςΑδύνατη η αποθήκευση της εικόνας προφίλ στο δίσκο(Ή πατήστε παρατεταμένα για να επιστρέψετε στο αρχικό)
+ Ο διακομιστής σας δεν υποστηρίζει την δημοσίευση εικονών προφίλψιθύρισεστο %sΑποστολή ιδιωτικού μηνύματος στην επαφή %sΣύνδεσηΑυτός ο λογαριασμός υπάρχει ήδηΕπόμενος
+ Σύσταση συνεδρίαςΠαράλειψηΑπενεργοποίηση ειδοποιήσεωνΕνεργοποίηση
- Η ομαδική συζήτηση απαιτεί κωδικό
+ Η ομαδική συζήτηση απαιτεί συνθηματικόΕισαγωγή συνθηματικού
+ Παρακαλώ αιτηθείτε ενημερώσεις παρουσίας από την επαφή σας πρώτα.\n\nΑυτό θα χρησιμοποιηθεί για να ταυτοποιηθεί το πρόγραμμα-πελάτης που χρησιμοποιεί η επαφή σας..Αίτηση τώραΑγνόηση
+ Προειδοποίηση: Η αποστολή αυτού χωρίς αμφίδρομες ενημερώσεις παρουσίας μπορεί να προκαλέσει απροσδόκητα προβλήματα.\n\nΠηγαίνετε στις \"Λεπτομέρειες επαφής\" για να επαληθεύσετε τις συνδρομές παρουσίας σας.ΑσφάλειαΝα επιτρέπεται η διόρθωση μηνυμάτωνΝα επιτρέπεται οι επαφές σας να διορθώνουν τα μηνύματά τους αναδρομικά
@@ -246,13 +285,16 @@
Οι ειδοποιήσεις θα σιγαστούν κατά τις ώρες ησυχίαςΆλλοΣυγχρονισμός με σελιδοδείκτες
- Είστε αποκλεισμένοι από αυτή την ομαδική συνδιάσκεψη
+ Συμμετοχή σε ομαδικές συζητήσεις αυτόματα αν ο σελιδοδείκτης αναφέρει αυτόματη συμμετοχή
+ Το αποτύπωμα OMEMO αντιγράφηκε στο πρόχειρο
+ Είστε αποκλεισμένοι από αυτή την ομαδική συζήτησηΑυτή η ομαδική συζήτηση είναι μόνο για μέληΠεριορισμός πόρων
- Έχετε διωχθει από αυτή την ομαδική συζήτηση
+ Έχετε διωχθεί από αυτή την ομαδική συζήτησηΗ ομαδική συζήτηση έχει τερματιστείΔεν είστε πλέον μέλος αυτής της ομαδικής συζήτησηςχρήση λογαριασμού %s
+ φιλοξενείται στο %sΈλεγχος %s στον διακομιστή HTTPΔεν είστε συνδεμένοι. Δοκιμάστε ξανά αργότεραΕλέγξτε το μέγεθος του %s
@@ -264,23 +306,24 @@
Αποστολή ξανάΔιεύθυνση URL αρχείουΗ διεύθυνση URL αντιγράφηκε στο πρόχειρο
- Αντιγραφή διεύθυνσης XMPP στο πρόχειρο
+ Η διεύθυνση XMPP αντιγράφηκε στο πρόχειροΤο μήνυμα λάθους αντιγράφηκε στο πρόχειροδιεύθυνση ιστούΣάρωση 2D γραμμοκώδικαΕμφάνιση 2D γραμμοκώδικα
- Εμφάνιση λίστας αποκλεισμένων
+ Εμφάνιση λίστας αποκλεισμούΛεπτομέρειες λογαριασμούΕπιβεβαίωσηΕπανάληψη
+ Υπηρεσία στο προσκήνιοΑποτρέπει τον τερματισμό της σύνδεσης από το λειτουργικό σύστημαΔημιουργία αντιγράφου ασφαλείαςΤα αντίγραφα ασφαλείας θα αποθηκεύονται στο %sΔημιουργία αντιγράφων ασφαλείαςΤο αντίγραφο ασφαλείας σας έχει δημιουργηθεί
- Τα αντίγραφα ασφαλείας έχουν αποθηκευτεί στο %s
+ Τα αρχεία του αντιγράφου ασφαλείας έχουν αποθηκευτεί στο %sΕπαναφορά αντιγράφου ασφαλείας
- Έχει γίνει επαναφορά του αντιγράφου ασφαλείας
+ Έχει γίνει επαναφορά του αντιγράφου ασφαλείας σαςΜην παραλείψετε να ενεργοποιήσετε τον λογαριασμό.Επιλογή αρχείουΛήψη %1$s (ολοκληρώθηκε %2$d%%)
@@ -289,26 +332,37 @@
αρχείοΆνοιγμα του %sαποστολή (ολοκλήρωση %1$d%%)
- το %s προσφέρθηκε για μεταφόρτωση
+ Προετοιμασία του αρχείου για διαμοιρασμό
+ %s προσφέρθηκε για μεταφόρτωσηΑκύρωση μετάδοσης
+ ο διαμοιρασμός του αρχείου απέτυχεη μεταφορά αρχείου ακυρώθηκε
+ Το αρχείο διαγράφηκε
+ Δεν βρέθηκε εφαρμογή για να ανοίξει το αρχείο
+ Δεν βρέθηκε εφαρμογή για να ανοίξει τον σύνδεσμο
+ Δεν βρέθηκε εφαρμογή για προβολή της επαφήςΔυναμικές ετικέτεςΕμφάνιση ετικετών μόνο για ανάγνωση κάτω από τις επαφέςΕνεργοποίηση ειδοποιήσεωνΔεν βρέθηκε διακομιστής ομαδικής συζήτησης
+ Η δημιουργία ομαδικής συζήτησης απέτυχεΕικόνα προφίλ λογαριασμούΑντιγραφή του αποτυπώματος OMEMO στο πρόχειροΑναδημιουργία κλειδιού OMEMOΚαθαρισμός συσκευών
+ Είστε βέβαιοι ότι θέλετε να αφαιρέσετε όλες τις άλλες συσκευές από την αναγγελία OMEMO; Την επόμενη φορά που θα συνδεθούν οι συσκευές σας θα αναγγείλουν την παρουσία τους ξανά, αλλά είναι πιθανό να μην λάβουν τυχόν μηνύματα που θα αποσταλούν στο ενδιάμεσο.
+ Δεν υπάρχουν διαθέσιμα χρήσιμα κλειδιά για αυτή την επαφή.\nΗ μεταφόρτωση νέων κλειδιών από τον διακομιστή ήταν ανεπιτυχής. Ίσως υπάρχει κάποιο πρόβλημα με τον διακομιστή της επαφής σας;
+ Δεν υπάρχουν διαθέσιμα χρήσιμα κλειδιά για αυτή την επαφή.\nΒεβαιωθείτε ότι έχετε συνδρομή αμοιβαίας παρουσίας.Κάτι πήγε στραβάΑνάκτηση ιστορικού από τον διακομιστήΔεν υπάρχει άλλο ιστορικό στον διακομιστήΕνημέρωση...
- Επιτυχής αλλαγή συνθηματικου!
+ Επιτυχής αλλαγή συνθηματικού!Δεν ήταν δυνατή η αλλαγή του συνθηματικούΑλλαγή συνθηματικούΤρέχον συνθηματικόΝέο συνθηματικό
+ Το συνθηματικό δεν μπορεί να είναι κενόΕνεργοποίηση όλων των λογαριασμώνΑπενεργοποίηση όλων των λογαριασμώνΕκτέλεση ενέργειας με
@@ -322,12 +376,13 @@
Απόδοση δικαιωμάτων διαχειριστήΑνάκληση δικαιωμάτων διαχειριστήΑπόδοση δικαιωμάτων ιδιοκτήτη
- Ανάκληση δικαιωμάτων ιδιοκτήτη
+ Ανάκληση δικαιωμάτων κατόχουΑφαίρεση από την ομάδική συζήτησηΑφαίρεση από το κανάλιΔεν ήταν δυνατή η αλλαγή του δεσμού της επαφής %sΑποκλεισμός από την ομαδική συζήτηση
- Απαγόρευση από το κανάλι
+ Αποκλεισμός από το κανάλι
+ Προσπαθείτε να αφαιρέσετε τον χρήστη %s από ένα δημόσιο κανάλι. Ο μόνος τρόπος να γίνει αυτό είναι να αποκλείσετε τον χρήστη για πάντα.Αποκλεισμός τώραΔεν ήταν δυνατή η αλλαγή ρόλου της επαφής %sΡύθμιση συζήτησης ιδιωτικής ομάδας
@@ -345,6 +400,7 @@
Σημείωμα ως αναγνωσμένοΕίσοδοςΑποστολή με το πλήκτρο Enter
+ Χρήση του πλήκτρου Enter για την αποστολή μηνύματος. Μπορείτε πάντα να χρησιμοποιείτε τον συνδυασμό Ctrl+Enter για να στείλετε μήνυμα, ακόμα και αν αυτή η επιλογή είναι απενεργοποιημένη.Εμφάνιση του πλήκτρου EnterΑλλαγή του πλήκτρου emoticons σε πλήκτρο Enterήχος
@@ -358,13 +414,14 @@
Προσφορά του %sΑπόκρυψη των εκτός σύνδεσηςΗ επαφή %s πληκτρολογεί...
- Ο χρήστης %s σταμάτησε να γράφει
+ Η επαφή %s σταμάτησε να πληκτρολογείΟι επαφές %s πληκτρολογούν...
- Η επαφή %s σταμάτησε να πληκτρολογεί
+ Η επαφές %s σταμάτησαν να πληκτρολογούνΕιδοποιήσεις πληκτρολόγησηςΕπιτρέψτε στις επαφές σας να γνωρίζουν πότε γράφετε μηνύματα προς αυτέςΑποστολή τοποθεσίαςΕμφάνιση τοποθεσίας
+ Δεν βρέθηκε εφαρμογή για την απεικόνιση τοποθεσίαςΤοποθεσίαΗ συζήτηση έκλεισεΑποχώρησε από την συζήτηση ιδιωτικής ομάδας
@@ -372,15 +429,16 @@
Μη έμπιστες αρχές πιστοποίησης συστήματοςΌλα τα πιστοποιητικά πρέπει να εγκριθούν χειροκίνηταΑφαίρεση πιστοποιητικών
- Διαγραφή με μη αυτόματο τρόπο των αναγνωρισμένων πιστοποιητικών
- Δεν υπάρχουν με μη αυτόματο τρόπο αναγνωρισμένα πιστοποιητικα
+ Διαγραφή των χειροκίνητα επιβεβαιωμένων πιστοποιητικών
+ Δεν υπάρχουν χειροκίνητα επιβεβαιωμένα πιστοποιητικαΑφαίρεση πιστοποιητικώνΔιαγραφή επιλογήςΑκύρωση
- %d πιστοποιητικο διαγραφθηκε
- %d πιστοποιητικά διαγραφθηκαν
+ %d πιστοποιητικό διαγράφηκε
+ %d πιστοποιητικά διαγράφηκαν
+ Αντικατάσταση του κουμπιού \"Αποστολή\" με γρήγορη ενέργειαΓρήγορη ΕνέργειαΚανέναΠιο πρόσφατα χρησιμοποιημένη
@@ -388,26 +446,37 @@
Αναζήτηση επαφώνΑναζήτηση σελιδοδεικτώνΑποστολή ιδιωτικού μηνύματος
+ Η επαφή %1$s αποχώρησε από την ομαδική συζήτησηΌνομα χρήστηΌνομα χρήστηΑυτό δεν είναι έγκυρο όνομα χρήστη
- Η μεταφόρτωση απέτυχε: Δεν βρέθηκε διακομιστής
+ Η μεταφόρτωση απέτυχε: Δεν βρέθηκε ο διακομιστήςΗ μεταφόρτωση απέτυχε: Δεν βρέθηκε το αρχείο
- Η μεταφόρτωση απέτυχε: Η σύνδεση ήταν αδύνατη
+ Η μεταφόρτωση απέτυχε: Δεν ήταν δυνατή η σύνδεσηΗ μεταφόρτωση απέτυχε: Αποτυχία εγγραφής αρχείουΤο δίκτυο Tor δεν είναι διαθέσιμοΑποτυχία διασύνδεσης
+ Ο διακομιστής δεν είναι υπεύθυνος για αυτόν τον τομέαΧαλασμένοςΔιαθεσιμότηταΕκτός χρήσης όταν η οθόνη είναι σβηστή
+ Εμφάνιση παρουσίας ως εκτός χρήσης όταν σβήνει η οθόνη
+ Απασχολημένος/η όταν βρίσκεται σε σιωπηρή λειτουργία
+ Σημειώνει την παρουσία σας ως Απασχολημένος/η όταν η συσκευή είναι σε κατάσταση σιωπήςΧρήση της κατάστασης δόνησης ως σιωπηρή κατάσταση
- Περισσότερες ρυθμίσεις σύνδεσης
- Εμφάνιση ονόματος μηχανήματος και ρυθμίσεων πόρτας όταν δημιουργείται ένας νέος λογαριασμός
+ Σημειώνει την παρουσία σας ως Απασχολημένος/η όταν η συσκευή είναι σε κατάσταση δόνησης
+ Αναλυτικότερες ρυθμίσεις σύνδεσης
+ Εμφάνιση ονόματος μηχανήματος και ρυθμίσεων θύρας όταν ρυθμίζεται νέος λογαριασμόςxmpp.example.com
+ Σύνδεση με πιστοποιητικό
+ Αδυναμία ανάγνωσης πιστοποιητικούΕπιλογές αρχειοθέτησηςΕπιλογές αρχειοθέτησης στον διακομιστήΜεταφόρτωση επιλογών αρχειοθέτησης. Παρακαλώ περιμένετε...
+ Αδυναμία μεταφόρτωσης ρυθμίσεων αρχειοθέτησης
+ Απαραίτηση η χρήση CAPTCHAΕισάγετε το κείμενο από την παραπάνω εικόνα
+ Η αλυσίδα του πιστοποιητικού δεν είναι έμπιστηΗ διεύθυνση XMPP δεν ταιριάζει με το πιστοποιητικόΑνανέωση πιστοποιητικούΣφάλμα μεταφόρτωσης κλειδιού OMEMO!
@@ -417,34 +486,50 @@
Σύνδεση μέσω TorΔρομολόγηση όλων των συνδέσεων μέσω του δικτύου Tor. Απαιτεί τη χρήση OrbotΌνομα μηχανήματος
- Πόρτα
- Ο αριθμός πόρτας δεν είναι έγκυρος
- Αυτό δεν είναι έγκυρο όνομα μηχανήματος
+ Θύρα
+ Διεύθυνση διακομιστή ή .onion
+ Ο αριθμός θύρας δεν είναι έγκυρος
+ Μη έγκυρο όνομα μηχανήματοςΣυνδέθηκαν %1$d από %2$d λογαριασμοί%d μήνυμα%d μηνύματαΦόρτωση περισσότερων μηνυμάτων
+ Το αρχείο διαμοιράστηκε με την επαφή %s
+ Η εικόνα διαμοιράστηκε με την επαφή %s
+ Η εικόνες διαμοιράστηκαν με την επαφή %s
+ Το κείμενο διαμοιράστηκε με την επαφή %s
+ Απόδοση δικαιώματος πρόσβασης του Conversations στον εξωτερικό αποθηκευτικό χώρο
+ Απόδοση δικαιώματος πρόσβασης στην φωτογραφική μηχανή για το ConversationsΣυγχρονισμός με επαφές
+ Το Conversations χρειάζεται δικαίωμα πρόσβασης στις επαφές σας για να ταιριάξει τη λίστα επαφών σας στον διακομιστή με το τοπικό βιβλίο διευθύνσεών σας για να σας δείξει τα πλήρη ονόματα και εικόνες προφίλ των επαφών σας.\n\nΘα διαβάσει τις επαφές σας και θα τις ταιριάξει τοπικά χωρίς να τις μεταφορτώσει στον διακομιστή σας. Δεν θα αποθηκευτεί αντίγραφο αυτών των τηλεφωνικών αριθμών.\n\nΓια περισσότερες πληροφορίες διαβάστε την πολιτική απορρήτου μας.
Θα σας ζητηθεί τώρα να δώσετε το δικαίωμα για πρόσβαση στις επαφές σας.]]>Ειδοποίηση για όλα τα μηνύματα
- Ειδοποίηση μόνο όταν κάποιος αναφέρεται σε εμένα
- Απενεργοποίηση ειδοποιήσεων
+ Ειδοποίηση μόνο όταν αναφέρεται το όνομά μου
+ Οι ειδοποιήσεις απενεργοποιήθηκανΠαύση ειδοποιήσεωνΣυμπίεση εικόνας
+ Συμβουλή: Χρησιμοποιήστε \"Επιλογή αρχείου\" αντι για \"Επιλογή εικόνας\" για να στείλετε μεμονωμένες εικόνες χωρίς συμπίεση, άσχετα από αυτή τη ρύθμιση.ΠάνταΜεγάλες εικόνες μόνο
- Ενεργοποίηση βελτιστοποίησης χρήσης μπαταρίας
+ Η βελτιστοποίηση χρήσης μπαταρίας είναι ενεργοποιημένη
+ Η συσκευή σας χρησιμοποιεί έντονη βελτιστοποίηση στην χρήση μπαταρίας του Conversations που μπορεί να οδηγήσει σε αργοπορημένες ειδοποιήσεις ή ακόμα και σε απώλεια μηνυμάτων.\nΠροτείνεται να την απενεργοποιήσετε.
+ Η συσκευή σας χρησιμοποιεί έντονη βελτιστοποίηση στην χρήση μπαταρίας του Conversations που μπορεί να οδηγήσει σε αργοπορημένες ειδοποιήσεις ή ακόμα και σε απώλεια μηνυμάτων.\nΘα σας ζητηθεί να την απενεργοποιήσετε.Απενεργοποίηση
- Η επιλεγμένη επιφάνεια είναι πολύ μεγάλη
+ Η επιλεγμένη περιοχή είναι πολύ μεγάλη(δεν υπάρχουν ενεργοί λογαριασμοί)Αυτό το πεδίο είναι υποχρεωτικόΔιόρθωση μηνύματοςΑποστολή διορθωμένου μηνύματος
+ Έχετε ήδη πιστοποιήσει με ασφάλεια το αποτύπωμα αυτού του ατόμου για να επιβεβαιώσετε την εμπιστοσύνη σας. Επιλέγοντας \"Τέλος\" απλά επιβεβαιώνετε ότι η επαφή %s είναι μέλος αυτής της ομαδικής συζήτησης.Έχετε απενεργοποιήσει αυτόν τον λογαριασμό
+ Σφάλμα ασφάλειας: Μη έγκυρη πρόσβαση σε αρχείο!
+ Δεν βρέθηκε εφαρμογή για να μοιραστείτε την διεύθυνση URIΔιαμοιρασμός της διεύθυνσης URI με... Εγγράφεστε με τον τηλεφωνικό σας αριθμό και το Quicksy αυτόματα — με βάση τους τηλεφωνικούς αριθμούς στο βιβλίο διευθύνσεών σας — προτείνει πιθανές επαφές για εσάς.
Με την εγγραφή σας συμφωνείτε με την πολιτική απορρήτου μας.]]>
+ Συμφωνώ και προχωρήστε
+ Θα καθοδηγηθείτε στη διαδικασία δημιουργίας ενός λογαριασμού στο conversations.im.¹\nΕπιλέγοντας το conversations.im ως πάροχο θα μπορείτε να επικοινωνείτε με χρήστες άλλων παρόχων δίνοντάς τους την πλήρη διεύθυνση XMPP σας.Η πλήρης ταυτότητα XMPP σας θα είναι: %sΔημιουργία λογαριασμούΧρήση του δικού μου παρόχου
@@ -453,8 +538,8 @@
Ορίστε την διαθεσιμότητά σας όταν διορθώνετε το μήνυμα κατάστασής σας.Μήνυμα κατάστασηςΕλεύθερος για συνομιλία
- Συνδεμένος
- Λείπω
+ Σε σύνδεση
+ Εκτός χρήσηςΜη διαθέσιμοςΑπασχολημένοςΈνα ασφαλές συνθηματικό έχει δημιουργηθεί
@@ -468,12 +553,17 @@
ΜικρόςΜεσαίοςΜεγάλος
+ Δημοσιοποίηση της τελευταίας χρήσης
+ Επιτρέψτε στις επαφές σας να γνωρίζουν πότε χρησιμοποιείτε το ConversationsΙδιωτικότηταΘέμαΕπιλογή παλέτας χρωμάτωνΑυτόματο
+ Ανοιχτόχρωμο
+ ΣκουρόχρωμοΠράσινο φόντοΧρήση πράσινου φόντου για εισερχόμενα μηνύματα
+ Αδυναμία σύνδεσης στο OpenKeychainΑυτή η συσκευή δεν χρησιμοποιείται πλέονΥπολογιστήςΚινητό τηλέφωνο
@@ -481,30 +571,41 @@
Περιηγητής ιστούΚονσόλαΑπαιτείται πληρωμή
+ Απόδοση δικαιώματος χρήσης InternetΕγώΗ επαφή ζητά συνδρομή σε υπηρεσία παρουσίαςΕπιτρέπωΔεν υπάρχει δικαίωμα για πρόσβαση στο %sΔεν βρέθηκε ο απομακρυσμένος διακομιστήςΛήξη χρόνου για τον απομακρυσμένο διακομιστή
+ Αδυναμία ενημέρωσης λογαριασμού
+ Αναφέρετε για αυτή την ταυτότητα XMPP ότι στέλνει ανεπιθύμητα μηνύματα.Διαγραφή ταυτοτήτων OMEMO
+ Αναδημιουργία των κλειδιών OMEMO. Όλες οι επαφές σας θα πρέπει να σας επαληθεύσουν πάλι. Χρησιμοποιήστε το μόνο ως τελευταία λύση.Διαγραφή επιλεγμένων κλειδιώνΠρέπει να είστε συνδεμένοι για να δημοσιεύσετε το avatar σας.Εμφάνιση μηνύματος λάθουςΜήνυμα λάθους
- Ενεργοποίηση μείωσης χρήσης δεδομένων κινητής
- Η συσκευή σας δεν υποστηρίζει την απενεργοποίηση μείωσης χρήσης δεδομένων κινητής για το Conversations.
+ Ενεργοποίηση μείωσης χρήσης δεδομένων
+ Το λειτουργικό σας σύστημα περιορίζει το Conversations από το να συνδέεται στο Internet όταν βρίσκεται στο παρασκήνιο. Για να λαμβάνετε ειδοποιήσεις νέων μηνυμάτων πρέπει να επιτρέψετε στο Conversations να έχει απεριόριστη πρόσβαση όταν ενεργοποιείται η μείωση χρήσης δεδομένων.\nΤο Conversations θα προσπαθεί να περιορίσει τη χρήση δεδομένων όταν είναι δυνατό.
+ Η συσκευή σας δεν υποστηρίζει την απενεργοποίηση μείωσης χρήσης δεδομένων για το Conversations.
+ Αδυναμία δημιουργίας προσωρινού αρχείουΑυτή η συσκευή έχει επαληθευτείΑντιγραφή αποτυπώματος
+ Έχετε επαληθεύσει όλα τα κλειδιά OMEMO που κατέχετε
+ Ο γραμμοκώδικας δεν περιέχει αποτυπώματα για την συζήτηση αυτή.Επαληθευμένα αποτυπώματα
- Χρήση της φωτογραφικής μηχανής για την σάρωση του γραμμοκώδικα κάποιας επαφής
+ Χρήση της φωτογραφικής μηχανής για την σάρωση του γραμμοκώδικα της επαφήςΠαρακαλώ περιμένετε την μεταφόρτωση των κλειδιώνΔιαμοιρασμός ως γραμμοκώδικαΔιαμοιρασμός ως διεύθυνση URI XMPPΔιαμοιρασμός ως σύνδεσμος HTTPΤυφλή εμπιστοσύνη πριν την επαλήθευση
+ Αυτόματη εμπιστοσύνη σε όλες τις νέες συσκευές επαφών που δεν έχουν επαληθευτεί παλιότερα, και παροχή χειροκίνητης επιβεβαίωσης κάθε φορά που μια επαληθευμένη επαφή προσθέτει μια νέα συσκευή.
+ Κλειδιά OMEMO με τυφλή εμπιστοσύνη, που σημαίνει ότι μπορεί να είναι κάποιος άλλος ή κάποιος τρίτος μπορεί να έχει αποκτήσει πρόσβαση. Μη έμπιστος
- Άκυρος 2D γραμμοκώδικας
+ Άκυρος γραμμοκώδικας 2D
+ Καθαρισμός κρυφού φακέλου (χρησιμοποείται από την εφαρμογή φωτογραφικής μηχανής)Καθαρισμός κρυφής μνήμηςΚαθαρισμός ιδιωτικής αποθήκευσηςΚαθαρισμός ιδιωτικού χώρου όπου αποθηκεύονται αρχεία (Μπορούν να μεταφορτωθούν ξανά από τον διακομιστή)
@@ -514,6 +615,7 @@
Εμφάνιση ανενεργώνΑπόκρυψη ανενεργώνΑναίρεση εμπιστοσύνης συσκευής
+ Είστε βέβαιοι ότι θέλετε να αφαιρέσετε την επαλήθευση για αυτή τη συσκευή;\nΑυτή η συσκευή και μηνύματα που έρχονται από αυτή τη συσκευή θα σημειωθούν ως μη έμπιστα.%d δευτερόλεπτο%d δευτερόλεπτα
@@ -539,23 +641,26 @@
%d μήνεςΑυτόματη διαγραφή μηνυμάτων
- Αυτόματη διαγραφή μηνυμάτων από αυτή την συσκευή που είναι παλιότερα από την ρυθμισμένη χρονική περίοδο
+ Αυτόματη διαγραφή μηνυμάτων από αυτή την συσκευή που είναι παλιότερα από την ρυθμισμένη χρονική περίοδο.Κρυπτογράφηση μηνύματοςΧωρίς μεταφόρτωση μηνυμάτων λόγω τοπικού χρόνου διατήρησης.Συμπίεση βίντεοΟι αντίστοιχες συζητήσεις έκλεισαν.Η επαφή αποκλείστηκε.Ειδοποιήσεις από άγνωστους
+ Ειδοποίηση για μηνύματα και κλήσεις που προέρχονται από άγνωστους.Λήψη μηνύματος από άγνωστοΑποκλεισμός αγνώστουΑποκλεισμός ολόκληρου τομέα
- συνδεμένος αυτή τη στιγμή
+ Σε σύνδεση αυτή τη στιγμήΕπανάληψη αποκρυπτογράφησηςΣφάλμα συνεδρίαςΥποβάθμιση του μηχανισμού SASLΟ διακομιστής απαιτεί εγγραφή σε ιστοσελίδαΆνοιγμα ιστοσελίδας
+ Δεν βρέθηκε εφαρμογή για να ανοίξει την ιστοσελίδαΑναδυόμενες ειδοποιήσεις
+ Εμφάνιση αναδυόμενων ειδοποιήσεωνΣήμεραΧτεςΕπαλήθευση ονόματος μηχανήματος με χρήση DNSSEC
@@ -570,7 +675,7 @@
Προστατευμένες εφαρμογέςΓια να συνεχίσετε να λαμβάνετε ειδοποιήσεις, ακόμα κι όταν η οθόνη είναι σβηστή, χρειάζεται να προσθέσετε το Conversations στον κατάλογο με τις προστατευμένες εφαρμογές.Αποδοχή άγνωστου πιστοποιητικού;
- Το πιστοποιητικό του διακομιστή δεν είναι υπογεγραμμένο από κάποια γνωστή Αρχή Πιστοποίησης
+ Το πιστοποιητικό του διακομιστή δεν είναι υπογεγραμμένο από κάποια γνωστή Αρχή Πιστοποίησης.Αποδοχή αναντίστοιχου ονόματος διακομιστή;Ο διακομιστής δεν ταυτοποιείται ως \"%s\". Το πιστοποιητικό είναι έγκυρο μόνο για:Θέλετε να συνδεθείτε έτσι κι αλλιώς;
@@ -583,8 +688,10 @@
Διόρθωση μηνύματος κατάστασηςΑπενεργοποίηση κρυπτογράφησηςΤο Conversations δεν είναι δυνατό να στείλει κρυπτογραφημένα μηνύματα στην επαφή %1$s. Αυτό μπορεί να συμβαίνει γιατί η επαφή σας χρησιμοποιεί παλιότερο διακομιστή ή πρόγραμμα που δε μπορεί να χειριστεί κρυπτογράφηση OMEMO.
+ Αδύναμια μεταφόρτωσης λίστας συσκευών
+ Αδυναμία ανάσυρσης κλειδιών κρυπτογράφησηςΣυμβουλή: Σε κάποιες περιπτώσεις αυτό μπορεί να διορθωθεί αν αμοιβαία προστεθείτε στους καταλόγους επαφών σας.
- Είστε βέβαιοι ότι θέλετε να απενεργοποιήσετε την κρυπτογράφηση OMEMO για αυτή τη συζήτηση;\nΑυτή η ενέργεια θα επιτρέψει στον διαχειριστή του διακομιστή να αναγνώσει τα μηνύματά σας, αλλά ίσως είναι ο μόνος τρόπος να επικοινωνήστε με επαφές που χρησιμοποιούν παλιότερα προγράμματα.
+ Είστε βέβαιοι ότι θέλετε να απενεργοποιήσετε την κρυπτογράφηση OMEMO για αυτή τη συζήτηση;\nΑυτή η ενέργεια θα επιτρέψει στον διαχειριστή του διακομιστή να αναγνώσει τα μηνύματά σας, αλλά ίσως είναι ο μόνος τρόπος να επικοινωνήσετε με επαφές που χρησιμοποιούν παλιότερα προγράμματα.Απενεργοποίηση τώραΠρόχειρο:Κρυπτογράφηση OMEMO
@@ -594,8 +701,8 @@
Δημιουργία συντόμευσηςΜέγεθος γραμματοσειράςΤο σχετικό μέγεθος γραμματοσειράς που χρησιμοποιείται στην εφαρμογή.
- Ενεργοποιημένο ερήμην
- Απενεργοποιημένο ερήμην
+ Ενεργοποιημένο από προεπιλογή
+ Απενεργοποιημένο από προεπιλογήΜικρόΜεσαίοΜεγάλο
@@ -611,7 +718,9 @@
Διαμοιρασμός τοποθεσίαςΕμφάνιση τοποθεσίαςΔιαμοιρασμός
+ Αδυναμία έναρξης εγγραφήςΠαρακαλώ περιμένετε...
+ Απόδοση δικαιώματος πρόσβασης στο μικρόφωνο για το ConversationsΑναζήτηση μηνυμάτωνGIFΕμφάνιση συζήτησης
@@ -626,20 +735,27 @@
Ο κεντρικός υπολογιστής δεν υποστηρίζει εικόνες προφίλ σε ομαδικές συζητήσειςΜόνο ο ιδιοκτήτης μπορεί να αλλάξει την εικόνα προφίλ μιας ομαδικής συζήτησηςΌνομα επαφής
- Παρατσούκλι
+ ΨευδώνυμοΌνομαΗ παροχή ονόματος είναι προαιρετικήΌνομα ομαδικής συζήτησηςΑυτή η ομαδική συζήτηση έχει καταστραφεί
+ Αδυναμία αποθήκευσης εγγραφήςΥπηρεσία στο προσκήνιοΑυτή η κατηγορία ειδοποιήσεων χρησιμοποιείται για την εμφάνιση μιας μόνιμης ειδοποίησης που δείχνει πως εκτελείται το Conversations.Πληροφορίες κατάστασηςΠροβλήματα συνδεσιμότηταςΑυτή η κατηγορία ειδοποιήσεων χρησιμοποιείται για να εμφανίσει μια ειδοποίηση σε περίπτωση που υπάρχει πρόβλημα σύνδεσης σε κάποιον λογαριασμό.Μηνύματα
+ ΚλήσειςΜηνύματα
+ Εισερχόμενες κλήσεις
+ Κλήσεις σε εξέλιξηΣιωπηρά μηνύματαΑυτή η κατηγορία ειδοποιήσεων χρησιμοποιείται για να εμφανίσει ειδοποιήσεις που δεν θα έπρεπε να παράγουν ήχο. Για παράδειγμα όταν κάποιος είναι ενεργός σε άλλη συσκευή (περίοδος χάριτος).
+ Αποτυχημένες διανομές
+ Ρυθμίσεις ειδοποίησης μηνυμάτων
+ Ρυθμίσεις ειδοποίησης εισερχόμενων κλήσεωνΣημασία, Ήχος, ΔόνησηΣυμπίεση βίντεοΕμφάνιση μέσου
@@ -681,7 +797,10 @@
Ο κωδικός που σας έχει σταλεί έχει λήξει.Άγνωστο σφάλμα δικτύου.Άγνωστη απάντηση από τον διακομιστή.
- Κάτι πήγε στραβά κατά την εξυπηρέτηση της αίτησής σας.
+ Αδυναμία σύνδεσης στον διακομιστή.
+ Αδυναμία δημιουργίας ασφαλούς σύνδεσης.
+ Αδυναμία εύρεσης του διακομιστή.
+ Κάτι δεν πήγε καλά κατά την εξυπηρέτηση της αίτησής σας.Μη έγκυρη εισαγωγήΠροσωρινά μη διαθέσιμο. Προσπαθήστε αργότερα.Χωρίς σύνδεση δικτύου.
@@ -694,7 +813,7 @@
Παρακαλώ εισάγετε το όνομά σας για να επιτρέψετε σε χρήστες, που δεν σας έχουν καταγράψει στα βιβλία διευθύνσεών τους, να γνωρίζουν ποιος/α είστε.Το όνομά σαςΕισάγετε το όνομά σας
- Χρησιμποιήστε το κουμπί διόρθωσης για να ορίσετε το όνομά σας.
+ Χρησιμοποιήστε το κουμπί διόρθωσης για να ορίσετε το όνομά σας.Απόρριψη αίτησηςΕγκατάσταση OrbotΕκκίνηση Orbot
@@ -709,6 +828,8 @@
ΕπαναφοράΕισάγετε τον κωδικό σας για τον λογαριασμό %s για να επαναφέρετε το αντίγραφο ασφαλείας.Μην χρησιμοποιείτε τη λειτουργία επαναφοράς αντιγράφων ασφαλείας για να κλωνοποιήσετε (ταυτόχρονη εκτέλεση) μια εγκατάσταση. Η επαναφορά αντιγράφου ασφαλείας προσφέρεται μόνο για μεταφορές ή σε περίπτωση που έχετε χάσει την αρχική συσκευή.
+ Αδυναμία επαναφοράς αντιγράφου ασφαλείας.
+ Αδυναμία αποκρυπτογράφησης του αντιγράφου ασφαλείας. Είναι ο κωδικός σωστός;Δημιουργία & Επαναφορά αντιγράφων ασφαλείαςΕισάγετε τη διεύθυνση XMPPΔημιουργία ομαδικής συζήτησης
@@ -723,24 +844,26 @@
Δημιουργία δημόσιου καναλιού...Αυτό το κανάλι υπάρχει ήδηΈχετε εισέρθει σε ένα προϋπάρχον κανάλι
+ Αδυναμία ορισμού ρυθμίσεων καναλιούΑλλαγή θέματος από οποιονδήποτεΠρόσκληση άλλων χρηστών από οποιονδήποτεΟποιοσδήποτε μπορεί να αλλάξει το θέμα.
- Οι ιδιοκτήτες μπορούν να διορθώσουν το θέμα.
- Οι διαχειριστές μπορούν να διορθώσουν το θέμα.
- Οι ιδιοκτήτες μπορούν να προσκαλούν άλλους χρήστες.
+ Οι ιδιοκτήτες μπορούν να αλλάξουν το θέμα.
+ Οι διαχειριστές μπορούν να αλλάξουν το θέμα.
+ Οι κάτοχοι μπορούν να προσκαλούν άλλους χρήστες.Οποιοσδήποτε μπορεί να προσκαλεί άλλους χρήστες.Οι διευθύνσεις XMPP είναι ορατές στους διαχειριστές.Οι διευθύνσεις XMPP είναι ορατές σε όλους.Αυτό το δημόσιο κανάλι δεν έχει συμμετέχοντες. Προσκαλέστε τις επαφές σας ή χρησιμοποιήστε το κουμπί διαμοιρασμού για να διαδώσετε τη διεύθυνση XMPP του.
- Αυτή η συζήτησης ιδιωτικής ομάδας δεν έχει συμμετέχοντες.
+ Αυτή η συζήτηση ιδιωτικής ομάδας δεν έχει συμμετέχοντες.Διαχείριση δικαιωμάτωνΑναζήτηση συμμετεχόντωνΤο αρχείο είναι πολύ μεγάλοΕπισύναψη
- Ανακάλυψη καναλιών
+ Εύρεση καναλιώνΑναζήτηση καναλιώνΠιθανή παραβίαση ιδιωτικότητας!
+ search.jabber.network.
Χρησιμοποιώντας αυτή τη λειτουργία θα μεταβιβαστεί η διεύθυνση IP σας και οι όροι αναζήτησης σε αυτή την υπηρεσία. Δείτε την Πολιτική Ιδιωτικότητας της για περισσότερες πληροφορίες.]]>Έχω ήδη λογαριασμόΠροσθήκη υπάρχοντος λογαριασμούΕγγραφή νέου λογαριασμού
@@ -748,21 +871,80 @@
Προσθήκη έτσι κι αλλιώςΑυτό μοιάζει με διεύθυνση καναλιούΔιαμοιρασμός αντιγράφων ασφαλείας
- Αντίγραφο Conversations
+ Αντίγραφο ασφαλείας ConversationsΓεγονόςΆνοιγμα αντιγράφου ασφαλείαςΤο αρχείο που επιλέξατε δεν είναι αντίγραφο ασφαλείας του ConversationsΑυτός ο λογαριασμός έχει προστεθεί ήδηΠαρακαλώ εισάγετε τον κωδικό για αυτό το λογαριασμό
+ Αδυναμία εκτέλεσης αυτής της λειτουργίαςΕίσοδος σε δημόσιο κανάλι...
+ Η εφαρμογή από την οποία έγινε διαμοίραση δεν έδωσε δικαιώματα πρόσβασης στο αρχείο.
+ jabber.networkΤοπικός διακομιστήςΟι περισσότεροι χρήστες πρέπει να επιλέξουν ‘jabber.network’ για καλύτερες προτάσεις από το σύνολο του οικοσυστήματος XMPP.
- Μέθοδος ανακάλυψης καναλιού
+ Μέθοδος εύρεσης καναλιούΑντίγραφο ασφαλείαςΣχετικά με
+ Παρακαλώ ενεργοποιήστε έναν λογαριασμό
+ Νέα κλήση
+ Εισερχόμενη κλήση
+ Εισερχόμενη κλήση βίντεο
+ Σύνδεση
+ Συνδέθηκε
+ Αποδοχή κλήσης
+ Τερματισμός κλήσης
+ Απάντηση
+ Παράβλεψη
+ Εύρεση συσκευών
+ Κουδούνισμα
+ Απασχολημένος
+ Αδυναμία σύνδεσης κλήσης
+ Απώλεια σύνδεσης
+ Αποσυρμένη κλήση
+ Αποτυχία εφαρμογής
+ Τερματισμός κλήσης
+ Κλήση σε εξέλιξη
+ Κλήση βίντεο σε εξέλιξη
+ Απενεργοποίηστε το Tor για να κάνετε κλήσεις
+ Εισερχόμενη κλήση
+ Εισερχόμενη κλήση · %s
+ Εξερχόμενη κλήση
+ Εξερχόμενη κλήση · %s
+ Αναπάντηση κλήση
+ Κλήση ήχου
+ Κλήση βίντεο
+ Βοήθεια
+ Εναλλαγή στη συζήτηση
+ Το μικρόφωνο δεν είναι διαθέσιμο
+ Μπορείτε να κάνετε μόνο μια κλήση τη φορά.
+ Επιστροφή στην κλήση σε εξέλιξη
+ Αδυναμία εναλλαγής κάμερας
+ Καρφίτσωμα στην κορυφή
+ Ξεκαρφίτσωμα από την κορυφή
+ Ίχνος GPX
+ Αδυναμία διόρθωσης μηνύματος
+ Όλες οι συζητήσεις
+ Αυτή η συζήτηση
+ Η φωτογραφία προφίλ σας
+ Φωτογραφία προφίλ του/της %s
+ Κρυπτογραφημένη με OMEMO
+ Κρυπτογραφημένη με OpenPGP
+ Χωρίς κρυπτογράφηση
+ Έξοδος
+ Ηχογράφηση μηνύματος τηλεφωνητή
+ Αναπαραγωγή ήχου
+ Παύση ήχου
+ Προσθήκη επαφής, δημιουργία ή είσοδος σε ομαδική συζήτηση, ή εύρεση καναλιώνΕμφάνιση %1$d συμμετέχονταΕμφάνιση %1$d συμμετεχόντων
+
+ Κάποιο μήνυμα δεν ήταν δυνατό να παραδοθεί
+ Κάποια μηνύματα δεν ήταν δυνατό να παραδοθούν
+
+ Αποτυχημένες διανομές
+ Περισσότερες επιλογές
diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml
index 31301b574..2a70dccea 100644
--- a/src/main/res/values-es/strings.xml
+++ b/src/main/res/values-es/strings.xml
@@ -753,6 +753,7 @@
Llamadas salientesMensajes sin sonidoEste grupo de notificaciones se usa para mostrar notificaciones que no deberían emitir ningún sonido. Por ejemplo, cuando estás activo en otro dispositivo (periodo de gracia).
+ Envíos fallidosAjustes de notificación de mensajesAjustes de notificación de llamadasImportancia, Sonido, Vibración
@@ -920,6 +921,8 @@
Solo puedes hacer una llamada a la vezVolver a la llamada en cursoNo se ha podido cambiar de cámara
+ Fijar en la parte superior
+ Desfijar de la parte superiorRecorrido GPXNo se pudo corregir el mensajeTodas las conversaciones
@@ -938,4 +941,11 @@
Ver %1$d ParticipanteVer %1$d Participantes
+
+ Un mensaje no se ha podido entregar
+ Algunos mensajes no se han podido entregar
+
+ Envíos fallidos
+ Más opciones
+ No se ha encontrado aplicación
diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml
index 523ec019f..e8918029b 100644
--- a/src/main/res/values-fr/strings.xml
+++ b/src/main/res/values-fr/strings.xml
@@ -4,6 +4,7 @@
Nouvelle conversationGérer les comptesGérer le compte
+ Fermer la conversationDétails du contactDétails du groupeDétails du canal
@@ -399,6 +400,7 @@
Marquer comme luSaisieTouche Entrée pour envoyer
+ Utilisez la touche Entrée pour envoyer un message. Vous pourrez toujours utiliser la combinaison Ctrl+Entrée pour envoyer un message, même si cette option est désactivée.Afficher la touche EntréeRemplacer la touche Émoticônes par une touche Entrée.audio
@@ -458,7 +460,11 @@
DétraquéDisponibilitéAbsent quand l\'écran est éteint
+ Absent quand l\'écran est éteint
+ Occupé en mode silence
+ Occupé lorsque l\'appareil est en mode silencieuxIndisponible en mode vibreur
+ Occupé lorsque l\'appareil est en mode vibreurParamètres de connexion avancésMontrer le nom d\'hôte et le port lors du paramétrage d\'un comptexmpp.example.com
@@ -747,6 +753,7 @@
Appels sortantsMessages silencieuxCe groupe de notifications est utilisé pour afficher les notifications qui ne doivent pas émettre de son. Par exemple, lorsque le son est activé sur un autre appareil (délai de grâce).
+ Échec lors de la livraisonParamètres de notification des messagesParamètres de notification d\'appels entrantsImportance, son, vibration
@@ -894,6 +901,7 @@
Ça sonneOccupéImpossible de connecter l\'appel
+ Connexion perdueAppel annuléÉchec de l\'applicationRaccrocher
@@ -907,12 +915,36 @@
Appel manquéAppel audioAppel vidéo
+ Aide
+ Basculer vers la conversationVotre micro n\'est pas disponibleVous ne pouvez prendre qu\'un appel à la fois.Reprendre l\'appel en coursImpossible de changer de caméra
+ Épingler en haut de la liste
+ Détacher du haut de la liste
+ Trace GPX
+ Impossible de corriger le message
+ Toutes les conversations
+ Cette conversation
+ Votre avatar
+ Avatar pour %s
+ Chiffré avec OMEMO
+ Chiffré avec OpenPGP
+ Non chiffré
+ Quitter
+ Enregistrer un message vocal
+ Lire l\'audio
+ Mettre en pause l\'audio
+ Ajouter un contact, créer ou joindre un groupe de discussion, ou découvrir les salonsVoir %1$d participantVoir %1$d participants
+
+ Certains messages n\'ont pas pu être distribués
+ Certains messages n\'ont pu être distribués
+
+ Échec lors de la livraison
+ Plus d\'options
diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml
index 7f38f1f4a..1fc99e5c1 100644
--- a/src/main/res/values-gl/strings.xml
+++ b/src/main/res/values-gl/strings.xml
@@ -550,7 +550,7 @@
Creando unha conversa en grupo...Invitar de novoDesactivar
- Corto
+ BreveMedioLongoPublicar utilización
@@ -947,4 +947,8 @@
Entregas fallidasMáis opcións
+ Non se atopa unha aplicación
+ Convida a Conversations
+ Non se puido enviar o convite
+ O servidor non soporta a creación de convites
diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml
index 2201a659f..d1c193856 100644
--- a/src/main/res/values-hu/strings.xml
+++ b/src/main/res/values-hu/strings.xml
@@ -4,6 +4,7 @@
Új beszélgetésFiókok kezeléseFiók kezelése
+ Beszélgetés bezárásaPartner részleteiCsoportos csevegés részleteiCsatorna részletei
@@ -39,6 +40,7 @@
ModerátorRésztvevőLátogató
+ Szeretné eltávolítani %s partnerét a partnerlistájából? Beszélgetései ezzel a partnerrel nem lesznek eltávolítva.Szeretné tiltani %s partnert, hogy ne tudjon üzeneteket küldeni?Szeretné feloldani %s partner tiltását és lehetővé tenni számára az üzenetek küldését?%s összes partnerét tiltja?
@@ -63,6 +65,7 @@
MentésRendbenA Conversations összeomlott
+ Azzal, hogy az XMPP fiókja használatával beküldi a veremkiíratásokat, elősegítheti a Conversations alkalmazás folyamatos fejlesztését.Küldés mostSose kérdezzen újraNem sikerült kapcsolódni a fiókhoz
@@ -77,6 +80,7 @@
Fájlok megosztása. Kérem várjon…Előzmények törléseBeszélgetés előzményeinek törlése
+ Biztosan törölni szeretné az összes üzenetet a beszélgetésen belül?\n\nFigyelmeztetés: Ez nem fogja érinteni a más eszközökön vagy kiszolgálókon tárolt üzeneteket.Fájl törléseBiztosan törölni szeretné ezt a fájlt?\n\nFigyelmeztetés: Ez nem fogja törölni a fájlnak azon másolatait, amelyek más eszközökön vagy kiszolgálókon vannak tárolva.A beszélgetés bezárása azután
@@ -91,13 +95,16 @@
Küldés titkosítatlanulA visszafejtés sikertelen. Talán nem rendelkezik a megfelelő személyes kulccsal.OpenKeychain
+ A Conversations az OpenKeychain-t használja az üzenetek titkosításához és visszafejtéséhez, valamint a személyes kulcsai kezeléséhez.\n\nEz GPLv3+ szerint licencelt, és elérhető az F-Droid és a Google Play szoftveráruházakban.\n\n(Ezután indítsa újra a Conversations alkalmazást.)ÚjraindításTelepítésTelepítse az OpenKeychain alkalmazásfelajánlás…várakozás…Nem található OpenPGP kulcs
+ Nem lehet titkosítani az üzenetét, mert a partnere nem közölte a nyilvános kulcsát.\n\nKérje meg a partnerét, hogy állítsa be az OpenPGP-t.Nem találhatók OpenPGP kulcsok
+ Nem lehet titkosítani az üzenetét, mert a partnerei nem közölték a nyilvános kulcsukat.\n\nKérje meg a partnereit, hogy állítsák be az OpenPGP-t.ÁltalánosFájlok fogadásaFájlok automatikus fogadása, amelyek kisebbek mint…
diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml
index b7b8e356a..600cc0d19 100644
--- a/src/main/res/values-it/strings.xml
+++ b/src/main/res/values-it/strings.xml
@@ -4,6 +4,7 @@
Nuova conversazioneGestisci accountGestisci account
+ Chiudi conversazioneDettagli del contattoDettagli chat di gruppoDettagli canale
@@ -752,6 +753,7 @@
Chiamate in uscitaMessaggi silenziosiQuesto gruppo di notifiche è usato per mostrare notifiche che non devono riprodurre alcun suono. Ad esempio mentre si è attivi su un altro dispositivo (Periodo di grazia).
+ Recapiti fallitiImpostazioni di notifica dei messaggiImpostazioni di notifica delle chiamate in arrivoImportanza, suono, vibrazione
@@ -919,10 +921,31 @@
Puoi fare solo una chiamata alla volta.Torna alla chiamata in corsoImpossibile cambiare fotocamera
+ Fissa in alto
+ Rimuovi dall\'altoTraccia GPXImpossibile correggere il messaggio
+ Tutte le conversazioni
+ Questa conversazione
+ Il tuo avatar
+ Avatar di %s
+ Crittografato con OMEMO
+ Crittografato con OpenPGP
+ Non crittografato
+ Esci
+ Registra in segreteria
+ Riproduci audio
+ Pausa
+ Aggiungi un contatto, crea o visita una chat di gruppo, o scopri canaliVedi %1$d partecipanteVedi %1$d partecipanti
+
+ Un messaggio non è stato recapitato
+ Alcuni messaggi non sono stati recapitati
+
+ Recapiti falliti
+ Altre opzioni
+ Nessuna applicazione trovata
diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml
index 92bf10639..f4a6a59dd 100644
--- a/src/main/res/values-ja/strings.xml
+++ b/src/main/res/values-ja/strings.xml
@@ -4,6 +4,7 @@
新しい会話アカウントの管理アカウントの管理
+ この会話を閉じる連絡先の詳細談話室の詳細チャンネルの詳細
@@ -15,6 +16,8 @@
連絡先のブロックを解除ドメインをブロックドメインのブロックを解除
+ 参加者をブロック
+ 参加者のブロックを解除アカウントの管理設定会話で共有
@@ -26,6 +29,7 @@
ちょうど今1 分前%d 分前
+ 未読%d件送信中…メッセージを復号化しています。しばらくお待ちください…OpenPGP 暗号化メッセージ
@@ -36,17 +40,20 @@
モデレーター参加者ビジター
+ 連絡先リストから %s を削除しますか? この連絡先との会話は削除されません。%s からあなたに送信されるメッセージをブロックしますか?%s のブロックを解除し、あなたにメッセージを送信できるようにしますか?%s からの連絡をすべてブロックしますか?%s からの連絡をすべてブロック解除しますか?連絡先をブロックしましたブロックしました
+ %s のブックマークを削除しますか? このブックマークとの会話は削除されません。サーバーに新しいアカウントを登録サーバーのパスワードを変更…で共有会話を始める連絡先を招待
+ 招待連絡先連絡先キャンセル
@@ -61,12 +68,19 @@
Conversations がクラッシュしました今すぐ送信今後表示しない
+ アカウントに接続できません
+ 複数のアカウントに接続できません
+ タップしてアカウントを管理添付ファイル
+ 連絡先が連絡先リストにありません。追加しますか?連絡先を追加配信に失敗しました
+ 転送用画像の準備中
+ 転送用画像の準備中フィルを共有しています。しばらくお待ちください…履歴をクリア会話履歴をクリア
+ この会話のすべてのメッセージを削除してもよろしいですか?\n\n警告: 他のデバイスやサーバーに保存されているメッセージのコピーには影響しません。ファイルを削除このファイルを削除してもよろしいですか?\n\n警告: これは、他のデバイスやサーバーに保存されているファイルのコピーは削除しません。その後、この会話を閉じる
@@ -77,16 +91,20 @@
OMEMO 暗号化メッセージを送信v\\OMEMO 暗号化メッセージを送信OpenPGP 暗号化メッセージを送信
+ ニックネームが変更されました暗号化されていない送信復号化に失敗しました。おそらく秘密鍵が正しくないようです。OpenKeychain
+ Conversations は OpenKeychain を利用して、メッセージの暗号化および復号化、そしてあなたの公開鍵を管理します。\n\nOpenKeychain は GPLv3+ ライセンスの下で、F-Droid および Google Play から利用可能です。\n\n(後で Conversations を再起動してください。)再起動インストールOpenKeychain をインストールしてください依頼中…待機中…OpenPGP の鍵はありません
+ 連絡先が公開鍵を通知しないため、あなたのメッセージを暗号化することができません。\n\n連絡先に OpenPGP をセットアップするように依頼してください。OpenPGP の鍵はありません
+ 連絡先が公開鍵を通知しないため、あなたのメッセージを暗号化することができません。\n\n連絡先に OpenPGP をセットアップするように依頼してください。全般ファイルを受取自動的に小さいファイルを受取…
@@ -96,12 +114,19 @@
新しいメッセージが届いたときに振動しますLED 通知新しいメッセージが届いたときに通知ライトを点滅します
+ 着信音
+ 通知音
+ 新着メッセージの通知音
+ 音声通話の着信音猶予期間
+ 別のデバイスでの操作を検知した際に、通知を止める時間の長さ詳細クラッシュレポートを送信しない
+ スタックトレースを送信して、Conversations の継続的な開発を支援しますメッセージの確認あなたがメッセージを受信して読んだときに、連絡先に知らせますUI
+ OpenKeychain でエラーが発生しました。暗号化の鍵が不正です。受付エラーが発生しました
@@ -114,8 +139,10 @@
写真の撮影事前にサブスクリプション要求を許可する選択したファイルは画像ではありません
+ 画像ファイルを変換できませんファイルが見つかりません一般的な I/O エラー。おそらく空き容量がなくなっていませんか?
+ あなたが画像の選択のために使用したアプリは、読み取りに必要なアクセス権がありません。\n\n別のファイルマネージャを使用して、画像を選択してください。不明一時的に無効オンライン
@@ -127,6 +154,8 @@
登録に失敗しましたユーザー名はすでに使用されています登録が完了しました
+ サーバーが登録をサポートしていません
+ トークンが無効ですTLS ネゴシエーションに失敗しましたポリシー違反互換性のないサーバー
@@ -142,14 +171,17 @@
OpenPGP 公開鍵を公開OpenPGP 公開鍵を削除在席通知から OpenPGP 公開鍵を削除してもよろしいですか?\n連絡先はあなたに OpenPGP 暗号化メッセージを送信できなくなります。
+ OpenPGP 公開鍵を公開しました。アカウントを有効にするよろしいですか?
+ アカウントを削除すると会話履歴がすべて消去されます音声を録音XMPP アドレスXMPP アドレスをブロックusername@example.comパスワード正しい XMPP アドレスではありません
+ メモリ不足です。画像が大きすぎます%s をお使いのアドレス帳に追加しますか?サーバー情報XEP-0313: メッセージ アーカイブ管理
@@ -165,9 +197,14 @@
利用不可公開鍵の通知がありませんちょうど今会いました
+ 1 分前に会いました%d 分前に会いました
+ 1 時間前に会いました%d 時間前に会いました
+ 1 日前に会いました%d 日前に会いました
+ 暗号化されたメッセージです。復号化するには OpenKeychain をインストールしてください。
+ 新しい OpenPGP 暗号化されたメッセージが見つかりましたOpenPGP 鍵 IDOMEMO フィンガープリントv\\OMEMO フィンガープリント
@@ -178,6 +215,7 @@
鍵の取得中…完了復号化
+ ブックマーク検索連絡先を入力連絡先を削除
@@ -193,8 +231,11 @@
ブックマークとして保存ブックマークを削除グループチャットを破棄する
+ チャンネルを破棄するこのグループチャットを破棄してもよろしいですか?\n\n警告: グループチャットはサーバーから完全に削除されます。
+ このチャンネルを破棄してもよろしいですか?\n\n警告: チャンネルはサーバーから完全に削除されます。グループチャットを削除できません
+ チャンネルを削除できません談話室の題を編集トピック談話室に参加しています…
@@ -203,25 +244,32 @@
戻りを追加%s はここまで読みました%s はここまで読みました
+ %1$s +%2$d 全員ここまで読みました全員ここまで読みました公開
+ アバターをタップしてギャラリーから画像を選択します公開中…サーバーがあなたの公開を拒否しました
+ 画像を変換できませんディスクにアバターを保存できませんでした(または長押しするとデフォルトに戻します)
+ ご利用のサーバーは、アバターの公開をサポートしていませんささやいた%s へプライベートメッセージを %s に送信接続このアカウントはすでに存在します次へ
+ セッションが確立スキップ通知を無効にする有効談話室にはパスワードが必要パスワードを入力してください
+ 最初に連絡先から参加アップデートを要求してください。\n\nこれは、連絡先が何のクライアントを使用しているかを決めるために使用されます。今すぐ要求無視
+ 警告: 相互の参加アップデートなしにこれを送信すると、予期しない問題が発生する可能性があります。\n\nあなたの参加サブスクリプションを検証するために、連絡先の詳細に移動します。セキュリティメッセージの修正を許可連絡先が、遡及的に自分のメッセージを編集することを許可します
@@ -235,6 +283,8 @@
消音時間の間、通知は無音になりますその他ブックマークと同期
+ ブックマークに従って、グループチャットに自動参加します。
+ OMEMO フィンガープリントをクリップボードにコピーしましたこの談話室から締め出されていますこの談話室はメンバー制ですリソース制約
@@ -253,6 +303,7 @@
再送ファイル URLURL をクリップボードにコピーしました
+ XMPP アドレスをクリップボードにコピーしましたエラーメッセージをクリップボードにコピーしましたウェブアドレス2D バーコードをスキャン
@@ -261,7 +312,16 @@
アカウントの詳細確認再度実行してください
+ フォアグラウンドサービスオペレーティングシステムが接続を切断するのを防止します
+ バックアップを作成
+ バックアップファイルは %s に保存されます
+ バックアップを作成しています
+ バックアップを作成しました
+ バックアップファイルは %s に保存されました
+ バックアップを復元
+ バックアップを復元しました
+ アカウントを有効にしてください。ファイルの選択%1$s 受信中 (%2$d%% 完了)%s のダウンロード
@@ -269,16 +329,25 @@
ファイル%s を開く送信中 (%1$d%% 完了)
+ 転送用ファイルの準備中%s ダウンロード依頼中転送をキャンセル
+ ファイル転送に失敗しました
+ 転送をキャンセルしました
+ ファイルを削除しました
+ ファイルを開くアプリケーションが見つかりません
+ リンクを開くアプリケーションが見つかりません
+ 連絡先を表示するアプリケーションが見つかりませんダイナミック タグ連絡先の下に、読み取り専用タグを表示します通知を有効にする談話室のサーバーが見つかりませんでした
+ グループチャットを作成できませんアカウント アバターOMEMO フィンガープリントをクリップボードにコピーOMEMO 鍵を再生成デバイスをクリア
+ この連絡先で使用可能な鍵がありません。\nサーバーから新しい鍵を取得できませんでした。連絡先のサーバーに問題がある可能性があります。何か問題が発生しました。サーバーから履歴を取得中サーバーにこれ以上履歴はありません
@@ -288,6 +357,7 @@
パスワードの変更現在のパスワード新しいパスワード
+ パスワードは空にできませんすべてのアカウントを有効にするすべてのアカウントを無効にするアクションを実行...
@@ -301,6 +371,7 @@
グループチャットから削除%s の所属を変更できません談話室から蹴り出す
+ チャンネルから追い出す今すぐ禁止%s の役割を変更できませんプライベート、メンバーのみ
@@ -334,6 +405,7 @@
あなたがメッセージを書いているときに、連絡先に知らせます位置を送信位置を表示
+ 位置を表示するアプリケーションが見つかりません位置会話が閉じられましたシステムの CA を信頼しない
diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml
index 07e572ae9..1e23928de 100644
--- a/src/main/res/values-pl/strings.xml
+++ b/src/main/res/values-pl/strings.xml
@@ -969,4 +969,5 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż
Nie dostarczone wiadomościWięcej ustawień
+ Nie znaleziono żadnej aplikacji
diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml
index a8d56e895..ce7e3d873 100644
--- a/src/main/res/values-pt-rBR/strings.xml
+++ b/src/main/res/values-pt-rBR/strings.xml
@@ -4,6 +4,7 @@
Nova conversaGerenciar contasGerenciar conta
+ Encerrar a conversaDetalhes do contatoDetalhes da conversa em grupoDetalhes do canal
@@ -752,6 +753,7 @@
Chamadas em andamentoSilenciar mensagensEssa categoria de notificação é utilizada para exibir notificações que não deveriam gerar nenhum som. Por exemplo, quando estiver ativo em outro dispositivo (Período de Espera).
+ Entregas não efetuadasConfigurações das notificações de mensagensconfigurações das notificações de chamadas recebidasImportância, som, vibração.
@@ -919,10 +921,30 @@
Você só pode ter uma chamada de cada vezRetornar para a chamada em andamentoNão foi possível trocar a câmera
+ Fixar no topo
+ Desafixar do topoTrilha GPXNão foi possível corrigir a mensagem
+ Todas as conversas
+ Essa conversa
+ Seu avatar
+ Avatar para %s
+ Criptografada com OMEMO
+ Criptografada com OpenPGP
+ Não criptografada
+ Sair
+ Gravar mensagem de voz
+ Tocar áudio
+ Pausar áudio
+ Adicionar contato, criar ou associar-se a uma conversa em grupo ou descobrir canaisVer %1$d participanteVer %1$d participantes
+
+ Não foi possível enviar a mensagem
+ Não foi possível enviar algumas mensagens
+
+ Entregas não efetuadas
+ Mais opções
diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml
index e17bc8fff..1022035f1 100644
--- a/src/main/res/values-ro-rRO/strings.xml
+++ b/src/main/res/values-ro-rRO/strings.xml
@@ -957,4 +957,8 @@
Trimiteri eșuateMai multe
+ Nu s-a găsit nici o aplicație
+ Invitați la Conversations
+ Nu s-a putut procesa invitația
+ Serverul nu suportă generarea de invitații
diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml
index 0942bbae8..19f9eb716 100644
--- a/src/main/res/values-ru/strings.xml
+++ b/src/main/res/values-ru/strings.xml
@@ -4,6 +4,7 @@
Новая беседаУправление аккаунтамиУправление аккаунтом
+ Закрыть беседуСведения о контактеПодробности конференцииСведения о канале
@@ -296,8 +297,8 @@
размещено на %sПроверка %s на сервере HTTPВы неподключены. Попробуйте позже
- Проверить размер %s
- Проверить размер %1$s на %2$s
+ Проверить размер (%s)
+ Проверить размер на %2$s (%1$s)Опции сообщенияЦитироватьВставить как цитату
@@ -764,6 +765,7 @@
Активные вызовыТихие сообщенияЭта группа уведомлений используется для отображения беззвучных оповещений. Например, при активности на другом устройстве (Грейс-период).
+ Недоставленные сообщенияНастройки уведомлений о сообщенияхНастройки уведомлений о входящих вызовахПриоритет, звук, вибрация
@@ -931,10 +933,35 @@
Нельзя одновременно совершать больше одного звонка.Вернуться к текущему звонкуНе удалось переключить камеру
+ Прикрепить
+ Открепить
+ GPX-трек
+ Не удалось исправить сообщение
+ Все беседы
+ Эта беседа
+ Ваш аватар
+ Аватар для %s
+ Зашифровано с помощью OMEMO
+ Зашифровано с помощью OpenPGP
+ Не зашифровано
+ Выйти
+ Записать голосовое сообщение
+ Воспроизвести аудио
+ Остановить воспроизведение
+ Добавить контакт, создать или присоединиться к конференции, или найти каналыПросмотр %1$d участникаПросмотр %1$d участниковПросмотр %1$d участниковПросмотр %1$d участников
+
+ Не удалось доставить сообщение
+ Не удалось доставить сообщения
+ Не удалось доставить сообщения
+ Не удалось доставить некоторые сообщения
+
+ Недоставленные сообщения
+ Ещё
+ Не найдено приложения
diff --git a/src/main/res/values-tr-rTR/strings.xml b/src/main/res/values-tr-rTR/strings.xml
index d23a9f999..83eb48990 100644
--- a/src/main/res/values-tr-rTR/strings.xml
+++ b/src/main/res/values-tr-rTR/strings.xml
@@ -4,6 +4,7 @@
Yeni konuşmaHesapları yönetHesabı yönet
+ Konuşmayı kapatKişi bilgileriKüme konuşması ayrıntılarıKanal ayrıntıları
@@ -33,22 +34,26 @@
İleti deşifre ediliyor. Lütfen bekleyin…OpenPGP şifreli iletiRumuz kullanılıyor
+ Geçersiz rumuzYöneticiSahipModeratörKatılımcıZiyaretçi
+ %s adlı kişiyi listenizden çıkarmak ister misiniz? Bu kişi ile olan konuşmalar silinmeyecektir.%s kişisinin size ileti göndermesini engellemek istiyor musunuz?% kişisinin size ileti göndermesine koyduğunuz engellemeyi kaldırmak ve size ileti göndermesine izin vermek istiyor musunuz?%s üzerinden gelen tüm kişileri engellemek istiyor musunuz? %s üzerinden gelen kişilerdeki engellemeyi kaldırmak istiyor musunuz?Kişi engellendiEngellendi
+ %s kişisini yer imlerinden çıkarmak ister misiniz? Bu yer imi ile kayıtlı konuşmalar silinmeyecektir.Sunucuda yeni bir hesap oluşturSunucudaki şifreni değiştirPaylaş...Konuşma başlatKişi davet et
+ Davet et KişilerKişiİptal et
@@ -61,30 +66,46 @@
KaydetTamamConversations çöktü
+ XMPP hesabınız ile yığın izleri göndermeniz Conversations\'ın devam eden gelişimine yardımcı oluyor.Şimdi gönderBir daha sorma
+ Hesaba bağlanılamadı
+ Birden fazla hesaba bağlanılamadı.
+ Hesaplarınızı yönetmek için dokununuz.Dosya ekle
+ Eksik olan bu kişiyi listenize eklemek ister misiniz?Kişi ekleulaştırılamadı
+ Görüntü gönderilmeye hazırlanılıyor.
+ Görüntüler gönderilmeye hazırlanılıyor.Dosyalar Paylaşılıyor. Lütfen bekleyin...Geçmişi silKonuşma geçmişini sil
+ Bu konuşmadaki tüm mesajları silmek istiyor musunuz? \n\nUyarı: Bu eylem, diğer aygıt ve sunucularda kayıtlı mesajları etkilemeyecektir.
+ Dosyayı sil
+ Bu dosyayı silmek istediğinizden emin misiniz? \n\nUyarı: Bu eylem, bu dosyanın diğer aygıt ve sunucularda kayıtlı kopyalarını silmeyecektir.
+ Devamında bu konuşmayı kapatAygıt seçŞifrelenmemiş ileti gönder
+ Mesajı gönder%s kişisine ileti gönderOMEMO ile şifrelenmiş ileti gönderv\\OMEMO ile şifrelenmiş ileti gönderOpenPGP ile şifrelenmiş ileti gönder
+ Yeni rumuz kullanımdaŞifrelenmemiş gönderDeşifre edilemedi. Uygun bir özel anahtarınız olmayabilir.OpenKeychain
+ Conversations mesajları şifrelemek ve çözmek ve genel anahtarlarınızı yönetmek için OpenKeychain kullanmaktadır. \n\nlt GPLv3+ altında lisanslıdır ve F-Droid ve Google Play\'de mevcuttur. \n\n(Lütfen devamında Conversations\'ı yeniden başlatın.)Yeniden başlatKurLütfen OpenKeychain’i kursunuluyor…bekliyor…Herhangi bir OpenPGP anahtarı bulunamadı
+ Bağlantınız genel anahtarını açıklamadığı için bağlanılamadı.\n\nLütfen bağlantızdan OpenPGP kurmasını isteyin.Herhangi bir OpenPGP anahtarı bulunamadı
+ Bağlantılarınız genel anahtarılarını açıklamadığı için bağlanılamadı.\n\nLütfen bağlantılarınızdan OpenPGP kurmaslarını isteyin.GenelDosyaları kabul et…‘den küçük olan dosyaları kendiliğinden kabul et
@@ -94,14 +115,23 @@
Yeni ileti geldiğinde titretLED BildirimiYeni bir ileti geldiğinde bildirim ışığı yanıp sönsün
+ Zil sesi
+ Bildirim sesi
+ Yeni mesajlar için bildirim sesi
+ Gelen çağrılar için zil sesiMühlet
+ Cihazlarınızın birinde faaliyet tespit edilmesinden sonra zaman hatırlatmalarının susturulma uzunluğu.GelişmişAsla çöküş raporu gönderme
+ Yığın izi göndererek gelişime yardımcı oluyorsunuz.İletileri onaylaOnların iletilerini aldığınızda ve okuduğunuzda, kişilerinizin bunu bilmesini sağlayınArabirim
+ OpenKeychain bir hata verdi.
+ Kötü anahar şifrelemesi.Kabul etBir hata oluştu
+ HataHesabınızÇevrim içi durum bildirimi gönderÇevrim içi durum bildirimi al
@@ -110,8 +140,10 @@
Resim çekAbonelik isteğini peşinen kabul etSeçtiğiniz dosya bir görüntü dosyası değil
+ Görüntü dosyası dönüştürülemedi.Dosya bulunamadıGenel G/Ç hatası. Depolama yeri kalmamış olabilir mi?
+ 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.BilinmeyenGeçici olarak devre dışıÇevrim içi
@@ -123,9 +155,13 @@
Hesap oluşturulamadıKullanıcı adı kullanılıyorHesap oluşturuldu
+ Hesap, sunucu tarafından desteklenmiyor.
+ Geçersiz hesak simgesi
+ TLS uzlaşması başarısızPolitika ihlaliSunucu uyuşmazlığıAkış hatası
+ Akış açılım hatasıŞifrelenmemişOTROpenPGP
@@ -134,11 +170,19 @@
Geçici olarak devre dışı bırakAvatar yayımlaOpenPGP genel anahtarını yayımla
+ OpenPGP genel anahtarını kaldır
+ OpenPGP genel anahtarınız Çevrim içi durum anonsunuzdan kaldırmak istediğinizden emin misiniz?\nArtık kişileriniz size şifrelenmiş OpenPGP mesajları gönderemeyecek.
+ OpenPGP genel anahtarı yayınlandı.Hesabı etkinleştirEmin misiniz?
+ Hesabınızın silinmesi bütün konuşma geçmişinizi silerSes kaydet
+ XMPP adresi
+ XMPP adresini engellekullanıcıadı@ornek.comparola
+ Bu geçerli bir XMPP adresi değil
+ Yetersiz bellek. Görüntü çok büyük.%s kişisini listenize eklemek ister misiniz?Sunucu bilgisiXEP-0313: MAM
@@ -147,6 +191,7 @@
XEP-0191: Blocking CommandXEP-0237: Roster VersioningXEP-0198: Stream Management
+ XEP-0215: Dış Hizmet KeşfiXEP-0163: PEP (Avatars / OMEMO)XEP-0363: HTTP File UploadXEP-0357: Push
@@ -154,9 +199,14 @@
mevcut değilKayıp genel anahtar bildirimlerien son şimdi görüldü
+ en son bir dakika önce görüldüen son %d dakika önce görüldü
+ en son bir saat önce görüldüen son %d saat önce görüldü
+ en son bir saat önce görüldüen son %d gün önce görüldü
+ Şifrelenmiş mesaj. Çözmek için lütfen OpenKeychain yükleyin.
+ Yeni şifrelenmiş OpenPGP mesajları bulunduOpenPGP Anahtar IDOMEMO parmak iziv\\OMEMO parmak izi
@@ -167,8 +217,10 @@
Anahtarları alıyor…TamamŞifreyi çöz
+ Yer imleriAraKişi Girin
+ Kişiyi silKişi bilgilerini görüntüleKişiyi engelleKişiyi engellemeyi kaldır
@@ -176,57 +228,103 @@
SeçKişi zaten mevcutKatıl
+ channel@cnoference.example.com/nick
+ channel@conference.example.comYer imi olarak kaydetYer imini sil
+ Grup konuşmasını yoket
+ Kanalı yoket
+ Bu grup konuşmasını yoketmek istediğinizden emin misiniz?\n\nUyarı: Grup konuşması sunucudan tamamen kaldırlacaktır.
+ Bu genel kanalı yoketmek istediğnizden emin misiniz?\n\nUyarı: Kanal sunucudan tamamen kaldırılacaktır.
+ Grup konuşması yokedilemedi
+ Kanal yokedilemedi
+ Grup konuşma konusunu düzenle
+ Başlık
+ Grup konuşmasına bağlanılıyor...AyrılKişi sizi listesine eklediSiz de ekleyin%s buraya kadar okudu
+ %sburaya kadar okudu
+ %1$s+%2$dkişi buraya kadar okudu
+ Buraya kadar herkes okuduYayımla
+ Galeriden resim seçmek için avatara dokununYayımlanıyor…Sunucu yayımladığınızı reddetti
+ Resminiz dönüştürülemedivatar diske kaydedilemedi(Veya varsayılan değerlere dönmek için uzun süre basılı tutun)
+ Sunucunuz avatarların tanıtılmasını desteklemiyor.fısıldandı%s kişisine%s kişisine özel ileti gönderBağlanBu hesap zaten mevcutSonraki
+ Oturum oluşturulduAtlaBildirimleri kapatEtkinleştir
+ Grup konuşması şifre talep ediyorParolayı gir
+ Lütfen bağlantınızdan önce çevrim içi durum bildirimi talep edin. \n\nBu, bağlantınızın hangi konuşma uygulamasını kullandığını belirlemekte kullanılacak. Şimdi isteYok say
+ Uyarı: Bu mesajı karşılıklı çevrim içi durum bildirimleri olmadan göndermek, beklenmeyen problemlere neden olabilir. \n\n Çevrim içi durum aboneliklerini doğrulamak için \"Kişi Bilgileri\" kısmına gidin.Güvenlikİleti düzeltmeye izin verKişilerinizin geçmiş iletilerini düzeltmelerine izin verUzman seçenekleriLütfen dikkatli olun
+ %sHakkındaSessiz SaatlerBaşlangıç zamanıBitiş zamanıSessiz saatleri etkinleştirBildirimler sessiz saatler boyunca sessize alınacaktırDiğer
+ Yer imleri ile senkronize et.
+ Yer imleri öyle belirtmişse grup konuşmalarına otomatik olarak katıl.
+ OMEMO parmak izi panoya kopyalandı
+ Bu grup konuşmasından menedildiniz
+ Bu grup konuşması yalnızca üyeleri içindir
+ Kaynak sınırlaması
+ Bu grup konuşmasından atıldınız
+ Grup konuşması kapatıldı
+ Artık bu grup konuşmasında değilsiniz%s hesabını kullanarak
+ %sev sahipliğindeHTTP sunucusundaki %s denetleniyorBağlı değilsiniz. Daha sonra yeniden deneyin%s boyutunu denetle%2$s üzerindeki %1$s boyutunu denetleİleti seçenekleriAlıntı
+ Alıntı olarak yapıştırOrijinal URL\'i kopyalaYeniden gönderDosya URL
+ URL panoya kopyalandı
+ XMPP adresi panoya kopyalandı
+ Hata mesajı panoya kopyalandı
+ web adresi2B Barkod Tara2B Barkod GösterEngellenenler listesini gösterHesap bilgileriDoğrulaYeniden deneyin
+ Ön planda çalışmasıİşletim sisteminin bağlantınızı koparmasına engel olur
+ Yedek oluştur
+ Yedekleme dosyaları %s\'da depolanacak
+ Yedekleme dosyaları oluşturuluyor
+ Yedeklemeniz oluşturuldu
+ Yedekleme dosyaları %s\'da depolandı
+ Yedekleme yükleniyor
+ Yedekleminiz yüklendi
+ Hesabı etkinleştirmeyi unutmayınDosya seç%1$s alıyor/(%2$d%% tamamlandı)%s indir
@@ -234,14 +332,28 @@
dosya%s açgönderiyor (%1$d%% tamamlandı)
+ Dosya paylaşılmaya hazırlanılıyor%s indirme işlemi için sunulduAktarmayı iptal et
+ Dosya paylaşılanamadı
+ Dosya aktarma iptal edildi
+ Dosya silindi
+ Dosyayı açacak bir uygulama bulunamadı
+ Linki açacak bir uygulama bulunamadı
+ Kişiyi görüntüleyecek bir uygulama bulunamadı
+ Dinamik etiketlerKişilerin görünmeyen salt okunur etiketlerini gösterBildirimleri etkinleştir
+ Grup konuşma sunucusu bulunamadı
+ Grup konuşması oluşturulamadıHesap avatarıOMEMO parmak izini panoya kopyalaOMEMO anahtarını yeniden oluşturAygıtları sil
+ OMEMO bildirimindeki diğer aygıtların hepsini silmek istediğinizden emin misiniz? Aygıtlarınız yeniden bağlandıklarında kendilerini yeniden bildirecekler ama bu süre zarfındaki iletileri alamayabilirler.
+ Bu kişi için kullanılabilecek bir anahtar bulunmuyor.\nSunucudan yeni anahtarlar alınamıyor. Belki bağlantınızın sunucusunda bir sorun vardır?
+ Bu kişi için kullanılabilir bir anahtar yok.\İkinizin de çevrim içi durum aboneliği oldudğundan emin olun.
+ Bir şeyler ters gittiSunucudan geçmiş alınıyorSunucuda başka geçmiş kalmadıGüncelleniyor…
@@ -250,6 +362,7 @@
Parolayı değiştirinMevcut parolaYeni parola
+ Şifre boş olamazTüm hesapları etkinleştirTüm hesapları devre dışı bırakKullanarak tamamla
@@ -258,17 +371,36 @@
BağlantısızÜyeGelişmiş kip
+ Üye imtiyazları tanı
+ Üye imtiyazlarını kaldırYönetici imtiyazlarını kabul etYönetici imtiyazlarını geri çevir
+ Sahip imtiyazları tanı
+ Sahip imtiyazlarını kaldır
+ Grup konuşmasından çıkar
+ Kanaldan çıkar%s kişisinin ortaklığı değiştirilemedi
+ Grup konuşmasından men et
+ Kanaldan men et
+ %s kişisini ortak kanaldan çıkarmaya çalışıyorsunuz. Bunu yapmanın tek yolu kişiyi tamamen men etmektir.Şimdi men et%s kişisinin rolü değiştirilemedi
+ Özel grup konuşması ayarları
+ Ortak kanal ayarları Özel, yalnızca üyeler
+ XMPP adreslerini herkes için görünür yap
+ Kanalı denetimli hale getirKatılımcı değilsiniz
+ Düzenlenmiş grup konuşması seçenekleri!
+ Grup konuşma seçenekleri düzenlenemediHiçbir zamanİkinci bildirime kadar
+ Ertele
+ Cevapla
+ Okundu olarak işaretleGirdiEnter=gönder
+ Mesaj yollamak için Enter tuşunu kullanın. Bu seçenek devre dışı olsa da her zaman Ctrl+Enter ile mesaj yollayabilirsiniz.\"Enter\" tuşunu gösterİfade simgesi tuşunu \"enter\" tuşu olarak değiştirses
@@ -289,8 +421,11 @@
Siz onlara ileti yazarken kişilerinizin bunu bilmesini sağlayınYer bildirimi gönderYer bildirimi göster
+ Konum göstermek için uygulama bulunamadıYerKonuşma sonlandı
+ Özel grup konuşmasından ayrıldı
+ Ortak kanaldan ayrıldıSistem sertifikalarına güvenmeyinTüm sertifikalar bizzat onaylanmalıdırSertifikaları kaldır
@@ -303,11 +438,15 @@
%d sertifikaları silindi%d sertifikaları silindi
+ \"Gönder\" tuşunu kısayol ile değiştirKısayolHiçbiriEn son kullanılanlarKısayolu seç
+ Kişilerde ara
+ Yer imleri araÖzel ileti gönder
+ %1$s grup konuşmasından ayrıldıKullanıcı adıKullanıcı adıKullanıcı adı geçerli değil
@@ -317,16 +456,28 @@
İndirme başarısız: Dosya yazılamıyorTor ağına erişilemiyorBağlantı başarısız
+ Sunucu bu alan adı için sorumlu değilBozuk
+ MevcudiyetEkran kapandığında uzakta
+ Ekran kapalı olduğund Uzakta olarak göster
+ Müsait değilken sessiz kipte olur
+ Telefonunuz sessizdeyken, durum bildiriminizi müsait değil olarak gösterir.Titreşim kipini sessiz kip olarak değerlendir
+ Telefonunuz titreşimdeyken, durum bildiriminizi müsait değil olarak gösterir.Genişletilmiş bağlantı seçenekleriHesap oluştururken sunucu adıyla bağlantı noktası seçeneğini gösterxmpp.ornek.com
+ Sertifika ile giriş yap
+ Sertifika çözümlenemediArşivleme tercihleriSunucu tarafı arşivleme tercihleriArşivleme tercihleri alınıyor. Lütfen bekleyin...
+ Arşivleme tercihleri alınamadı
+ CAPTCHA gerekliResimdeki yazıyı girin
+ Güvenilmeyen sertifika zinciri
+ XMPP adresi sertifika ile uyuşmuyorSertifikayı yenileOMEMO anahtarı alınırken hata oluştu!Sertifikalı OMEMO anahtarı onaylandı!
@@ -336,6 +487,7 @@
Tüm bağlantıları Tor ağı üzerinden aktar. Orbot gerekir.Sunucu adıBağlantı noktası
+ Sunucu- veya .onion- adresiBu bağlantı noktası geçerli değilBu sunucu geçerli değil %2$d hesabın %1$d kadarı bağlandı
@@ -344,23 +496,69 @@
%d iletiDaha fazla ileti yükle
+ Dosya %s ile paylaşıldı
+ Resim %sile paylaşıldı
+ Resimler %s ile paylaşıldı
+ Metin %s ile paylaşıldı
+ Conversations\'ın haricii depolama erişimine izin ver
+ Conversations\'ın kamera erişimine izin verKişilerle senkronize et
+ Conversations XMPP listenizi kişi listesi ile eşleştirmek ve kişileri tam isimleri ve avatarları ile görüntülemek için listenize erişim izni istiyor. \n\nlt kişilerinizi sadece yerel olarak okuyup eşleştirmekte olup sunucunuza yüklemeyecektir.
+ Bu telefon numaralarının herhangi bir kopyasını depolamayacağız. \n\nDaha fazla bilgi için gizlilik sözleşmemizi okuyun.
Şimdi kişilerinize ulaşılması için sizden izin istenecek.
+ Tüm iletilerde uyar
+ Yalnızca bahsedilğinde haber ver
+ Uyarılar devre dışı
+ Uyarılar geçici olarak durduruldu
+ Görüntü sıkıştırma
+ İpucu: ayarladan bağımsız olarak sıkışktırılmamış resimleri ayrı ayrı göndermek için \'Resim seç\' yerine \'Dosya seç\' tuşunu kullanın.
+ Her zaman
+ Sadece büyük resimler
+ Pil optimizasyonu devrede
+ Telefonunuz Conversations için ağır pil optimizasyonları yürütüyor. Bu gecikmiş bildirimlere ve hatta mesaj kaybına neden olabilir.\nlt Optimizasyonların devre dış bırakılması tavsiye edilir.
+ Telefonunuz Conversations için ağır pil optimizasyonları yürütüyor. Bu gecikmiş bildirimlere ve hatta mesaj kaybına neden olabilir.\n\nSizden şimdi onları devre dışı bırakmanız istenilecek.
+ Devre dışı
+ Seçilen alan çok büyük
+ (Etkin hesap bulunmuyor)
+ Bu alan zorunludur
+ ileti düzelt
+ Düzeltilmiş iletiyi gönder
+ Bu kişinin güvenini doğrulamak için parmak izini zaten güvenle onayladınız. \"Tamam\"ı seçerek sadece%s kişisinin, grup konuşmasının bir parçası olduğunu doğruluyorsunuz.
+ Bu hesabı devre dışı bıraktınız
+ Güvenlik hatası: Geçersiz dosya erişimi!
+ URL paylaşacak uygulama bulunamadı
+ URI paylaş ile...
+ Telefon numarası ile kayıt olursunuz ve Quicksy -adres defterinizdeki numaraları baz alarak- otomatik olarak size olası yeni kişiler önerir.
Kayıt olarak gizlilik sözleşmemizi onaylamış oluyorsunuz.]]>Tüm iletilerde uyar
+ Yalnızca bahsedilğinde haber verUyarılar devre dışıUyarılar geçici olarak durduruldu
+ Görüntü sıkıştırma
+ İpucu: ayarladan bağımsız olarak sıkışktırılmamış resimleri ayrı ayrı göndermek için \'Resim seç\' yerine \'Dosya seç\' tuşunu kullanın.Her zaman
+ Sadece büyük resimlerPil optimizasyonu devrede
+ Telefonunuz Conversations için ağır pil optimizasyonları yürütüyor. Bu gecikmiş bildirimlere ve hatta mesaj kaybına neden olabilir.\nlt Optimizasyonların devre dış bırakılması tavsiye edilir.
+ Telefonunuz Conversations için ağır pil optimizasyonları yürütüyor. Bu gecikmiş bildirimlere ve hatta mesaj kaybına neden olabilir.\n\nSizden şimdi onları devre dışı bırakmanız istenilecek.Devre dışıSeçilen alan çok büyük(Etkin hesap bulunmuyor)Bu alan zorunludurileti düzeltDüzeltilmiş iletiyi gönder
+ Bu kişinin güvenini doğrulamak için parmak izini zaten güvenle onayladınız. \"Tamam\"ı seçerek sadece%s kişisinin, grup konuşmasının bir parçası olduğunu doğruluyorsunuz.Bu hesabı devre dışı bıraktınız
+ Güvenlik hatası: Geçersiz dosya erişimi!
+ URL paylaşacak uygulama bulunamadıURI paylaş ile...
+ Telefon numarası ile kayıt olursunuz ve Quicksy -adres defterinizdeki numaraları baz alarak- otomatik olarak size olası yeni kişiler önerir.
Kayıt olarak gizlilik sözleşmemizi onaylamış oluyorsunuz.]]>
+ Kabul et ve devam et
+ Conversations\'da hesap kurulum için bir rehber hazırlanmıştır.¹\nConversations.im\'i bir sağlayıcı olarak seçtikten sonra başka sağlayıcılar kullanan kullanıcılarla onlara tam XMPP adresinizi vererek iletişim kurabilirsiniz.
+ Tam XMPP adresiniz %s olacakHesap OluşturKendi sağlayıcımı kullanKullanıcı adınızı seçin
+ Mevcudiyet durumunu elle yönet
+ Durumunuzu düzenlerken mevcudiyetinizi kaydedinDurum iletisiKonuşmak için uygunÇevrim içi
@@ -372,16 +570,23 @@
Hesap oluşturulamadı: Sonra yeniden deneyinKayıt Başarısız: Parola çok zayıfKatılımcıları seç
+ Grup konuşması oluşturuluyor...Yeniden davet et
+ Devre dışı bırakKısaOrtaUzun
+ Kullanımı yayınla
+ Kişiler Conversations\'ı kullandığınız zaman bundan haberdar olur.GizlilikGövdeRenk paletini seçin
- Açık gövde
- Koyu gövde
- OpenKeychain\'e bağlanılamıyor
+ Otomatik
+ Aydınlık
+ Karanlık
+ Yeşil arka plan
+ Gelen iletiler için yeşil arka plan kullan
+ OpenKeychain\'e bağlanılamadıBu aygıt artık kullanılmıyorBilgisayarMobil telefon
@@ -389,20 +594,29 @@
Web tarayıcıKonsolÖdeme gerekli
+ Internet kullanıma izin verBenKişi sizi eklemek istiyorİzin ver%s e erişim hakkınız yokUzak sunucu bulunamadı
+ Uzak sunucu zaman aşımına uğradı
+ Hesap güncellenemedi
+ Bu XMPP adresini spam mesajlardan dolayı rapor etOMEMO kimliğini sil
+ OMEMO anahtarını yeniden oluştur. Tüm kişilerinizin sizi yeniden doğrulaması gerekecek. Bunu yalnızca son çare olarak kullanın.Seçilen anahtarları silAvatarınızı yayımlamak için bağlı olmalısınız.Hata iletisini gösterHata İletisiVeri tasarrufu etkin
+ İşletim sisteminiz Conversations arka planda çalışırken İnternet erişimini sınırlıyor. Yeni ileti alındığında uyarı gelmesi için Conversations\'a veri tasarrufu etkin durumdayken sınırsız erişim vermeniz gerekiyor.\nConversations mümkün olan durumlarda veri tasarrufu için çaba harcar.Aygıtınız Conversations için Veri tasarrufunu devre dışı bırakmayı desteklemiyor
+ Geçici dosya oluşturulamadıBu aygıt doğrulandıParmak izini kopyala
+ Sahip olduğunuz bütün OMEMO anahtarlarını doğruladınız
+ Barkod bu konuşmanın parmak izlerlerini içermiyorDoğrulanmış parmak izleriBir kişinin barkodunu taramak için kamerayı kullanAnahtarların alınması için lütfen bekle
@@ -410,21 +624,354 @@
XMPP URI\'si olarak paylaşHTTP bağlantısı olarak paylaşDoğrulamadan Önce Kör Güven
+ Doğrulanmamış kişilerin tüm yani aygıtlarına güven, ama doğrulanmış kişilerden aygıtlarını onaylamalarını iste.
+ OMEMO anahtarlarına körlemisne güvenildi, yani bu kişi bir başkası olabilir veya biri konuşmayı dinleyebilir.GüvenilmeyenGeçersiz 2D barkod
+ Önbellek dizinini temizle (Kamera uygulamasının kullandığı)Önbelleği temizleÖzel depolama alanını temizleDosyaların tutulduğu özel depolama alanını temizle (Sunucu üzerinden tekrar indirilebilir)Bu bağlantıyı güvenilir bir kaynaktan takip ettimBir bağlantıyı tıkladıktan sonra %1$s in OMEMO anahtarını doğrulamış olacaksınız. Bu yalnızca bağlantının %2$s tarafından yayınladığından eminseniz güvenlidir.OMEMO anahtarlarını doğrula
+ Aktif olmayanları göster
+ Aktif olmayanları saklaGüvensiz aygıt
+ Bu cihazın doğrulamasını kaldırmak istediğinizden emin misiniz? \ Bu cihaz ve cihazdan gelen mesajlar güvenilmez olarak işaretlenecektir.
+
+ %d saniye
+ %d saniye
+
+
+ %d dakika
+ %d dakika
+
+
+ %d saat
+ %d saat
+
+
+ %d gün
+ %d gün
+
+
+ %d hafta
+ %d hafta
+
+
+ %d ay
+ %d ay
+ Otomatik ileti silmekBu cihazdan, ayarlanmış zaman aralığından eski olan mesajları otomatik olarak silin.İletiyi şifrelemekYerel saklama süresi nedeniyle ileti getirilmiyor.
+ Video sıkıştırılıyorKonuşma sonlandıKişi engellendi.Yabancılardan bildirimler
+ Yabancılardan alınan ileti ve aramaları bildir.Yabancıdan alınmış ileti
+ Yabancıyı blokla
+ Bütün alan adını engelle
+ Şu anda çevrimiçi
+ Tekrar şifrelemeyi dene
+ Oturum başarısız
+ SASL mekanizması downgrade edildi
+ Sunucu web sitesinden oturum açılmasını istiyor
+ Açık web sitesi
+ Web sitesini aç
+ Uyarı bildirimleri
+ Uyarı bildirimlerini göster
+ Bugün
+ Dün
+ Sunucu adını DNSSEC ile doğrula
+ Doğrulanmış sunucu adına sahip sunucu sertifikaları onaylanmış kabul edilir
+ Sertifika bir XMPP adresi içermiyor
+ kısmi
+ Video kaydet
+ Panoya kopyala
+ İleti panoya kopyalandı
+ İleti
+ Özel iletiler devre dışı bırakıldı
+ Korunan uygulamalar
+ Ekranınız kapalıyken bile bildirim almak için Conversations\'ı korunan uygulamalara eklemelisiniz.
+ Bilinmeyen sertifikayı kabul et?
+ Sunucu sertifikası bilinen bir Sertifika Yetkilisi tarafından imzalanmamış.
+ Uyuşmayan Sunucu isimlerini kabul et?
+ Sunucu \"%s\" olarak doğrulayamad. Bu sertifika sadece bunun için geçerlidir:
+ Yine de bağlanmak istiyor musunuz?
+ Sertifika detayları:
+ Bir kez
+ QR kod tarayıcısın kameraya erişmesi gerek
+ En alta in
+ İleti gönderdikten sonra alta in
+ Durum mesajını düzenle
+ Durum mesajını düzenle
+ Şifrelemeyi devre dışı bırak
+ Conversations %1$skişisine şifrelenmiş ileti gönderemiyor. Bunun sebebi, kişinizin tarihi geçmiş bir sunucu veya OMEMO çalıştıramayan bir istemci kullanıyor olması olabilir.
+ Aygıt listesi alınamadı
+ Şifreleme anahtarları alınamadı
+ İpucu: Kimi durumlarda bu sorun, birbirinizi kişi listenize eklemenizle çözülebilir.
+ Bu konuşma için OMEMO şifrelemesini devre dışı bırakmak istediğinizden emin misiniz?\nBu, sunucu yöneticinizin mesajlarınızı okumasını mümkün kılsa da, tarihi geçmiş istemcileri kullanan insanlarla iletişim kurmanın tek yolu olabilir.
+ Şimdi devre dışı bırak
+ Taslak:
+ OMEMO Şifrelemesi
+ Bire bir ve grup konuşmalarında her zaman OMEMO kullanılacak.
+ Yeni konuşmalarda OMEMO varsayılan olarak kullanılacak.
+ Özellikle yeni konuşmalarda OMEMO aktif hale getirilecek.
+ Kısayol oluştur
+ Yazı tipi boyutu
+ Uygulamaya kıyasla kullanılan yazı tipi boyutu.
+ Varsayılan olarak aktif
+ Varsayılan olarak devre dışı
+ Küçük
+ Orta
+ Büyük
+ İleti bu cihaz için şifrelenmedi
+ OMEMO mesajı çözümlenemedi
+ geri al
+ Konum paylaşımı devre dışı bırakıldı
+ Konumu sabitle
+ Konumu ayır
+ Konumu Kopyala
+ Konumu paylaş
+ Yol tarifi
+ Konumu paylaş
+ Konumu göster
+ Paylaş
+ Kayıt başlatılamadı
+ Lütfen bekleyin...
+ Conversations\'ın mikrofon erişimine izin ver
+ İleti ara
+ GIF
+ Konuşma görüntüle
+ Konum Eklentisini Paylaş
+ Varolan harita yerine Konum Eklentisini Paylaş\'ı kullan
+ Web adresini kopyala
+ XMPP adresini kopyala
+ S3 için HTTP Dosya Paylaşımı
+ Doğrudan arama
+ \'Konuşma Başlat\" ekranında klavyeyi aç ve arama kısmına imleci getir
+ Grup konuşması avatarı
+ Yönetici grup konuşması avatarlarını desteklemiyor
+ Yalnızca yönetici grup konuşması avatarını değiştirebilir
+ Kişi ismi
+ Rumuz
+ İsim
+ İsim vermek isteğe bağlıdır
+ Grup konuşması ismi
+ Bu grup konuşması yokedildi
+ Kayıt kaydedilemedi
+ Ön planda çalışması
+ Bu bildirim kategorisi Conversations\'ın çalıştığını sürekli belirtmekte kullanmaktadır.
+ Durum bilgisi
+ Bağlantı Sorunları
+ Bu bildirim kategorisi, bir hesaba bağlanmakta sorun olduğunu belirtmekte kullanılır
+ İletiler
+ Aramalar
+ İletiler
+ Gelen aramalar
+ Yapılan aramalar
+ Sessiz iletiler
+ Bu bildirim grubu, bildirimlerin herhangi bir ses çıkarmaması gerektiğini belirtmekte kullanılır. Mesela başka bir cihazda aktif olunduğunda (Mühlet)
+ Başarısız gönderiler
+ İleti bildirim ayarları
+ Gelen arama bildirimleri ayarları
+ Önem, Ses, Titreş
+ Video sıkıştırma
+ Medyayı görüntüle
+ Katılımcılar
+ Medya tarayıcısı
+ Dosya güvenlik ihlalinden dolayı dahil edilmedi
+ Video kalitesi
+ Daha düşük kalite, daha ufak dosya anlamına gelir
+ Orta (360P)
+ Yüksek (720p)
+ iptal edildi
+ Zaten taslak halinde bir iletiniz var
+ Özellik uygulanmadı
+ Geçersiz ülke kodu
+ Bir ülke seçin
+ telefon numarası
+ Telefon numaranızı doğrulayın
+ Quicksy, telefon numaranızı doğrulamak için bir SMS iletisi yollayacak (operatör fiyatlandırmaları uygulanabilir). Ülke kodunuzu ve telefon numaranızı giriniz:
+ %s
telefon numarasını doğrulayacağız. Bu numara doğru mudur, yoksa numarayı değiştirmek mi istersiniz?]]>
+ %s geçerli bir telefon numarası değil.
+ Lütfen telefon numaranızı girin.
+ Ülkelerde ara
+ %s doğrula
+ %s telefonunuza bir SMS gönderdik.]]>
+ Size 6 haneli kodun olduğu başka bir SMS gönderdik.
+ Lüfen aşağıya 6 haneli kodu girin.
+ Tekrar sms gönder
+ Tekrar sms gönder (%s)
+ Lütfen bekleyin (%s)
+ geri
+ Olası kod, otomatik olarak panodan yapıştırıldı
+ Lütfen 6 haneli kodu girin.
+ Kayıt sürecini iptal etmek istediğinizden emin misiniz?
+ Evet
+ Hayır
+ Doğrulanıyor...
+ SMS talep ediliyor...
+ Girdiğiniz kod yanlış
+ Girdiğiniz kodun tarihi geçmiş
+ Bilinmeyen ağ hatası
+ Sunucudan bilinmeyen cevap
+ Sunucuya bağlanılamadı
+ Güvenli bağlantı oluşturulamadı
+ Sunucu bulunamadı
+ Talebinizin işlenmesinde bir hata meydana geldi
+ Geçersiz kullanıcı girişi
+ Geçici olarak ulaşılamıyor. Daha sonra tekrar deneyiniz.
+ Ağ bağlantısı yok
+ Lütfen %s içerisinde tekrar deneyiniz.
+ Sınırlandınız
+ Çok fazla girişim
+ Bu uygulamamının tarihi geçmiş bir versiyonunu kullanıyorsunuz.
+ Güncelle
+ Bu telefon numarası şu anda başka bir cihazla giriş yapmış durumda.
+ Adres defterlerinde sizin kim olduğunuzu bilmeyen kişilerin sizi tanıması için lütfen isminizi girin.
+ Adınız
+ Adınızı girin
+ Adınızı belirlemek için düzenle tuşunu kullanın
+ Talebi reddet
+ Orbot\'u yükle
+ Orbot\'u başlat
+ Herhangi bir mağaza uygulaması yüklenmedi
+ Bu kanal XMPP adresinizi herkese açık hale getirecek.
+ e-kitap
+ Orijinal (sıkıştırılmamış)
+ Şununla aç...
+ Conversations profil resmi
+ Hesap seç
+ Yedekleri yükle
+ Geri getir
+ Yedekleri geri getirmek için %s hesabının şifrenizi girin.
+ Herhangi bir yüklemeyi klonlama (aynı anda çalışan) girişiminde yedekleri geri yükleme özelliğini kullanmayın. Yedeklerin geri yüklenmesi sadece hesap aktarımları veya asıl cihazı kaybetmeniz durumu için kullanılmalıdır.
+ Yedekler geri yüklenemedi
+ Yedekleme çözülemedi. Şifre doğru mu?
+ Yedekleme & Geri yükle
+ XMPP adresini girin
+ Grup konuşması oluştur
+ Ortak kanala katıl
+ Özel grup konuşması oluştur
+ Ortak kanal oluştur
+ Kanal ismi
+ XMPP adresi
+ Lütfen kanal için bir isim belirleyin
+ Lütfen bir XMPP adresi sağlayın
+ Bu bir XMPP adresi. Lütfen bir isim belirleyin.
+ Ortak kanal oluşturuluyor...
+ Bu kanal zaten var
+ Varolan bir kanala katıldınız
+ Kanal ayarları kaydedilemedi
+ Herhangi birinin başlığı düzenlemesine izin ver
+ Herhangi birinin davet etmesine izin ver
+ Herhangi biri başlığı düzenleyebilir.
+ Yöneticiler başlığı düzenleyebilir
+ Yöneticiler başlığı düzenleyebilir
+ Yöneticiler başkalarını davet edebilir.
+ Herhangi biri başkalarını davet edebilir.
+ XMPP adresleri yöneticelere görünür.
+ XMPP adresleri herkese görünür.
+ Bu ortak kanalın herhangi bir katılımcısı yok. Kişilerinizi davat edin veya paylaş düğmesi ile XMPP adresini paylaşın.
+ Bu özel grup konuşmasının herhangi bir katılımcısı yok.
+ İmtiyazları düzenle
+ Katılımcı ara
+ Dosya çok büyük
+ Ekle
+ Kanal keşfet
+ Kanal ara
+ Olası gizlilik ihlali!
+ search.jabber.network isimli bir üçüncü parti hizmeti kullanıyor.
Bu özelliği kullandığınız zaman IP adresiniz ve arama tercihleriniz bu hizmete aktarılacak. Daha fazla bilgi için Gizlilik Politikasını inceleyiniz.]]>
+ Zaten bir hesabım var
+ Varolan bir hesap ekle
+ Yeni hesap oluştur
+ Bu bir alan adı adresine benziyor
+ Yine de ekle
+ Bu bir kanal adresine benziyor
+ Yedekleme dosyalarını paylaş
+ Conversations yedeklemesi
+ Faaliyet
+ Yedeklemeyi aç
+ Seçtioiniz dosya bir Conversations yedekleme dosyası değildir
+ Bu hesap zaten kurulu vaziyette
+ Lütfen bu hesabın şifresini girin
+ Eylem gerçekleştirilemedi
+ Ortak kanala katılınıyor
+ Paylaşımda bulunan uygulama dosya erişimi için yetki sağlamıyor
+
+ jabber.network
+ Yerel sunucu
+ XMPP ortamının genelinde daha iyi öneriler almak için çoğu kullanıcı \'jabber.network\'\'ü seçmelidir.
+ Kanal keşif yöntemi
+ Yedekle
+ Hakkında
+ Lütfen bir hesabı etkinleştirin
+ Arama yap
+ Gelen arama
+ Gelen görüntülü arama
+ Bağlanıyor
+ Bağlandı
+ Arama kabül ediliyor
+ Arama sonlandırılıyor
+ Cevapla
+ Reddet
+ Cihazlar keşfediliyor
+ Çaldırılıyor
+ Meşgul
+ Arama bağlanılamadı
+ Bağlantı kesildi
+ Geri çekilmiş arama
+ Uygulama hatası
+ Çağrıyı sonlandır
+ Devam eden arama
+ Deaam eden görüntülü arama
+ Arama yapmak için Tor\'u devre dışı bırak
+ Gelen arama
+ Gelen arama. %s
+ Yapılan arama
+ Yapılan arama. %s
+ Cevapsız arama
+ Sesli arama
+ Görüntülü arama
+ Yardım
+ Konuşmaya geç
+ Mikrofonunuza erişilemiyor
+ Bir seferde yalnızca tek bir aramanız olabilir.
+ Devam eden aramaya dön
+ Kamera açılamadı
+ En başa iliştir
+ En baştan kaldır
+ GPX izi
+ İleti düzeltilemedi
+ Bütün konuşmalar
+ Bu konuşma
+ Avatarınız
+ %s avatarı
+ OMEMO ile şifrelendi
+ OpenPGP ile şifrelendi
+ Şifrelenmedi
+ Çıkış
+ Sesli ileti kaydet
+ Sesi oynat
+ Sesi durdur
+ Kişi ekle, grup konuşması oluştur veya katıl, veya kanal keşfet
+
+ %1$d
+ %1$d Katılımcılarını görüntüle
+
+
+ aaa
+ Bağzı iletiler gönderilemedi
+
+ Başarısız gönderiler
+ Daha fazla seçenek
+ Herhangi bir uygulama bulunamadı
+ Conversations\'a davet et
+ Davet iletilemedi
+ Sunucu, davet oluşturulmasını desteklemiyor
diff --git a/src/main/res/values-w384dp/dimens.xml b/src/main/res/values-w384dp/dimens.xml
index 4f3550ab5..3c5d3c6f2 100644
--- a/src/main/res/values-w384dp/dimens.xml
+++ b/src/main/res/values-w384dp/dimens.xml
@@ -1,6 +1,10 @@
+
+ 12dp
+ 16dp
+
12sp288dp288dp
diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml
index 0e44707be..8b9b114aa 100644
--- a/src/main/res/values-zh-rCN/strings.xml
+++ b/src/main/res/values-zh-rCN/strings.xml
@@ -4,6 +4,7 @@
新对话管理账户管理账户
+ 关闭 Conversation联系人详情群聊详情频道详情
@@ -744,6 +745,7 @@
正在进行的通话无声消息此通知组用于显示不应触发任何声音的通知。 例如,当在另一个设备上激活时(宽限期)。
+ 发送失败消息通知设置来电通知设置重要程度、声音、振动
@@ -911,9 +913,32 @@
只能同时打一通电话返回正在进行的通话无法切换摄像头
+ 置顶
+ 取消置顶GPX轨迹无法更正消息
+ 所有会话
+ 该会话
+ 你的头像
+ %s的头像
+ 用OMEMO加密
+ 用OpenPGP加密
+ 未加密
+ 退出
+ 语音邮件录音
+ 播放音频
+ 暂停音频播放
+ 添加联系人、创建或加入群组会话,或发现频道查看%1$d成员
+
+ 一些消息无法发送
+
+ 发送失败
+ 更多选项
+ 没有找到应用程序
+ 邀请到 Conversations
+ 无法解析邀请
+ 服务器不支持生成邀请
diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml
index 069c19edc..fba306510 100644
--- a/src/main/res/values/dimens.xml
+++ b/src/main/res/values/dimens.xml
@@ -1,5 +1,7 @@
+ 8dp
+ 12dp8dp8dp16dp
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index a16bf406c..9f79f86a5 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -949,4 +949,8 @@
Failed deliveriesMore options
+ No application found
+ Invite to Conversations
+ Unable to parse invite
+ Server does not support generating invites
diff --git a/src/quicksy/java/eu/siacs/conversations/ui/EasyOnboardingInviteActivity.java b/src/quicksy/java/eu/siacs/conversations/ui/EasyOnboardingInviteActivity.java
new file mode 100644
index 000000000..e34a29a5b
--- /dev/null
+++ b/src/quicksy/java/eu/siacs/conversations/ui/EasyOnboardingInviteActivity.java
@@ -0,0 +1,9 @@
+package eu.siacs.conversations.ui;
+
+import eu.siacs.conversations.entities.Account;
+
+public class EasyOnboardingInviteActivity {
+ public static void launch(Account account, XmppActivity activity) {
+ throw new IllegalArgumentException("Easy Onboarding is not implemented for Quicksy");
+ }
+}
diff --git a/src/quicksy/res/values-el/strings.xml b/src/quicksy/res/values-el/strings.xml
index e699767ef..cd750c262 100644
--- a/src/quicksy/res/values-el/strings.xml
+++ b/src/quicksy/res/values-el/strings.xml
@@ -5,19 +5,21 @@
Το Quicksy χρησιμοποιεί μια τρίτη εφαρμογή που ονομάζεται OpenKeychain για να κρυπτογραφεί και να αποκρυπτογραφεί μηνύματα και να διαχειρίζεται τα δημόσια κλειδιά σας.\n\nΤο OpenKeychain δημοσιεύεται με την άδεια GPLv3 και είναι διαθέσιμο στο F-Droid και το Google Play.\n\n(Παρακαλώ επανεκκινήστε το Quicksy αμέσως μετά.)Το Quicksy αδυνατεί να κρυπτογραφήσει τα μηνύματά σας γιατί η επαφή σας δεν ανακοινώνει το δημόσιο κλειδί της.\n\nΠαρακαλώ ζητήστε από την επαφή σας να εγκαταστήσει το OpenPGP.Το Quicksy αδυνατεί να κρυπτογραφήσει τα μηνύματά σας γιατί οι επαφές σας δεν ανακοινώνουν το δημόσιο κλειδί τους.\n\nΠαρακαλώ ζητήστε από τις επαφές σας να εγκαταστήσουν το OpenPGP.
- Η διάρκεια του χρόνου που το Quicksy σταματάει τη λειτουργία του καθώς ανιχνεύθηκε δραστηριόητα σε άλλη συσκευή
- Στέλνοντας ίχνη στοίβας βοηθάτε την συνεχόμενη ανάπτυξη του Quicksy
+ Ο χρόνος σίγασης ειδοποιήσεων του Quicksy αφότου ανιχνευθεί δραστηριότητα σε μια από τις άλλες συσκευές σας.
+ Στέλνοντας ίχνη στοίβας προωθείτε την συνεχόμενη ανάπτυξη του QuicksyΤο Quicksy χρειάζεται πρόσβαση στον εξωτερικό αποθηκευτικό χώροΤο Quicksy χρειάζεται πρόσβαση στην φωτογραφική μηχανή
+ Η συσκευή σας χρησιμοποιεί έντονη βελτιστοποίηση στην χρήση μπαταρίας του Quicksy που μπορεί να οδηγήσει σε αργοπορημένες ειδοποιήσεις ή ακόμα και σε απώλεια μηνυμάτων.\nΠροτείνεται να την απενεργοποιήσετε.
+ Η συσκευή σας χρησιμοποιεί έντονη βελτιστοποίηση στην χρήση μπαταρίας του Quicksy που μπορεί να οδηγήσει σε αργοπορημένες ειδοποιήσεις ή ακόμα και σε απώλεια μηνυμάτων.\nΘα σας ζητηθεί να την απενεργοποιήσετε.Επιτρέψτε στις επαφές σας να γνωρίζουν πότε χρησιμοποιείτε το Quicksy
- Το λειτουργικό σας σύστημα περιορίζει το Quicksy από το να συνδέεται στο Internet όταν βρίσκεται στο παρασκήνιο. Για να λαμβάνετε ειδοποιήσεις νέων μηνυμάτων πρέπει να επιτρέψετε στο Quicksy να έχει απεριόριστη πρόσβαση όταν ενεργοποιείται η μείωση χρήσης δεδομένων κινητής.\nΤο Quicksy θα προσπαθεί να περιορίσει τη χρήση δεδομένων όταν είναι δυνατό.
- Η συσκευή σας δεν υποστηρίζει την απενεργοποίηση μείωσης χρήσης δεδομένων κινητής για το Quicksy.
+ Το λειτουργικό σας σύστημα περιορίζει το Quicksy από το να συνδέεται στο Internet όταν βρίσκεται στο παρασκήνιο. Για να λαμβάνετε ειδοποιήσεις νέων μηνυμάτων πρέπει να επιτρέψετε στο Quicksy να έχει απεριόριστη πρόσβαση όταν ενεργοποιείται η μείωση χρήσης δεδομένων.\nΤο Quicksy θα προσπαθεί να περιορίσει τη χρήση δεδομένων όταν είναι δυνατό.
+ Η συσκευή σας δεν υποστηρίζει την απενεργοποίηση μείωσης χρήσης δεδομένων για το Quicksy.Για να συνεχίσετε να λαμβάνετε ειδοποιήσεις, ακόμα κι όταν η οθόνη είναι σβηστή, χρειάζεται να προσθέσετε το Quicksy στον κατάλογο με τις προστατευμένες εφαρμογές.Το Quicksy αδυνατεί να στείλει κρυπτογραφημένα μηνύματα στην επαφή %1$s. Αυτό μπορεί να συμβαίνει γιατί η επαφή σας χρησιμοποιεί παλιότερο διακομιστή ή πρόγραμμα που δε μπορεί να χειριστεί κρυπτογράφηση OMEMO.Το Quicksy χρειάζεται πρόσβαση στο μικρόφωνοΑυτή η κατηγορία ειδοποιήσεων χρησιμοποιείται για την εμφάνιση μιας μόνιμης ειδοποίησης που δείχνει πως εκτελείται το Quicksy.
- Φωτογραφία προφίλ του quicksy
- Το quicksy δεν είναι διαθέσιμο στην χώρα σας.
+ Φωτογραφία προφίλ του Quicksy
+ Το Quicksy δεν είναι διαθέσιμο στην χώρα σας.Αδυναμία επαλήθευσης της ταυτότητας του διακομιστή.Άγνωστο σφάλμα ασφάλειας.Λήξη χρονικού ορίου κατά τη σύνδεση στον διακομιστή.
diff --git a/src/quicksy/res/values-tr-rTR/strings.xml b/src/quicksy/res/values-tr-rTR/strings.xml
new file mode 100644
index 000000000..d2fe31568
--- /dev/null
+++ b/src/quicksy/res/values-tr-rTR/strings.xml
@@ -0,0 +1,26 @@
+
+
+ Quicksy çalışmayı durdurdu
+ Çöküş raporu yollayarak Quicksy\'nin halihazırda devam eden gelişimine katkıda bulunuyorsunuz.\Uyarı:Çöküş raporları geliştiricilere gönderiminde XMPP hesabınız kullanılacak.
+ Quicksy; iletileri şifrelemek ve herkese açık anahtarlarınızı yönetmek içinOpenKeychain isimli bir üçüncü parti uygulaması kullanmaktadır.\n\nOpenKeychain GPLv3 altında lisanslıdır ve F-Droid ile Google Play üzerinden indirilebilir.\n\n (Lütfen sonrasında Quicksy\'i tekrar başlatın
+ Bağlantınız ortak anahatarını paylaşmıyor olduğu için Quicksy iletilerinizi şifreliyemiyor.\n\nLütfen bağlantınızdan OpenPGP kurmasını isteyin.
+ Kişileriniz ortak anahtarlarını yayımlamadığı için Conversations iletilerinizi şifreleyemiyor.\n\nLütfen kişilerden OpenPGP’yi ayarlamalarını isteyin.
+ Başka bir aygıt üstünde etkinlik algılandığında Quicksy\'nin sessiz kalma süresi
+ Çöküş raporu göndermeniz Quicksy\'nin geliştirlmesinde katkıda bulunacaktır.
+ Quicksy\'nin haricii depolamaya erişmesi gerekiyor
+ Quicksy\'nin kameraya erişmesi gerekiyor
+ Telefonunuz Quicksy için ağır pil optimizasyonları yürütüyor. Bu gecikmiş bildirimlere ve hatta mesaj kaybına neden olabilir.\nlt Optimizasyonların devre dış bırakılması tavsiye edilir.
+ Telefonunuz Quicksy için ağır pil optimizasyonları yürütüyor. Bu gecikmiş bildirimlere ve hatta mesaj kaybına neden olabilir.\n\nSizden şimdi onları devre dışı bırakmanız istenilecek.
+ Tüm kişileriniz ne zaman Quicksy kullandığınızı görsün
+ İşletim sisteminiz Quicksy arka planda çalışırken İnternet erişimini sınırlıyor. Yeni ileti alındığında uyarı gelmesi için Quicksy\'e veri tasarrufu etkin durumdayken sınırsız erişim vermeniz gerekiyor.\nQuicksy mümkün olan durumlarda veri tasarrufu için çaba harcar.
+ Aygıtınız Quicksy için Veri tasarrufunu devre dışı bırakmayı desteklemiyor
+ Ekranınız kapalıyken bile bildirim almak için Quicksy\'i korunan uygulamalara eklemelisiniz.
+ Quicksy %1$skişisine şifrelenmiş ileti gönderemiyor. Bunun sebebi, kişinizin tarihi geçmiş bir sunucu veya OMEMO çalıştıramayan bir istemci kullanıyor olması olabilir.
+ Quicksy\'nin mikrofona erişmesi gerekiyor
+ Bu bildirim kategorisi Quicksy\'nin çalıştığını sürekli belirtmekte kullanmaktadır.
+ Quicksy profil resmi
+ Quicksy ülkenizde kullanılamıyor.
+ Sunucu kimliği belirlenemiyor.
+ Bilinmeyen güvenlik hatası.
+ Sunucuya bağlanılırken zaman aşımına uğrandı.
+