Merge tag '2.8.9' into develop

This commit is contained in:
genofire 2020-07-18 13:38:12 +02:00
commit 1b28ea9dda
75 changed files with 643 additions and 409 deletions

View File

@ -11,7 +11,7 @@ android:
- '.+'
before_script:
- mkdir libs
- wget -O libs/libwebrtc-m83.aar http://gultsch.de/files/libwebrtc-m83.aar
- wget -O libs/libwebrtc-m84.aar http://gultsch.de/files/libwebrtc-m84.aar
script:
- ./gradlew assembleConversationsFreeSystemRelease
- ./gradlew assembleQuicksyFreeCompatRelease

View File

@ -1,6 +1,13 @@
# Changelog
### Version 2.8.9
* add 'Return to chat' to audio call screen
* Improve keyboard shortcuts
* bug fixes
### Version 2.8.8
* Fixed notifications not showing up under certain conditions
* Fixed compatibility issues and crashes related to A/V calls
@ -33,7 +40,7 @@
### Version 2.8.2
* Add button to switch camea during video call
* Add button to switch camera during video call
* Fixed voice calls on tablets
### Version 2.8.1

View File

@ -96,8 +96,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 394
versionName "2.8.8"
versionCode 395
versionName "2.8.9"
archivesBaseName += "-$versionName"
applicationId "eu.sum7.conversations"
resValue "string", "applicationId", applicationId
@ -232,7 +232,7 @@ android {
}
lintOptions {
disable 'ExtraTranslation', 'MissingTranslation', 'InvalidPackage', 'MissingQuantity', 'AppCompatResource'
disable 'MissingTranslation', 'InvalidPackage', 'AppCompatResource'
abortOnError false
}

View File

@ -1,3 +1,3 @@
* Show help button if A/V call fails
* Fixed some annoying crashes
* Fixed Jingle connections (file transfer + calls) with bare JIDs
Show help button if A/V call fails
Fixed some annoying crashes
Fixed Jingle connections (file transfer + calls) with bare JIDs

View File

@ -0,0 +1,3 @@
• add 'Return to chat' to audio call screen
• Improve keyboard shortcuts
• bug fixes

View File

@ -1,5 +1,6 @@
package eu.siacs.conversations.services;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
@ -10,9 +11,21 @@ import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.provider.OpenableColumns;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;
import com.google.common.base.Charsets;
import com.google.common.io.CountingInputStream;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.io.CipherInputStream;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
@ -33,10 +46,6 @@ import java.util.zip.GZIPInputStream;
import java.util.zip.ZipException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@ -44,14 +53,9 @@ import eu.siacs.conversations.persistance.DatabaseBackend;
import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.ManageAccountActivity;
import eu.siacs.conversations.utils.BackupFileHeader;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
import eu.siacs.conversations.xmpp.Jid;
import static eu.siacs.conversations.services.ExportBackupService.CIPHERMODE;
import static eu.siacs.conversations.services.ExportBackupService.KEYTYPE;
import static eu.siacs.conversations.services.ExportBackupService.PROVIDER;
public class ImportBackupService extends Service {
private static final int NOTIFICATION_ID = 21;
@ -117,9 +121,9 @@ public class ImportBackupService extends Service {
return running.get();
}
public void loadBackupFiles(OnBackupFilesLoaded onBackupFilesLoaded) {
public void loadBackupFiles(final OnBackupFilesLoaded onBackupFilesLoaded) {
executor.execute(() -> {
List<Jid> accounts = mDatabaseBackend.getAccountJids(false);
final List<Jid> accounts = mDatabaseBackend.getAccountJids(false);
final ArrayList<BackupFile> backupFiles = new ArrayList<>();
final Set<String> apps = new HashSet<>(Arrays.asList("Conversations", "Quicksy", getString(R.string.app_name)));
for (String app : apps) {
@ -128,7 +132,12 @@ public class ImportBackupService extends Service {
Log.d(Config.LOGTAG, "directory not found: " + directory.getAbsolutePath());
continue;
}
for (File file : directory.listFiles()) {
final File[] files = directory.listFiles();
if (files == null) {
onBackupFilesLoaded.onBackupFilesLoaded(backupFiles);
return;
}
for (final File file : files) {
if (file.isFile() && file.getName().endsWith(".ceb")) {
try {
final BackupFile backupFile = BackupFile.read(file);
@ -149,24 +158,67 @@ public class ImportBackupService extends Service {
}
private void startForegroundService() {
startForeground(NOTIFICATION_ID, createImportBackupNotification(1, 0));
}
private void updateImportBackupNotification(final long total, final long current) {
final int max;
final int progress;
if (total == 0) {
max = 1;
progress = 0;
} else {
max = 100;
progress = (int) (current * 100 / total);
}
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
try {
notificationManager.notify(NOTIFICATION_ID, createImportBackupNotification(max, progress));
} catch (final RuntimeException e) {
Log.d(Config.LOGTAG, "unable to make notification", e);
}
}
private Notification createImportBackupNotification(final int max, final int progress) {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup");
mBuilder.setContentTitle(getString(R.string.restoring_backup))
.setSmallIcon(R.drawable.ic_unarchive_white_24dp)
.setProgress(1, 0, true);
startForeground(NOTIFICATION_ID, mBuilder.build());
.setProgress(max, progress, max == 1 && progress == 0);
return mBuilder.build();
}
private boolean importBackup(Uri uri, String password) {
private boolean importBackup(final Uri uri, final String password) {
Log.d(Config.LOGTAG, "importing backup from " + uri);
try {
SQLiteDatabase db = mDatabaseBackend.getWritableDatabase();
final SQLiteDatabase db = mDatabaseBackend.getWritableDatabase();
final InputStream inputStream;
if ("file".equals(uri.getScheme())) {
inputStream = new FileInputStream(new File(uri.getPath()));
final String path = uri.getPath();
final long fileSize;
if ("file".equals(uri.getScheme()) && path != null) {
final File file = new File(path);
inputStream = new FileInputStream(file);
fileSize = file.length();
} else {
final Cursor returnCursor = getContentResolver().query(uri, null, null, null, null);
if (returnCursor == null) {
fileSize = 0;
} else {
returnCursor.moveToFirst();
fileSize = returnCursor.getLong(returnCursor.getColumnIndex(OpenableColumns.SIZE));
returnCursor.close();
}
inputStream = getContentResolver().openInputStream(uri);
}
final DataInputStream dataInputStream = new DataInputStream(inputStream);
if (inputStream == null) {
synchronized (mOnBackupProcessedListeners) {
for (final OnBackupProcessed l : mOnBackupProcessedListeners) {
l.onBackupRestoreFailed();
}
}
return false;
}
final CountingInputStream countingInputStream = new CountingInputStream(inputStream);
final DataInputStream dataInputStream = new DataInputStream(countingInputStream);
final BackupFileHeader backupFileHeader = BackupFileHeader.read(dataInputStream);
Log.d(Config.LOGTAG, backupFileHeader.toString());
@ -179,15 +231,14 @@ public class ImportBackupService extends Service {
return false;
}
final Cipher cipher = Compatibility.twentyEight() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
byte[] key = ExportBackupService.getKey(password, backupFileHeader.getSalt());
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(backupFileHeader.getIv());
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
final byte[] key = ExportBackupService.getKey(password, backupFileHeader.getSalt());
GZIPInputStream gzipInputStream = new GZIPInputStream(cipherInputStream);
BufferedReader reader = new BufferedReader(new InputStreamReader(gzipInputStream, "UTF-8"));
AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
cipher.init(false, new AEADParameters(new KeyParameter(key), 128, backupFileHeader.getIv()));
final CipherInputStream cipherInputStream = new CipherInputStream(countingInputStream, cipher);
final GZIPInputStream gzipInputStream = new GZIPInputStream(cipherInputStream);
BufferedReader reader = new BufferedReader(new InputStreamReader(gzipInputStream, Charsets.UTF_8));
String line;
StringBuilder multiLineQuery = null;
while ((line = reader.readLine()) != null) {
@ -198,10 +249,12 @@ public class ImportBackupService extends Service {
if (count % 2 == 1) {
db.execSQL(multiLineQuery.toString());
multiLineQuery = null;
updateImportBackupNotification(fileSize, countingInputStream.getCount());
}
} else {
if (count % 2 == 0) {
db.execSQL(line);
updateImportBackupNotification(fileSize, countingInputStream.getCount());
} else {
multiLineQuery = new StringBuilder(line);
}
@ -220,7 +273,7 @@ public class ImportBackupService extends Service {
}
}
return true;
} catch (Exception e) {
} catch (final Exception e) {
Throwable throwable = e.getCause();
final boolean reasonWasCrypto = throwable instanceof BadPaddingException || e instanceof ZipException;
synchronized (mOnBackupProcessedListeners) {

View File

@ -8,4 +8,5 @@
<string name="magic_create_text_on_x">Du wurdest zu %1$s eingeladen. Wir führen dich durch den Prozess der Kontoerstellung.\nWenn du %1$s als Provider wählst, kannst du mit Nutzern anderer Anbieter kommunizieren, indem du ihnen deine vollständige XMPP-Adresse gibst.</string>
<string name="magic_create_text_fixed">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.</string>
<string name="your_server_invitation">Deine Einladung für den Server</string>
<string name="improperly_formatted_provisioning">Falsch formatierter Provisionierungscode</string>
</resources>

View File

@ -8,4 +8,4 @@
<string name="magic_create_text_on_x">Έχετε προσκληθεί στο %1$s. Ένα όνομα χρήστη έχει ήδη επιλεγεί για εσάς. Θα σας καθοδηγήσουμε στη διαδικασία δημιουργίας λογαριασμού.\nΕπιλέγοντας τον %1$s ως πάροχο θα μπορείτε να επικοινωνείτε με χρήστες άλλων παρόχων δίνοντάς τους την πλήρη διεύθυνση XMPP σας.</string>
<string name="magic_create_text_fixed">Έχετε προσκληθεί στο %1$s. Ένα όνομα χρήστη έχει ήδη επιλεγεί για εσάς. Θα σας καθοδηγήσουμε στη διαδικασία δημιουργίας λογαριασμού.\nΘα μπορείτε να επικοινωνείτε με χρήστες άλλων παρόχων δίνοντάς τους την πλήρη διεύθυνση XMPP σας.</string>
<string name="your_server_invitation">Η πρόσκλησή σας στον διακομιστή</string>
</resources>
</resources>

View File

@ -8,4 +8,5 @@
<string name="magic_create_text_on_x">Has sido invitado a %1$s. Te guiaremos durante el proceso de creación de la cuenta.\nCuando selecciones %1$s como proveedor podrás comunicarte con usuarios de otros servidores proporcionándoles tu dirección XMPP completa. </string>
<string name="magic_create_text_fixed">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. </string>
<string name="your_server_invitation">Tu invitación al servidor</string>
<string name="improperly_formatted_provisioning">Código de abastecimiento formateado incorrectamente</string>
</resources>

View File

@ -8,4 +8,5 @@
<string name="magic_create_text_on_x">Vous avez été invité à %1$s. Nous allons vous guider à travers le processus de création dun compte.\nEn choisissant %1$s comme fournisseur, vous pourrez communiquer avec les utilisateurs des autres fournisseurs en leur donnant votre adresse XMPP complète.</string>
<string name="magic_create_text_fixed">Vous avez été invité à %1$s. Un nom dutilisateur a déjà été choisi pour vous. Nous allons vous guider à travers le processus de création dun compte.\nVous pourrez communiquer avec les utilisateurs des autres fournisseurs en leur donnant votre adresse XMPP complète.</string>
<string name="your_server_invitation">Votre invitation au serveur</string>
<string name="improperly_formatted_provisioning">Code de provisionnement mal formaté</string>
</resources>

View File

@ -8,4 +8,5 @@
<string name="magic_create_text_on_x">Convidáronte a %1$s. Guiarémoste no proceso para crear unha conta.\nAo escoller %1$s como provedor poderás comunicarte con usuarias de outros provedores cando lles deas o teu enderezo XMPP completo.</string>
<string name="magic_create_text_fixed">Convidáronte a %1$s. Escollemos un nome de usuaria por ti. Guiarémoste no proceso de crear unha conta.\nPoderás comunicarte con usuarias de outros provedores cando lles digas o teu enderezo XMPP completo.</string>
<string name="your_server_invitation">O convite do teu servidor</string>
<string name="improperly_formatted_provisioning">Código de aprovisionamento con formato non válido</string>
</resources>

View File

@ -8,4 +8,4 @@
<string name="magic_create_text_on_x">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.</string>
<string name="magic_create_text_fixed">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.</string>
<string name="your_server_invitation">Az Ön kiszolgálómeghívása</string>
</resources>
</resources>

View File

@ -10,4 +10,4 @@ In ogni caso per facilitare puoi creare facilmente un account su chat.sum7.eu, u
<string name="magic_create_text_on_x">Sei stato invitato su %1$s. Ti guideremo nel procedimento per creare un account.\nQuando scegli %1$s come fornitore sarai in grado di comunicare con utenti di altri fornitori dando loro l\'indirizzo XMPP completo.</string>
<string name="magic_create_text_fixed">Sei stato invitato su %1$s. È già stato scelto un nome utente per te. Ti guideremo nel procedimento per creare un account.\nSarai in grado di comunicare con utenti di altri fornitori dando loro l\'indirizzo XMPP completo.</string>
<string name="your_server_invitation">Il tuo invito al server</string>
</resources>
</resources>

View File

@ -8,4 +8,4 @@
<string name="magic_create_text_on_x">Je ontving een uitnodiging voor %1$s. We zullen je helpen een account aan te maken.\nWanneer je %1$s als je provider kiest kan je met gebruikers van andere providers communiceren door hen je volledige XMPP-adres te geven.</string>
<string name="magic_create_text_fixed">Je ontving een uitnodiging voor %1$s. Er werd reeds een gebruikersnaam voor jou gekozen. We zullen je helpen een account aan te maken.\nJe zal met gebruikers van andere providers communiceren door hen je volledige XMPP-adres te geven.</string>
<string name="your_server_invitation">Je server uitnodiging</string>
</resources>
</resources>

View File

@ -8,4 +8,4 @@
<string name="magic_create_text_on_x">Zostałeś zaproszony do %1$s. Poprowadzimy ciebie przez proces tworzenia konta.\nWybierając %1$s jako dostawcę będziesz mógł komunikować się z innymi użytkownikami podając swój pełny adres XMPP.</string>
<string name="magic_create_text_fixed">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.</string>
<string name="your_server_invitation">Zaproszenie twojego serwera</string>
</resources>
</resources>

View File

@ -8,4 +8,4 @@
<string name="magic_create_text_on_x">Você foi convidado para %1$s. Nós iremos guiá-lo ao longo do processo de criação de uma conta.\nAo escolher %1$s como um provedor você conseguirá se comunicar com usuários de outros provedores dando a eles seu endereço XMPP completo.</string>
<string name="magic_create_text_fixed">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.</string>
<string name="your_server_invitation">Seu convite do servidor</string>
</resources>
</resources>

View File

@ -8,4 +8,5 @@
<string name="magic_create_text_on_x">Ați fost invitați la %1$s. Vă vom ghida prin procesul de creare al unui cont.\nCând alegeți %1$s ca furnizor veți putea comunica cu utilizatorii altor furnizori oferindu-le adresa dumneavoastră completă XMPP.</string>
<string name="magic_create_text_fixed">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.</string>
<string name="your_server_invitation">Invitația serverului dumneavoastră</string>
<string name="improperly_formatted_provisioning">Cod de acces formatat necorespunzător</string>
</resources>

View File

@ -4,5 +4,8 @@
<string name="use_chat.sum7.eu">Использовать chat.sum7.eu</string>
<string name="create_new_account">Создать новый аккаунт</string>
<string name="do_you_have_an_account">У вас есть аккаунт XMPP? Если вы использовали Conversations или другой XMPP-клиент в прошлом, то скорее всего, он у вас есть. Если у вас нет аккаунта, вы можете создать его прямо сейчас.\nНекоторые провайдеры электронной почты также регистрируют аккаунты XMPP. </string>
<string name="server_select_text">XMPP - это независимая сеть обмена сообщениями. Conversations позволяет вам подключиться к любому XMPP-серверу на ваш выбор.\nЕсли у вас нет сервера, предлагаем вам зарегистрировать аккаунт на chat.sum7.eu, сервер, специально предназначеный для работы с приложением Conv6sations.</string>
</resources>
<string name="server_select_text">XMPP - это независимая сеть обмена сообщениями. Conversations позволяет вам подключиться к любому XMPP-серверу на ваш выбор.\nЕсли у вас нет сервера, предлагаем вам зарегистрировать аккаунт на chat.sum7.eu, сервере, специально предназначенном для работы с Conversations.</string>
<string name="magic_create_text_on_x">Вас пригласили на %1$s. Мы проведём вас через процесс создания аккаунта. Аккаунт на %1$s позволит вам общаться с пользователями и на этом, и на других серверах, используя ваш полный XMPP-адрес.</string>
<string name="magic_create_text_fixed">Вас пригласили на %1$s. Вам уже назначили имя пользователя. Мы проведём вас через процесс создания аккаунта. Этот аккаунт позволит вам общаться с пользователями и на этом, и на других серверах, используя ваш полный XMPP-адрес.</string>
<string name="your_server_invitation">Ваше приглашение</string>
</resources>

View File

@ -8,4 +8,5 @@
<string name="magic_create_text_on_x">Вас запросили до %1$s. Ми проведемо вас крок за кроком, щоб створити обліковий запис.\nОбираючи %1$s в якості свого постачальника, ви зможете спілкуватися з користувачами інших постачальників, для цього повідомте їм свою повну адресу XMPP.</string>
<string name="magic_create_text_fixed">Вас запросили до %1$s. Для вас створено ім\'я користувача. Ми проведемо вас крок за кроком, щоб створити обліковий запис.\nВи зможете спілкуватися з користувачами інших постачальників, для цього повідомите їм свою повну адресу XMPP.</string>
<string name="your_server_invitation">Ваше запрошення до сервера</string>
<string name="improperly_formatted_provisioning">Неправильно відформатований код забезпечення</string>
</resources>

View File

@ -8,4 +8,4 @@
<string name="magic_create_text_on_x">您已受邀参加%1$s。 我们将指导您完成创建帐户的过程。\n选择%1$s作为提供者后您可以通过提供其他人的完整XMPP地址与其他提供者的用户进行交流。</string>
<string name="magic_create_text_fixed">您已受邀参加%1$s。 已经为您选择了一个用户名。 我们将指导您完成创建帐户的过程。\n您可以通过向其他提供商的用户提供完整的XMPP地址来与他们进行交流。</string>
<string name="your_server_invitation">你的服务器邀请</string>
</resources>
</resources>

View File

@ -173,7 +173,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
public String findMostRecentRemoteDisplayableId() {
final boolean multi = mode == Conversation.MODE_MULTI;
synchronized (this.messages) {
for(final Message message : Lists.reverse(this.messages)) {
for (final Message message : Lists.reverse(this.messages)) {
if (message.getStatus() == Message.STATUS_RECEIVED) {
final String serverMsgId = message.getServerMsgId();
if (serverMsgId != null && multi) {
@ -186,6 +186,21 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return null;
}
public Message getLastEditableMessage() {
synchronized (this.messages) {
for (final Message message : Lists.reverse(this.messages)) {
if (message.isEditable()) {
if (message.isGeoUri() || message.getType() != Message.TYPE_TEXT) {
return null;
}
return message;
}
}
}
return null;
}
public Message findUnsentMessageWithUuid(String uuid) {
synchronized (this.messages) {
for (final Message message : this.messages) {
@ -499,7 +514,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
@Override
public int compareTo(@NonNull Conversation another) {
return ComparisonChain.start()
.compareFalseFirst(another.getBooleanAttribute(ATTRIBUTE_PINNED_ON_TOP, false), getBooleanAttribute(ATTRIBUTE_PINNED_ON_TOP,false))
.compareFalseFirst(another.getBooleanAttribute(ATTRIBUTE_PINNED_ON_TOP, false), getBooleanAttribute(ATTRIBUTE_PINNED_ON_TOP, false))
.compare(another.getSortableTime(), getSortableTime())
.result();
}

View File

@ -15,7 +15,6 @@ import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@ -613,15 +612,15 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
public boolean isLastCorrectableMessage() {
Message next = next();
while (next != null) {
if (next.isCorrectable()) {
if (next.isEditable()) {
return false;
}
next = next.next();
}
return isCorrectable();
return isEditable();
}
private boolean isCorrectable() {
public boolean isEditable() {
return getStatus() != STATUS_RECEIVED && !isCarbon();
}

View File

@ -1,16 +1,20 @@
package eu.siacs.conversations.ui;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.databinding.DataBindingUtil;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Intents;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.text.Spannable;
@ -57,15 +61,15 @@ import eu.siacs.conversations.utils.IrregularUnicodeDetector;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.Jid;
public class ContactDetailsActivity extends OmemoActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated, OnMediaLoaded {
public static final String ACTION_VIEW_CONTACT = "view_contact";
private final int REQUEST_SYNC_CONTACTS = 0x28cf;
ActivityContactDetailsBinding binding;
private MediaAdapter mMediaAdapter;
private Contact contact;
@ -110,47 +114,41 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
private boolean showInactiveOmemo = false;
private String messageFingerprint;
private DialogInterface.OnClickListener addToPhonebook = new DialogInterface.OnClickListener() {
private void checkContactPermissionAndShowAddDialog() {
if (hasContactsPermission()) {
showAddToPhoneBookDialog();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_SYNC_CONTACTS);
}
}
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
private boolean hasContactsPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return checkSelfPermission(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED;
} else {
return true;
}
}
private void showAddToPhoneBookDialog() {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.action_add_phone_book));
builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid().toEscapedString()));
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setPositiveButton(getString(R.string.add), (dialog, which) -> {
final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
intent.setType(Contacts.CONTENT_ITEM_TYPE);
intent.putExtra(Intents.Insert.IM_HANDLE, contact.getJid().toEscapedString());
intent.putExtra(Intents.Insert.IM_PROTOCOL, CommonDataKinds.Im.PROTOCOL_JABBER);
intent.putExtra("finishActivityOnSaveCompleted", true);
try {
ContactDetailsActivity.this.startActivityForResult(intent, 0);
startActivityForResult(intent, 0);
} catch (ActivityNotFoundException e) {
Toast.makeText(ContactDetailsActivity.this, R.string.no_application_found_to_view_contact, Toast.LENGTH_SHORT).show();
}
}
};
private OnClickListener onBadgeClick = new OnClickListener() {
@Override
public void onClick(View v) {
Uri systemAccount = contact.getSystemAccount();
if (systemAccount == null) {
AlertDialog.Builder builder = new AlertDialog.Builder(
ContactDetailsActivity.this);
builder.setTitle(getString(R.string.action_add_phone_book));
builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid().toEscapedString()));
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setPositiveButton(getString(R.string.add), addToPhonebook);
builder.create().show();
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(systemAccount);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(ContactDetailsActivity.this, R.string.no_application_found_to_view_contact, Toast.LENGTH_SHORT).show();
}
}
}
};
});
builder.create().show();
}
@Override
public void onRosterUpdate() {
@ -233,6 +231,18 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
mMediaAdapter.setAttachments(Collections.emptyList());
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
if (grantResults.length > 0)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (requestCode == REQUEST_SYNC_CONTACTS && xmppConnectionServiceBound) {
showAddToPhoneBookDialog();
xmppConnectionService.loadPhoneContacts();
xmppConnectionService.startContactObserver();
}
}
}
@Override
public boolean onOptionsItemSelected(final MenuItem menuItem) {
if (MenuDoubleTabUtil.shouldIgnoreTap()) {
@ -417,7 +427,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
}
binding.detailsAccount.setText(getString(R.string.using_account, account));
AvatarWorkerTask.loadAvatar(contact, binding.detailsContactBadge, R.dimen.avatar_on_details_screen_size);
binding.detailsContactBadge.setOnClickListener(this.onBadgeClick);
binding.detailsContactBadge.setOnClickListener(this::onBadgeClick);
binding.detailsContactKeys.removeAllViews();
boolean hasKeys = false;
@ -496,6 +506,21 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
}
}
private void onBadgeClick(View view) {
final Uri systemAccount = contact.getSystemAccount();
if (systemAccount == null) {
checkContactPermissionAndShowAddDialog();
} else {
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(systemAccount);
try {
startActivity(intent);
} catch (final ActivityNotFoundException e) {
Toast.makeText(this, R.string.no_application_found_to_view_contact, Toast.LENGTH_SHORT).show();
}
}
}
public void onBackendConnected() {
if (accountJid != null && contactJid != null) {
Account account = xmppConnectionService.findAccountByJid(accountJid);

View File

@ -32,6 +32,7 @@ import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -2733,15 +2734,26 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
@Override
public boolean onEnterPressed() {
SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getActivity());
final boolean enterIsSend = p.getBoolean("enter_is_send", getResources().getBoolean(R.bool.enter_is_send));
if (enterIsSend) {
public boolean onEnterPressed(final boolean isCtrlPressed) {
if (isCtrlPressed || enterIsSend()) {
sendMessage();
return true;
} else {
return false;
}
return false;
}
private boolean enterIsSend() {
final SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getActivity());
return p.getBoolean("enter_is_send", getResources().getBoolean(R.bool.enter_is_send));
}
public boolean onArrowUpCtrlPressed() {
final Message lastEditableMessage = conversation == null ? null : conversation.getLastEditableMessage();
if (lastEditableMessage != null) {
correctMessage(lastEditableMessage);
return true;
}
return false;
}
@Override

View File

@ -49,6 +49,7 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
@ -493,8 +494,19 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
Intent pendingIntent = pendingViewIntent.peek();
public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) {
if (keyCode == KeyEvent.KEYCODE_DPAD_UP && keyEvent.isCtrlPressed()) {
final ConversationFragment conversationFragment = ConversationFragment.get(this);
if (conversationFragment != null && conversationFragment.onArrowUpCtrlPressed()) {
return true;
}
}
return super.onKeyDown(keyCode, keyEvent);
}
@Override
public void onSaveInstanceState(final Bundle savedInstanceState) {
final Intent pendingIntent = pendingViewIntent.peek();
savedInstanceState.putParcelable("intent", pendingIntent != null ? pendingIntent : getIntent());
super.onSaveInstanceState(savedInstanceState);
}

View File

@ -88,6 +88,10 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
RtpEndUserState.APPLICATION_ERROR,
RtpEndUserState.CONNECTIVITY_ERROR
);
private static final List<RtpEndUserState> STATES_SHOWING_SWITCH_TO_CHAT = Arrays.asList(
RtpEndUserState.CONNECTING,
RtpEndUserState.CONNECTED
);
private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session";
private static final int REQUEST_ACCEPT_CALL = 0x1111;
private WeakReference<JingleRtpConnection> rtpConnectionReference;
@ -135,7 +139,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.activity_rtp_session, menu);
final MenuItem help = menu.findItem(R.id.action_help);
final MenuItem gotoChat = menu.findItem(R.id.action_goto_chat);
help.setVisible(isHelpButtonVisible());
gotoChat.setVisible(isSwitchToConversationVisible());
return super.onCreateOptionsMenu(menu);
}
@ -153,10 +159,25 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
}
private boolean isSwitchToConversationVisible() {
final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
return connection != null && STATES_SHOWING_SWITCH_TO_CHAT.contains(connection.getEndUserState());
}
private void switchToConversation() {
final Contact contact = getWith();
final Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), false, true);
switchToConversation(conversation);
}
public boolean onOptionsItemSelected(final MenuItem item) {
if (item.getItemId() == R.id.action_help) {
launchHelpInBrowser();
return true;
switch (item.getItemId()) {
case R.id.action_help:
launchHelpInBrowser();
break;
case R.id.action_goto_chat:
switchToConversation();
break;
}
return super.onOptionsItemSelected(item);
}
@ -308,6 +329,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
return;
}
final Account account = extractAccount(intent);
final String action = intent.getAction();
final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID);
if (sessionId != null) {
@ -320,6 +342,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
requestPermissionsAndAcceptCall();
resetIntent(intent.getExtras());
}
} else if (asList(ACTION_MAKE_VIDEO_CALL, ACTION_MAKE_VOICE_CALL).contains(action)) {
proposeJingleRtpSession(account, with, actionToMedia(action));
binding.with.setText(account.getRoster().getContact(with).getDisplayName());
} else {
throw new IllegalStateException("received onNewIntent without sessionId");
}
@ -437,7 +462,11 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
return;
}
}
retractSessionProposal();
//TODO apparently this method is not getting called on Android 10 when using the task switcher
final boolean emptyReference = rtpConnectionReference == null || rtpConnectionReference.get() == null;
if (emptyReference && xmppConnectionService != null) {
retractSessionProposal();
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
@ -910,15 +939,17 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
}
private void disableMicrophone(View view) {
JingleRtpConnection rtpConnection = requireRtpConnection();
rtpConnection.setMicrophoneEnabled(false);
updateInCallButtonConfiguration();
final JingleRtpConnection rtpConnection = requireRtpConnection();
if (rtpConnection.setMicrophoneEnabled(false)) {
updateInCallButtonConfiguration();
}
}
private void enableMicrophone(View view) {
JingleRtpConnection rtpConnection = requireRtpConnection();
rtpConnection.setMicrophoneEnabled(true);
updateInCallButtonConfiguration();
final JingleRtpConnection rtpConnection = requireRtpConnection();
if (rtpConnection.setMicrophoneEnabled(true)) {
updateInCallButtonConfiguration();
}
}
private void switchToEarpiece(View view) {

View File

@ -553,7 +553,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.VISIBLE);
final FileParams params = message.getFileParams();
final double target = metrics.density * 288;
final float target = activity.getResources().getDimension(R.dimen.image_preview_width);
final int scaledW;
final int scaledH;
if (Math.max(params.height, params.width) * metrics.density <= target) {
@ -569,7 +569,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
scaledW = (int) target;
scaledH = (int) (params.height / ((double) params.width / target));
}
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scaledW, scaledH);
final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scaledW, scaledH);
layoutParams.setMargins(0, (int) (metrics.density * 4), 0, (int) (metrics.density * 4));
viewHolder.image.setLayoutParams(layoutParams);
activity.loadBitmap(message, viewHolder.image);

View File

@ -54,13 +54,14 @@ public class EditMessage extends EmojiWrapperEditText {
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent e) {
public boolean onKeyDown(final int keyCode, final KeyEvent e) {
final boolean isCtrlPressed = e.isCtrlPressed();
if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
lastInputWasTab = false;
if (keyboardListener != null && keyboardListener.onEnterPressed()) {
if (keyboardListener != null && keyboardListener.onEnterPressed(isCtrlPressed)) {
return true;
}
} else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
} else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !isCtrlPressed) {
if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
lastInputWasTab = true;
return true;
@ -191,7 +192,7 @@ public class EditMessage extends EmojiWrapperEditText {
}
public interface KeyboardListener {
boolean onEnterPressed();
boolean onEnterPressed(boolean isCtrlPressed);
void onTypingStarted();

View File

@ -29,280 +29,281 @@
package eu.siacs.conversations.utils;
import android.support.annotation.NonNull;
import android.util.LruCache;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Pattern;
public class Emoticons {
private static final UnicodeRange MISC_SYMBOLS_AND_PICTOGRAPHS = new UnicodeRange(0x1F300,0x1F5FF);
private static final UnicodeRange SUPPLEMENTAL_SYMBOLS = new UnicodeRange(0x1F900,0x1F9FF);
private static final UnicodeRange EMOTICONS = new UnicodeRange(0x1F600,0x1F64F);
private static final UnicodeRange TRANSPORT_SYMBOLS = new UnicodeRange(0x1F680,0x1F6FF);
private static final UnicodeRange MISC_SYMBOLS = new UnicodeRange(0x2600,0x26FF);
private static final UnicodeRange DINGBATS = new UnicodeRange(0x2700,0x27BF);
private static final UnicodeRange ENCLOSED_ALPHANUMERIC_SUPPLEMENT = new UnicodeRange(0x1F100,0x1F1FF);
private static final UnicodeRange ENCLOSED_IDEOGRAPHIC_SUPPLEMENT = new UnicodeRange(0x1F200,0x1F2FF);
private static final UnicodeRange REGIONAL_INDICATORS = new UnicodeRange(0x1F1E6,0x1F1FF);
private static final UnicodeRange GEOMETRIC_SHAPES = new UnicodeRange(0x25A0,0x25FF);
private static final UnicodeRange LATIN_SUPPLEMENT = new UnicodeRange(0x80,0xFF);
private static final UnicodeRange MISC_TECHNICAL = new UnicodeRange(0x2300,0x23FF);
private static final UnicodeRange TAGS = new UnicodeRange(0xE0020,0xE007F);
private static final UnicodeList CYK_SYMBOLS_AND_PUNCTUATION = new UnicodeList(0x3030,0x303D);
private static final UnicodeList LETTERLIKE_SYMBOLS = new UnicodeList(0x2122,0x2139);
private static final UnicodeRange MISC_SYMBOLS_AND_PICTOGRAPHS = new UnicodeRange(0x1F300, 0x1F5FF);
private static final UnicodeRange SUPPLEMENTAL_SYMBOLS = new UnicodeRange(0x1F900, 0x1F9FF);
private static final UnicodeRange EMOTICONS = new UnicodeRange(0x1F600, 0x1F64F);
private static final UnicodeRange TRANSPORT_SYMBOLS = new UnicodeRange(0x1F680, 0x1F6FF);
private static final UnicodeRange MISC_SYMBOLS = new UnicodeRange(0x2600, 0x26FF);
private static final UnicodeRange DINGBATS = new UnicodeRange(0x2700, 0x27BF);
private static final UnicodeRange ENCLOSED_ALPHANUMERIC_SUPPLEMENT = new UnicodeRange(0x1F100, 0x1F1FF);
private static final UnicodeRange ENCLOSED_IDEOGRAPHIC_SUPPLEMENT = new UnicodeRange(0x1F200, 0x1F2FF);
private static final UnicodeRange REGIONAL_INDICATORS = new UnicodeRange(0x1F1E6, 0x1F1FF);
private static final UnicodeRange GEOMETRIC_SHAPES = new UnicodeRange(0x25A0, 0x25FF);
private static final UnicodeRange LATIN_SUPPLEMENT = new UnicodeRange(0x80, 0xFF);
private static final UnicodeRange MISC_TECHNICAL = new UnicodeRange(0x2300, 0x23FF);
private static final UnicodeRange TAGS = new UnicodeRange(0xE0020, 0xE007F);
private static final UnicodeList CYK_SYMBOLS_AND_PUNCTUATION = new UnicodeList(0x3030, 0x303D);
private static final UnicodeList LETTERLIKE_SYMBOLS = new UnicodeList(0x2122, 0x2139);
private static final UnicodeBlocks KEYCAP_COMBINEABLE = new UnicodeBlocks(new UnicodeList(0x23),new UnicodeList(0x2A),new UnicodeRange(0x30,0x39));
private static final UnicodeBlocks KEYCAP_COMBINEABLE = new UnicodeBlocks(new UnicodeList(0x23), new UnicodeList(0x2A), new UnicodeRange(0x30, 0x39));
private static final UnicodeBlocks SYMBOLIZE = new UnicodeBlocks(
GEOMETRIC_SHAPES,
LATIN_SUPPLEMENT,
CYK_SYMBOLS_AND_PUNCTUATION,
LETTERLIKE_SYMBOLS,
KEYCAP_COMBINEABLE);
private static final UnicodeBlocks EMOJIS = new UnicodeBlocks(
MISC_SYMBOLS_AND_PICTOGRAPHS,
SUPPLEMENTAL_SYMBOLS,
EMOTICONS,
TRANSPORT_SYMBOLS,
MISC_SYMBOLS,
DINGBATS,
ENCLOSED_ALPHANUMERIC_SUPPLEMENT,
ENCLOSED_IDEOGRAPHIC_SUPPLEMENT,
MISC_TECHNICAL);
private static final UnicodeBlocks SYMBOLIZE = new UnicodeBlocks(
GEOMETRIC_SHAPES,
LATIN_SUPPLEMENT,
CYK_SYMBOLS_AND_PUNCTUATION,
LETTERLIKE_SYMBOLS,
KEYCAP_COMBINEABLE);
private static final UnicodeBlocks EMOJIS = new UnicodeBlocks(
MISC_SYMBOLS_AND_PICTOGRAPHS,
SUPPLEMENTAL_SYMBOLS,
EMOTICONS,
TRANSPORT_SYMBOLS,
MISC_SYMBOLS,
DINGBATS,
ENCLOSED_ALPHANUMERIC_SUPPLEMENT,
ENCLOSED_IDEOGRAPHIC_SUPPLEMENT,
MISC_TECHNICAL);
private static final int MAX_EMOIJS = 42;
private static final int MAX_EMOIJS = 42;
private static final int ZWJ = 0x200D;
private static final int VARIATION_16 = 0xFE0F;
private static final int COMBINING_ENCLOSING_KEYCAP = 0x20E3;
private static final int BLACK_FLAG = 0x1F3F4;
private static final UnicodeRange FITZPATRICK = new UnicodeRange(0x1F3FB,0x1F3FF);
private static final int ZWJ = 0x200D;
private static final int VARIATION_16 = 0xFE0F;
private static final int COMBINING_ENCLOSING_KEYCAP = 0x20E3;
private static final int BLACK_FLAG = 0x1F3F4;
private static final UnicodeRange FITZPATRICK = new UnicodeRange(0x1F3FB, 0x1F3FF);
private static final LruCache<CharSequence,Pattern> CACHE = new LruCache<>(256);
private static final LruCache<CharSequence, Pattern> CACHE = new LruCache<>(256);
private static List<Symbol> parse(String input) {
List<Symbol> symbols = new ArrayList<>();
Builder builder = new Builder();
boolean needsFinalBuild = false;
for (int cp, i = 0; i < input.length(); i += Character.charCount(cp)) {
cp = input.codePointAt(i);
if (builder.offer(cp)) {
needsFinalBuild = true;
} else {
symbols.add(builder.build());
builder = new Builder();
if (builder.offer(cp)) {
needsFinalBuild = true;
}
}
}
if (needsFinalBuild) {
symbols.add(builder.build());
}
return symbols;
}
private static List<Symbol> parse(String input) {
List<Symbol> symbols = new ArrayList<>();
Builder builder = new Builder();
boolean needsFinalBuild = false;
for (int cp, i = 0; i < input.length(); i += Character.charCount(cp)) {
cp = input.codePointAt(i);
if (builder.offer(cp)) {
needsFinalBuild = true;
} else {
symbols.add(builder.build());
builder = new Builder();
if (builder.offer(cp)) {
needsFinalBuild = true;
}
}
}
if (needsFinalBuild) {
symbols.add(builder.build());
}
return symbols;
}
public static Pattern getEmojiPattern(CharSequence input) {
Pattern pattern = CACHE.get(input);
if (pattern == null) {
pattern = generatePattern(input);
CACHE.put(input, pattern);
}
return pattern;
}
public static Pattern getEmojiPattern(final CharSequence input) {
Pattern pattern = CACHE.get(input);
if (pattern == null) {
pattern = generatePattern(input);
CACHE.put(input, pattern);
}
return pattern;
}
private static Pattern generatePattern(CharSequence input) {
final HashSet<String> emojis = new HashSet<>();
int i = 0;
for(Symbol symbol : parse(input.toString())) {
if (symbol instanceof Emoji) {
emojis.add(symbol.toString());
if (++i >= MAX_EMOIJS) {
return Pattern.compile("");
}
}
}
final StringBuilder pattern = new StringBuilder();
for(String emoji : emojis) {
if (pattern.length() != 0) {
pattern.append('|');
}
pattern.append(Pattern.quote(emoji));
}
return Pattern.compile(pattern.toString());
}
private static Pattern generatePattern(CharSequence input) {
final HashSet<String> emojis = new HashSet<>();
int i = 0;
for (final Symbol symbol : parse(input.toString())) {
if (symbol instanceof Emoji) {
emojis.add(symbol.toString());
if (++i >= MAX_EMOIJS) {
return Pattern.compile("");
}
}
}
final StringBuilder pattern = new StringBuilder();
for (String emoji : emojis) {
if (pattern.length() != 0) {
pattern.append('|');
}
pattern.append(Pattern.quote(emoji));
}
return Pattern.compile(pattern.toString());
}
public static boolean isEmoji(String input) {
List<Symbol> symbols = parse(input);
return symbols.size() == 1 && symbols.get(0).isEmoji();
}
public static boolean isEmoji(String input) {
List<Symbol> symbols = parse(input);
return symbols.size() == 1 && symbols.get(0).isEmoji();
}
public static boolean isOnlyEmoji(String input) {
List<Symbol> symbols = parse(input);
for(Symbol symbol : symbols) {
if (!symbol.isEmoji()) {
return false;
}
}
return symbols.size() > 0;
}
public static boolean isOnlyEmoji(String input) {
List<Symbol> symbols = parse(input);
for (Symbol symbol : symbols) {
if (!symbol.isEmoji()) {
return false;
}
}
return symbols.size() > 0;
}
private static abstract class Symbol {
private static abstract class Symbol {
private final String value;
private final String value;
public Symbol(List<Integer> codepoints) {
StringBuilder builder = new StringBuilder();
for(Integer codepoint : codepoints) {
builder.appendCodePoint(codepoint);
}
this.value = builder.toString();
}
Symbol(List<Integer> codepoints) {
final StringBuilder builder = new StringBuilder();
for (final Integer codepoint : codepoints) {
builder.appendCodePoint(codepoint);
}
this.value = builder.toString();
}
abstract boolean isEmoji();
abstract boolean isEmoji();
@Override
public String toString() {
return value;
}
}
@NonNull
@Override
public String toString() {
return value;
}
}
public static class Emoji extends Symbol {
public static class Emoji extends Symbol {
public Emoji(List<Integer> codepoints) {
super(codepoints);
}
Emoji(List<Integer> codepoints) {
super(codepoints);
}
@Override
boolean isEmoji() {
return true;
}
}
@Override
boolean isEmoji() {
return true;
}
}
public static class Other extends Symbol {
public static class Other extends Symbol {
public Other(List<Integer> codepoints) {
super(codepoints);
}
public Other(List<Integer> codepoints) {
super(codepoints);
}
@Override
boolean isEmoji() {
return false;
}
}
@Override
boolean isEmoji() {
return false;
}
}
private static class Builder {
private final List<Integer> codepoints = new ArrayList<>();
private static class Builder {
private final List<Integer> codepoints = new ArrayList<>();
public boolean offer(int codepoint) {
boolean add = false;
if (this.codepoints.size() == 0) {
if (SYMBOLIZE.contains(codepoint)) {
add = true;
} else if (REGIONAL_INDICATORS.contains(codepoint)) {
add = true;
} else if (EMOJIS.contains(codepoint) && !FITZPATRICK.contains(codepoint) && codepoint != ZWJ) {
add = true;
}
} else {
int previous = codepoints.get(codepoints.size() -1);
if (codepoints.get(0) == BLACK_FLAG) {
add = TAGS.contains(codepoint);
} else if (COMBINING_ENCLOSING_KEYCAP == codepoint) {
add = KEYCAP_COMBINEABLE.contains(previous) || previous == VARIATION_16;
} else if (SYMBOLIZE.contains(previous)) {
add = codepoint == VARIATION_16;
} else if (REGIONAL_INDICATORS.contains(previous) && REGIONAL_INDICATORS.contains(codepoint)) {
add = codepoints.size() == 1;
} else if (previous == VARIATION_16) {
add = isMerger(codepoint);
} else if (FITZPATRICK.contains(previous)) {
add = codepoint == ZWJ;
} else if (ZWJ == previous) {
add = EMOJIS.contains(codepoint);
} else if (isMerger(codepoint)) {
add = true;
} else if (codepoint == VARIATION_16 && EMOJIS.contains(previous)) {
add = true;
}
}
if (add) {
codepoints.add(codepoint);
return true;
} else {
return false;
}
}
public boolean offer(int codepoint) {
boolean add = false;
if (this.codepoints.size() == 0) {
if (SYMBOLIZE.contains(codepoint)) {
add = true;
} else if (REGIONAL_INDICATORS.contains(codepoint)) {
add = true;
} else if (EMOJIS.contains(codepoint) && !FITZPATRICK.contains(codepoint) && codepoint != ZWJ) {
add = true;
}
} else {
int previous = codepoints.get(codepoints.size() - 1);
if (codepoints.get(0) == BLACK_FLAG) {
add = TAGS.contains(codepoint);
} else if (COMBINING_ENCLOSING_KEYCAP == codepoint) {
add = KEYCAP_COMBINEABLE.contains(previous) || previous == VARIATION_16;
} else if (SYMBOLIZE.contains(previous)) {
add = codepoint == VARIATION_16;
} else if (REGIONAL_INDICATORS.contains(previous) && REGIONAL_INDICATORS.contains(codepoint)) {
add = codepoints.size() == 1;
} else if (previous == VARIATION_16) {
add = isMerger(codepoint) || codepoint == VARIATION_16;
} else if (FITZPATRICK.contains(previous)) {
add = codepoint == ZWJ;
} else if (ZWJ == previous) {
add = EMOJIS.contains(codepoint);
} else if (isMerger(codepoint)) {
add = true;
} else if (codepoint == VARIATION_16 && EMOJIS.contains(previous)) {
add = true;
}
}
if (add) {
codepoints.add(codepoint);
return true;
} else {
return false;
}
}
private static boolean isMerger(int codepoint) {
return codepoint == ZWJ || FITZPATRICK.contains(codepoint);
}
private static boolean isMerger(int codepoint) {
return codepoint == ZWJ || FITZPATRICK.contains(codepoint);
}
public Symbol build() {
if (codepoints.size() > 0 && SYMBOLIZE.contains(codepoints.get(codepoints.size() - 1))) {
return new Other(codepoints);
} else if (codepoints.size() > 1 && KEYCAP_COMBINEABLE.contains(codepoints.get(0)) && codepoints.get(codepoints.size() - 1) != COMBINING_ENCLOSING_KEYCAP) {
return new Other(codepoints);
}
return codepoints.size() == 0 ? new Other(codepoints): new Emoji(codepoints);
}
}
public Symbol build() {
if (codepoints.size() > 0 && SYMBOLIZE.contains(codepoints.get(codepoints.size() - 1))) {
return new Other(codepoints);
} else if (codepoints.size() > 1 && KEYCAP_COMBINEABLE.contains(codepoints.get(0)) && codepoints.get(codepoints.size() - 1) != COMBINING_ENCLOSING_KEYCAP) {
return new Other(codepoints);
}
return codepoints.size() == 0 ? new Other(codepoints) : new Emoji(codepoints);
}
}
public static class UnicodeBlocks implements UnicodeSet {
final UnicodeSet[] unicodeSets;
public static class UnicodeBlocks implements UnicodeSet {
final UnicodeSet[] unicodeSets;
public UnicodeBlocks(UnicodeSet... sets) {
this.unicodeSets = sets;
}
UnicodeBlocks(final UnicodeSet... sets) {
this.unicodeSets = sets;
}
@Override
public boolean contains(int codepoint) {
for(UnicodeSet unicodeSet : unicodeSets) {
if (unicodeSet.contains(codepoint)) {
return true;
}
}
return false;
}
}
@Override
public boolean contains(int codepoint) {
for (UnicodeSet unicodeSet : unicodeSets) {
if (unicodeSet.contains(codepoint)) {
return true;
}
}
return false;
}
}
public interface UnicodeSet {
boolean contains(int codepoint);
}
public interface UnicodeSet {
boolean contains(int codepoint);
}
public static class UnicodeList implements UnicodeSet {
public static class UnicodeList implements UnicodeSet {
private final List<Integer> list;
private final List<Integer> list;
public UnicodeList(Integer... codes) {
this.list = Arrays.asList(codes);
}
UnicodeList(final Integer... codes) {
this.list = Arrays.asList(codes);
}
@Override
public boolean contains(int codepoint) {
return this.list.contains(codepoint);
}
}
@Override
public boolean contains(int codepoint) {
return this.list.contains(codepoint);
}
}
public static class UnicodeRange implements UnicodeSet {
public static class UnicodeRange implements UnicodeSet {
private final int lower;
private final int upper;
private final int lower;
private final int upper;
UnicodeRange(int lower, int upper) {
this.lower = lower;
this.upper = upper;
}
UnicodeRange(int lower, int upper) {
this.lower = lower;
this.upper = upper;
}
public boolean contains(int codePoint) {
return codePoint >= lower && codePoint <= upper;
}
}
public boolean contains(int codePoint) {
return codePoint >= lower && codePoint <= upper;
}
}
}

View File

@ -1085,8 +1085,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return webRTCWrapper.isMicrophoneEnabled();
}
public void setMicrophoneEnabled(final boolean enabled) {
webRTCWrapper.setMicrophoneEnabled(enabled);
public boolean setMicrophoneEnabled(final boolean enabled) {
return webRTCWrapper.setMicrophoneEnabled(enabled);
}
public boolean isVideoEnabled() {

View File

@ -370,12 +370,19 @@ public class WebRTCWrapper {
}
}
void setMicrophoneEnabled(final boolean enabled) {
boolean setMicrophoneEnabled(final boolean enabled) {
final AudioTrack audioTrack = this.localAudioTrack;
if (audioTrack == null) {
throw new IllegalStateException("Local audio track does not exist (yet)");
}
audioTrack.setEnabled(enabled);
try {
audioTrack.setEnabled(enabled);
return true;
} catch (final IllegalStateException e) {
Log.d(Config.LOGTAG, "unable to toggle microphone", e);
//ignoring race condition in case MediaStreamTrack has been disposed
return false;
}
}
boolean isVideoEnabled() {
@ -502,7 +509,7 @@ public class WebRTCWrapper {
final CameraEnumerator enumerator = getCameraEnumerator();
final Set<String> deviceNames = ImmutableSet.copyOf(enumerator.getDeviceNames());
for (final String deviceName : deviceNames) {
if (enumerator.isFrontFacing(deviceName)) {
if (isFrontFacing(enumerator, deviceName)) {
final CapturerChoice capturerChoice = of(enumerator, deviceName, deviceNames);
if (capturerChoice == null) {
return Optional.absent();
@ -518,6 +525,14 @@ public class WebRTCWrapper {
}
}
private static boolean isFrontFacing(final CameraEnumerator cameraEnumerator, final String deviceName) {
try {
return cameraEnumerator.isFrontFacing(deviceName);
} catch (final NullPointerException e) {
return false;
}
}
public PeerConnection.PeerConnectionState getState() {
return requirePeerConnection().connectionState();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

View File

@ -1,11 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_help"
android:icon="?attr/icon_help"
android:title="@string/help"
app:showAsAction="always"/>
app:showAsAction="always" />
<item
android:id="@+id/action_goto_chat"
android:icon="?attr/icon_goto_chat"
android:title="@string/switch_to_conversation"
app:showAsAction="always" />
</menu>

View File

@ -316,7 +316,6 @@
<string name="reply">الرد</string>
<string name="mark_as_read">إعتباره كمقروء</string>
<string name="pref_enter_is_send">أدخل للإرسال</string>
<string name="pref_enter_is_send_summary">استخدام مفتاح الدخول لإرسال رسالة</string>
<string name="pref_display_enter_key">عرض مفتاح الادخال</string>
<string name="pref_display_enter_key_summary">تغيير مفتاح الرموز إلى مفتاح الدخول</string>
<string name="audio">صوت</string>

View File

@ -316,7 +316,6 @@
<string name="mark_as_read">Отбелязване като прочетено</string>
<string name="pref_input_options">Въвеждане</string>
<string name="pref_enter_is_send">Enter изпраща</string>
<string name="pref_enter_is_send_summary">Натискането на клавиша Enter изпраща съобщението</string>
<string name="pref_display_enter_key">Показване на клавиша Enter</string>
<string name="pref_display_enter_key_summary">Смяна на клавиша за емотикони с клавиша Enter</string>
<string name="audio">аудио</string>

View File

@ -314,7 +314,6 @@
<string name="mark_as_read">Marcar com llegit</string>
<string name="pref_input_options">Entrada</string>
<string name="pref_enter_is_send">Entra per enviar</string>
<string name="pref_enter_is_send_summary">Utilitza el botó enter per enviar el missatge</string>
<string name="pref_display_enter_key">Mostra el botó enter</string>
<string name="pref_display_enter_key_summary">Canviar la clau dels emoticones per un botó d\'entrada </string>
<string name="audio">audio</string>

View File

@ -264,7 +264,6 @@
<string name="until_further_notice">Než opět změním</string>
<string name="pref_input_options">Vstup</string>
<string name="pref_enter_is_send">Enter odesílá</string>
<string name="pref_enter_is_send_summary">Použít klávesu enter pro odesílání zpráv</string>
<string name="pref_display_enter_key">Zobrazit klávesu enter</string>
<string name="pref_display_enter_key_summary">Změnit klávesu emotikon na klávesu enter</string>
<string name="audio">audio</string>

View File

@ -400,10 +400,10 @@
<string name="reply">Antworten</string>
<string name="mark_as_read">Als gelesen markieren</string>
<string name="pref_input_options">Eingabe</string>
<string name="pref_enter_is_send">Eingabe-Taste (Enter) sendet Nachricht</string>
<string name="pref_enter_is_send_summary">Eingabe-Taste (Enter) zum Versenden einer Nachricht verwenden</string>
<string name="pref_display_enter_key">Zeige Eingabe-Taste (Enter)</string>
<string name="pref_display_enter_key_summary">Emoji-Taste durch Eingabe-Taste ersetzen</string>
<string name="pref_enter_is_send">Eingabetaste sendet Nachricht</string>
<string name="pref_enter_is_send_summary">Nutze die Eingabetaste zum Versenden einer Nachricht. Strg+Eingabetaste sendet die Nachricht unabhängig von dieser Einstellung.</string>
<string name="pref_display_enter_key">Zeige Eingabetaste</string>
<string name="pref_display_enter_key_summary">Emoji-Taste durch Eingabetaste ersetzen</string>
<string name="audio">Audio</string>
<string name="video">Video</string>
<string name="image">Bild</string>
@ -916,6 +916,7 @@
<string name="audio_call">Audioanruf</string>
<string name="video_call">Videoanruf</string>
<string name="help">Hilfe</string>
<string name="switch_to_conversation">Zur Unterhaltung wechseln</string>
<string name="microphone_unavailable">Dein Mikrofon ist nicht verfügbar</string>
<string name="only_one_call_at_a_time">Du kannst immer nur einen Anruf zur gleichen Zeit machen.</string>
<string name="return_to_ongoing_call">Zurück zum laufenden Aufruf</string>

View File

@ -347,7 +347,6 @@
<string name="mark_as_read">Σημείωμα ως αναγνωσμένο</string>
<string name="pref_input_options">Είσοδος</string>
<string name="pref_enter_is_send">Αποστολή με το πλήκτρο Enter</string>
<string name="pref_enter_is_send_summary">Χρήση του πλήκτρου Enter για την αποστολή μηνύματος</string>
<string name="pref_display_enter_key">Εμφάνιση του πλήκτρου Enter</string>
<string name="pref_display_enter_key_summary">Αλλαγή του πλήκτρου emoticons σε πλήκτρο Enter</string>
<string name="audio">ήχος</string>

View File

@ -401,7 +401,7 @@
<string name="mark_as_read">Marcar como leído</string>
<string name="pref_input_options">Entrada</string>
<string name="pref_enter_is_send">Intro para enviar</string>
<string name="pref_enter_is_send_summary">Usar la tecla intro para enviar el mensaje</string>
<string name="pref_enter_is_send_summary">Utilizar la tecla Enter para enviar un mensaje. Siempre puedes usar Ctrl+Enter para enviar un mensaje, incluso si esta opción está deshabilitada.</string>
<string name="pref_display_enter_key">Mostrar tecla Intro</string>
<string name="pref_display_enter_key_summary">Cambiar la tecla de emoticonos por la tecla Intro</string>
<string name="audio">audio</string>
@ -916,6 +916,7 @@
<string name="audio_call">Audio llamada</string>
<string name="video_call">Video llamada</string>
<string name="help">Ayuda</string>
<string name="switch_to_conversation">Cambiar a conversación</string>
<string name="microphone_unavailable">Tu micrófono no está disponible</string>
<string name="only_one_call_at_a_time">Solo puedes hacer una llamada a la vez</string>
<string name="return_to_ongoing_call">Volver a la llamada en curso</string>

View File

@ -344,7 +344,6 @@
<string name="mark_as_read">Irakurrita bezala markatu</string>
<string name="pref_input_options">Sarrera</string>
<string name="pref_enter_is_send">Sartu teklak bidaltzen du</string>
<string name="pref_enter_is_send_summary">Sartu tekla erabili mezua bidaltzeko</string>
<string name="pref_display_enter_key">Sartu tekla erakutsi</string>
<string name="pref_display_enter_key_summary">Aurpegieren tekla sartu teklarekin aldatu</string>
<string name="audio">audioa</string>

View File

@ -401,7 +401,6 @@
<string name="mark_as_read">Marquer comme lu</string>
<string name="pref_input_options">Saisie</string>
<string name="pref_enter_is_send">Touche Entrée pour envoyer</string>
<string name="pref_enter_is_send_summary">Utiliser la touche Entrée pour envoyer un message.</string>
<string name="pref_display_enter_key">Afficher la touche Entrée</string>
<string name="pref_display_enter_key_summary">Remplacer la touche Émoticônes par une touche Entrée.</string>
<string name="audio">audio</string>

View File

@ -401,7 +401,7 @@
<string name="mark_as_read">Marcar como lido</string>
<string name="pref_input_options">Entrada</string>
<string name="pref_enter_is_send">Enter envía</string>
<string name="pref_enter_is_send_summary">Utilizar a tecla Enter para enviar mensaxe</string>
<string name="pref_enter_is_send_summary">Usa a tecla Enter para enviar a mensaxe. Igualmente poderás usar Ctrl+Enter para enviar, incluso se esta opción está desactivada.</string>
<string name="pref_display_enter_key">Mostrar a tecla Enter</string>
<string name="pref_display_enter_key_summary">Cambiar a chave de emoticonas pola tecla Enter</string>
<string name="audio">son</string>
@ -916,6 +916,7 @@
<string name="audio_call">Chamada de audio</string>
<string name="video_call">Chamada de vídeo</string>
<string name="help">Axuda</string>
<string name="switch_to_conversation">Ir á conversa</string>
<string name="microphone_unavailable">O micrófono non está dispoñible</string>
<string name="only_one_call_at_a_time">Só podes manter unha chamada en cada momento.</string>
<string name="return_to_ongoing_call">Voltar á chamada activa</string>

View File

@ -384,7 +384,6 @@
<string name="mark_as_read">Megjelölés olvasottként</string>
<string name="pref_input_options">Bevitel</string>
<string name="pref_enter_is_send">Küldés enterrel</string>
<string name="pref_enter_is_send_summary">Az enter billentyű használata az üzenet küldéséhez</string>
<string name="pref_display_enter_key">Enter billentyű megjelenítése</string>
<string name="pref_display_enter_key_summary">Hangulatjelek billentyű megváltoztatása az enter billentyűre</string>
<string name="audio">hang</string>

View File

@ -226,7 +226,6 @@
<string name="until_further_notice">Sampai pemberitahuan selanjutnya</string>
<string name="pref_input_options">Masukan</string>
<string name="pref_enter_is_send">Enter untuk mengirim</string>
<string name="pref_enter_is_send_summary">Gunakan enter untuk mengrim pesan</string>
<string name="pref_display_enter_key">Tampilkan masukan kunci</string>
<string name="pref_display_enter_key_summary">Mengubah kunci emoji untuk memasukan kunci</string>
<string name="audio">audio</string>

View File

@ -401,7 +401,6 @@
<string name="mark_as_read">Segna come già letto</string>
<string name="pref_input_options">Input</string>
<string name="pref_enter_is_send">Invio spedisce</string>
<string name="pref_enter_is_send_summary">Il tasto invio spedisce il messaggio</string>
<string name="pref_display_enter_key">Mostra il tasto invio</string>
<string name="pref_display_enter_key_summary">Cambia il tasto delle faccine nel tasto di invio</string>
<string name="audio">audio</string>

View File

@ -220,7 +220,6 @@
<string name="never">לעולם לא</string>
<string name="until_further_notice">עד אחרית הימים</string>
<string name="pref_enter_is_send">לחצן Enter שולח את ההודעה</string>
<string name="pref_enter_is_send_summary">השתמש בלחצן ה-Enter כלחצן השליחה</string>
<string name="pref_display_enter_key">הראה את לחצן ה Enter</string>
<string name="pref_display_enter_key_summary">שנה את לחצן האימוג\'י ללחצן Enter</string>
<string name="audio">קול</string>

View File

@ -316,7 +316,6 @@
<string name="mark_as_read">既読にする</string>
<string name="pref_input_options">入力</string>
<string name="pref_enter_is_send">Enter は送信</string>
<string name="pref_enter_is_send_summary">Enter キーをメッセージの送信に使用します</string>
<string name="pref_display_enter_key">Enter キーを表示</string>
<string name="pref_display_enter_key_summary">絵文字キーを Enter キーに変更</string>
<string name="audio">オーディオ</string>

View File

@ -257,7 +257,6 @@
<string name="until_further_notice">나중에 알릴때까지 </string>
<string name="pref_input_options">입력</string>
<string name="pref_enter_is_send">엔터 키로 전송 </string>
<string name="pref_enter_is_send_summary">엔터 키로 메세지를 보냅니다 </string>
<string name="pref_display_enter_key">엔터 키 표시 </string>
<string name="pref_display_enter_key_summary">이모티콘 키를 엔터 키로 바꿉니다 </string>
<string name="audio">오디오 </string>

View File

@ -285,7 +285,6 @@
<string name="mark_as_read">Merk som lest</string>
<string name="pref_input_options">Inndata</string>
<string name="pref_enter_is_send">Enter-forsendelsesknapp</string>
<string name="pref_enter_is_send_summary">Bruk enter for å sende en melding</string>
<string name="pref_display_enter_key">Vis enter-tast</string>
<string name="pref_display_enter_key_summary">Endre smilefjas-tast til en enter-tast</string>
<string name="audio">lyd</string>

View File

@ -356,7 +356,6 @@
<string name="mark_as_read">Markeren als gelezen</string>
<string name="pref_input_options">Invoer</string>
<string name="pref_enter_is_send">Enter is versturen</string>
<string name="pref_enter_is_send_summary">Gebruik de enter-toets om berichten te versturen</string>
<string name="pref_display_enter_key">Toon enter-toets</string>
<string name="pref_display_enter_key_summary">Verander de emoticon-toets in een enter-toets</string>
<string name="audio">audio</string>

View File

@ -401,7 +401,6 @@
<string name="mark_as_read">Oznacz jako przeczytane</string>
<string name="pref_input_options">Ustawienia wprowadzania</string>
<string name="pref_enter_is_send">Enter wysyła</string>
<string name="pref_enter_is_send_summary">Używaj klawisza Enter do wysyłania wiadomości</string>
<string name="pref_display_enter_key">Pokaż klawisz Enter</string>
<string name="pref_display_enter_key_summary">Zamień klawisz emotikon na klawisz Enter</string>
<string name="audio">plik audio</string>

View File

@ -401,7 +401,6 @@
<string name="mark_as_read">Marcar como lida</string>
<string name="pref_input_options">Entrada</string>
<string name="pref_enter_is_send">Enter envia</string>
<string name="pref_enter_is_send_summary">Usa o botão Enter para enviar a mensagem.</string>
<string name="pref_display_enter_key">Exibir o botão Enter</string>
<string name="pref_display_enter_key_summary">Altera o botão de emoticons para um botão Enter.</string>
<string name="audio">áudio</string>

View File

@ -279,7 +279,6 @@
<string name="mark_as_read">Marcar como lida</string>
<string name="pref_input_options">Introdução</string>
<string name="pref_enter_is_send">O enter envia</string>
<string name="pref_enter_is_send_summary">Use o enter para enviar a mensagem</string>
<string name="pref_display_enter_key">Exibir tecla enter</string>
<string name="pref_display_enter_key_summary">Alterar a tecla dos emoticons para uma tecla enter</string>
<string name="audio">áudio</string>

View File

@ -401,7 +401,7 @@
<string name="mark_as_read">Marchează ca citit</string>
<string name="pref_input_options">Opțiuni introducere text</string>
<string name="pref_enter_is_send">ENTER trimite</string>
<string name="pref_enter_is_send_summary">Apasă tasta ENTER pentru a trimite mesajul</string>
<string name="pref_enter_is_send_summary">Apasă tasta Enter pentru a trimite mesajul. Puteți mereu apăsa Ctrl-Enter pentru a trimite mesajul, chiar dacă această opțiune nu este activată.</string>
<string name="pref_display_enter_key">Arată tasta ENTER</string>
<string name="pref_display_enter_key_summary">Preschimbă tasta emoticon în ENTER</string>
<string name="audio">audio</string>
@ -924,6 +924,7 @@
<string name="audio_call">Apel audio</string>
<string name="video_call">Apel video</string>
<string name="help">Ajutor</string>
<string name="switch_to_conversation">Comutare la discuție</string>
<string name="microphone_unavailable">Microfonul nu este disponibil</string>
<string name="only_one_call_at_a_time">Puteți avea un singur apel simultan.</string>
<string name="return_to_ongoing_call">Reveniți la apelul în curs</string>

View File

@ -121,11 +121,15 @@
<string name="pref_notification_sound_summary">Звук уведомления о новых сообщениях</string>
<string name="pref_call_ringtone_summary">Мелодия входящего звонка</string>
<string name="pref_notification_grace_period">Грейс-период</string>
<string name="pref_notification_grace_period_summary">Время, на которое уведомления будут отключены, когда вы пользуетесь аккаунтом на другом устройстве.</string>
<string name="pref_advanced_options">Дополнительно</string>
<string name="pref_never_send_crash">Не отправлять отчёты об ошибках</string>
<string name="pref_never_send_crash_summary">Отправляя отчеты об ошибках, вы помогаете разработке этого приложения</string>
<string name="pref_confirm_messages">Отчёты о получении</string>
<string name="pref_confirm_messages_summary">Позволяет вашим контактам видеть, когда вы получили и прочитали их сообщения</string>
<string name="pref_ui_options">Интерфейс</string>
<string name="openpgp_error">OpenKeychain вызвал ошибку.</string>
<string name="bad_key_for_encryption">Неподходящий ключ для шифрования.</string>
<string name="accept">Принять</string>
<string name="error">Произошла ошибка</string>
<string name="recording_error">Ошибка</string>
@ -137,8 +141,10 @@
<string name="attach_take_picture">Сделать снимок</string>
<string name="preemptively_grant">Удовлетворять запросы на подписки</string>
<string name="error_not_an_image_file">Выбранный файл не является изображением</string>
<string name="error_compressing_image">Не удалось конвертировать изображение</string>
<string name="error_file_not_found">Файл не найден</string>
<string name="error_io_exception">Общая ошибка ввода/вывода. Возможно, на устройстве недостаточно свободного места?</string>
<string name="error_security_exception_during_image_copy">У приложения, которым вы выбрали это изображение, недостаточно прав, чтобы прочитать этот файл.\n\n<small>Пожалуйста, используйте другой файловый менеджер, чтобы выбрать это изображение</small>.</string>
<string name="account_status_unknown">Неизвестен</string>
<string name="account_status_disabled">Временно отключён</string>
<string name="account_status_online">В сети</string>
@ -150,6 +156,8 @@
<string name="account_status_regis_fail">Регистрация не удалась</string>
<string name="account_status_regis_conflict">Имя пользователя уже используется</string>
<string name="account_status_regis_success">Регистрация завершена</string>
<string name="account_status_regis_not_sup">Сервер не поддерживает возможность регистрации</string>
<string name="account_status_regis_invalid_token">Неправильный токен регистрации</string>
<string name="account_status_tls_error">Не удалось согласовать TLS</string>
<string name="account_status_policy_violation">Нарушение правил</string>
<string name="account_status_incompatible_server">Несовместимый сервер</string>
@ -165,14 +173,17 @@
<string name="mgmt_account_publish_pgp">Анонсировать OpenPGP ключ</string>
<string name="unpublish_pgp">Удалить открытый ключ OpenPGP</string>
<string name="unpublish_pgp_message">Вы действительно хотите удалить ваш OpenPGP публичный ключ из опубликованных?\nВаши собеседники не смогут больше отправлять вам зашифрованные OpenPGP сообщения.</string>
<string name="openpgp_has_been_published">Публичный ключ OpenPGP опубликован.</string>
<string name="mgmt_account_enable">Включить аккаунт</string>
<string name="mgmt_account_are_you_sure">Вы уверены?</string>
<string name="mgmt_account_delete_confirm_text">Удаление аккаунта также удалит всю историю вашей переписки</string>
<string name="attach_record_voice">Запись голоса</string>
<string name="account_settings_jabber_id">XMPP-адрес</string>
<string name="block_jabber_id">Заблокировать XMPP-адрес</string>
<string name="account_settings_example_jabber_id">username@example.com</string>
<string name="password">Пароль</string>
<string name="invalid_jid">Недопустимый XMPP-адрес</string>
<string name="error_out_of_memory">Нехватка памяти. Изображение слишком большое</string>
<string name="add_phone_book_text">Вы хотите добавить %s в вашу адресную книгу?</string>
<string name="server_info_show_more">Информация о сервере</string>
<string name="server_info_mam">XEP-0313: Архив сообщений</string>
@ -181,6 +192,7 @@
<string name="server_info_blocking">XEP-0191: Команда блокирования</string>
<string name="server_info_roster_version">XEP-0237: Версии списков</string>
<string name="server_info_stream_management">XEP-0198: Управление потоками</string>
<string name="server_info_external_service_discovery">XEP-0215: Обнаружение внешних служб</string>
<string name="server_info_pep">XEP-0163: PEP (Аватары / OMEMO)</string>
<string name="server_info_http_upload">XEP-0363: Загрузка по HTTP</string>
<string name="server_info_push">XEP-0357: Push-уведомления</string>
@ -188,9 +200,14 @@
<string name="server_info_unavailable">недоступно</string>
<string name="missing_public_keys">Отсутствие анонсирования открытых ключей</string>
<string name="last_seen_now">Присутствие: только что</string>
<string name="last_seen_min">Присутствие: одну минуту назад</string>
<string name="last_seen_mins">Присутствие: %d мин. назад</string>
<string name="last_seen_hour">Присутствие: один час назад</string>
<string name="last_seen_hours">Присутствие: %d час. назад</string>
<string name="last_seen_day">Присутствие: один день назад</string>
<string name="last_seen_days">Присутствие: %d дн. назад</string>
<string name="install_openkeychain">Зашифрованное сообщение. Пожалуйста, установите OpenKeychain для дешифрования.</string>
<string name="openpgp_messages_found">Найдены новые OpenPGP зашифрованые сообщения</string>
<string name="openpgp_key_id">ID OpenPGP ключа</string>
<string name="omemo_fingerprint">OMEMO отпечаток</string>
<string name="omemo_fingerprint_x509">v\\OMEMO отпечаток</string>
@ -212,10 +229,14 @@
<string name="select">Выбрать</string>
<string name="contact_already_exists">Контакт уже существует</string>
<string name="join">Присоединиться</string>
<string name="channel_full_jid_example">канал@конференция.пример.com/никнейм</string>
<string name="channel_bare_jid_example">канал@конференция.пример.com</string>
<string name="save_as_bookmark">Сохранить закладку</string>
<string name="delete_bookmark">Удалить закладку</string>
<string name="destroy_room">Уничтожить конференцию</string>
<string name="destroy_channel">Уничтожить канал</string>
<string name="destroy_room_dialog">Вы уверены, что хотите распустить эту конференцию?\n\n<b>Предупреждение:</b>Конференция будет полностью удалена с сервера.</string>
<string name="destroy_channel_dialog">Вы уверены, что хотите закрыть этот публичный канал?\n\n<b>Предупреждение:</b> Канал будет полностью удален с сервера.</string>
<string name="could_not_destroy_room">Не удалось уничтожить конференцию</string>
<string name="could_not_destroy_channel">Не удалось уничтожить канал</string>
<string name="action_edit_subject">Редактировать тему конференции</string>
@ -378,7 +399,6 @@
<string name="mark_as_read">Прочитано</string>
<string name="pref_input_options">Ввод</string>
<string name="pref_enter_is_send">Отправить на \"Enter\"</string>
<string name="pref_enter_is_send_summary">Клавиша \"Enter\" отправляет сообщение</string>
<string name="pref_display_enter_key">Показывать клавишу ввода</string>
<string name="pref_display_enter_key_summary">Поменять кнопку смайликов на кнопку ввода</string>
<string name="audio">аудио</string>
@ -393,7 +413,7 @@
<string name="hide_offline">Скрыть пользователей вне сети</string>
<string name="contact_is_typing">%s печатает…</string>
<string name="contact_has_stopped_typing">%s прекратил набор</string>
<string name="contacts_are_typing">%s печатают...</string>
<string name="contacts_are_typing">%s печатают</string>
<string name="contacts_have_stopped_typing">%s перестали печатать</string>
<string name="pref_chat_states">Оповещения о наборе</string>
<string name="pref_chat_states_summary">Позволяет вашим контактам видеть, когда вы пишете им новое сообщение</string>
@ -440,11 +460,11 @@
<string name="server_info_broken">Повреждено</string>
<string name="pref_presence_settings">Доступность</string>
<string name="pref_away_when_screen_off">\"Отошёл\" когда экран выключен</string>
<string name="pref_away_when_screen_off_summary">Отмечает ваш ресурс как «отошёл» когда экран выключен</string>
<string name="pref_away_when_screen_off_summary">Устанавливает статус \"Отошёл\", когда экран выключен</string>
<string name="pref_dnd_on_silent_mode">\"Не беспокоить\" в беззвучном режиме</string>
<string name="pref_dnd_on_silent_mode_summary">Помечать ресурс как \"Не беспокоить\", когда устройство в беззвучном режиме</string>
<string name="pref_dnd_on_silent_mode_summary">Устанавливает статус \"Не беспокоить\", когда устройство в беззвучном режиме</string>
<string name="pref_treat_vibrate_as_silent">Не доступен в режиме вибрации</string>
<string name="pref_treat_vibrate_as_dnd_summary">Помечать ресурс как \"Не беспокоить\", когда устройство на вибрации</string>
<string name="pref_treat_vibrate_as_dnd_summary">Устанавливает статус \"Не беспокоить\", когда устройство на вибрации</string>
<string name="pref_show_connection_options">Расширенные настройки подключения</string>
<string name="pref_show_connection_options_summary">Показывать имя сервера и порт в настройках аккаунтов</string>
<string name="hostname_example">xmpp.example.com</string>
@ -674,6 +694,7 @@
<string name="mtm_hostname_mismatch">Серверу не удалось аутентифицироваться в качестве \"%s\". Сертификат подходит только для:</string>
<string name="mtm_connect_anyway">Вы все равно хотите подключиться?</string>
<string name="mtm_cert_details">Детали сертификата:</string>
<string name="once">Один раз</string>
<string name="qr_code_scanner_needs_access_to_camera">Сканеру QR-кода необходим доступ к камере</string>
<string name="pref_scroll_to_bottom">Прокручивать вниз</string>
<string name="pref_scroll_to_bottom_summary">Прокручивать вниз после отправки сообщения</string>
@ -707,6 +728,7 @@
<string name="action_unfix_from_location">Открепить позицию</string>
<string name="action_copy_location">Копировать местоположение</string>
<string name="action_share_location">Поделиться местоположением</string>
<string name="action_directions">Направления</string>
<string name="title_activity_share_location">Поделиться местоположением</string>
<string name="title_activity_show_location">Показать местоположение</string>
<string name="share">Поделиться</string>
@ -782,8 +804,8 @@
<string name="abort_registration_procedure">Вы уверены, что хотите прервать процедуру регистрации?</string>
<string name="yes">Да</string>
<string name="no">Нет</string>
<string name="verifying">Подтверждение...</string>
<string name="requesting_sms">Запрос SMS...</string>
<string name="verifying">Подтверждение</string>
<string name="requesting_sms">Запрос SMS</string>
<string name="incorrect_pin">Введенный вами код некорректен.</string>
<string name="pin_expired">Отправленный вам код просрочен.</string>
<string name="unknown_api_error_network">Неизвестная ошибка сети.</string>
@ -799,6 +821,7 @@
<string name="rate_limited">У вас есть ограничение скорости</string>
<string name="too_many_attempts">Слишком много попыток</string>
<string name="the_app_is_out_of_date">Вы используете устаревшую версию приложения</string>
<string name="update">Обновить</string>
<string name="logged_in_with_another_device">Этот номер телефона в данный момент авторизирован на другом устройстве.</string>
<string name="enter_your_name_instructions">Пожалуйста, введите ваше имя, чтобы другие люди, у которых нет вас в списке контактов, знали кто вы.</string>
<string name="your_name">Ваше имя</string>
@ -811,7 +834,7 @@
<string name="group_chat_will_make_your_jabber_id_public">Этот канал сделает ваш XMPP-адрес публичным</string>
<string name="ebook">Электронная книга</string>
<string name="video_original">Оригинал (без сжатия)</string>
<string name="open_with">Открыть с помощью...</string>
<string name="open_with">Открыть с помощью</string>
<string name="set_profile_picture">Картинка профиля Conversations</string>
<string name="choose_account">Выбрать аккаунт</string>
<string name="restore_backup">Восстановить из резервной копии</string>
@ -831,7 +854,7 @@
<string name="please_enter_name">Пожалуйста, предоставьте имя для канала</string>
<string name="please_enter_xmpp_address">Пожалуйста, предоставьте XMPP-адрес</string>
<string name="this_is_an_xmpp_address">Это XMPP-адрес. Пожалуйста, предоставьте имя.</string>
<string name="creating_channel">Создание публичного канала...</string>
<string name="creating_channel">Создание публичного канала</string>
<string name="channel_already_exists">Этот канал уже существует</string>
<string name="joined_an_existing_channel">Вы присоединились к существующему каналу</string>
<string name="unable_to_set_channel_configuration">Не удалось сохранить настройки канала</string>
@ -868,11 +891,12 @@
<string name="account_already_setup">Эта учетная запись уже настроена</string>
<string name="please_enter_password">Пожалуйста, введите пароль этой учетной записи</string>
<string name="unable_to_perform_this_action">Не удалось совершить это действие</string>
<string name="open_join_dialog">Присоединиться к публичному каналу...</string>
<string name="open_join_dialog">Присоединиться к публичному каналу…</string>
<string name="sharing_application_not_grant_permission">Приложение для обмена не предоставило право доступа к этому файлу.</string>
<string name="group_chats_and_channels"><![CDATA[Группы и каналы]]></string>
<string name="jabber_network">jabber.network</string>
<string name="local_server">Локальный сервер</string>
<string name="pref_channel_discovery_summary">Большиству пользователей следует выбрать jabber.network для лучших предложений от всей публичной экосистемы XMPP.</string>
<string name="pref_channel_discovery_summary">Большиству пользователей следует выбрать jabber.network для получения наиболее подходящих предложений от всей публичной экосистемы XMPP.</string>
<string name="pref_channel_discovery">Способ поиска каналов</string>
<string name="backup">Резервное копирование</string>
<string name="category_about">О</string>
@ -890,6 +914,8 @@
<string name="rtp_state_ringing">Вызов</string>
<string name="rtp_state_declined_or_busy">Занято</string>
<string name="rtp_state_connectivity_error">Не удалось установить соединение</string>
<string name="rtp_state_connectivity_lost_error">Соединение потеряно</string>
<string name="rtp_state_retracted">Вызов отклонен</string>
<string name="rtp_state_application_failure">Ошибка приложения</string>
<string name="hang_up">Завершить</string>
<string name="ongoing_call">Активный звонок</string>
@ -902,9 +928,18 @@
<string name="missed_call">Пропущен вызов</string>
<string name="audio_call">Аудиозвонок</string>
<string name="video_call">Видеозвонок</string>
<string name="help">Помощь</string>
<string name="switch_to_conversation">Переключиться на беседу</string>
<string name="microphone_unavailable">Микрофон недоступен</string>
<string name="only_one_call_at_a_time">Нельзя одновременно совершать больше одного звонка.</string>
<string name="return_to_ongoing_call">Вернуться к текущему звонку</string>
<string name="could_not_switch_camera">Не удалось переключить камеру</string>
<string name="add_to_favorites">Добавить в избранные</string>
<string name="remove_from_favorites">Убрать из избранных</string>
<plurals name="view_users">
<item quantity="one">Просмотр %1$d участника</item>
<item quantity="few">Просмотр %1$d участников</item>
<item quantity="many">Просмотр %1$d участников</item>
<item quantity="other">Просмотр %1$d участников</item>
</plurals>
</resources>

View File

@ -216,7 +216,6 @@
<string name="never">Nikdy</string>
<string name="until_further_notice">Až do odvolania</string>
<string name="pref_enter_is_send">Enter odosiela</string>
<string name="pref_enter_is_send_summary">Použiť klávesu enter na odoslanie správy</string>
<string name="pref_display_enter_key">Zobraziť klávesu enter</string>
<string name="pref_display_enter_key_summary">Zmeniť klávesu s emotikonmi na klávesu enter</string>
<string name="audio">audio</string>

View File

@ -296,7 +296,6 @@
<string name="mark_as_read">Означи прочитаним</string>
<string name="pref_input_options">Унос</string>
<string name="pref_enter_is_send">Ентер шаље</string>
<string name="pref_enter_is_send_summary">Користи Ентер тастер за слање порука</string>
<string name="pref_display_enter_key">Прикажи Ентер тастер</string>
<string name="pref_display_enter_key_summary">Промени тастер за емотиконе у ентер тастер</string>
<string name="audio">звук</string>

View File

@ -319,7 +319,6 @@
<string name="mark_as_read">Läsmarkera</string>
<string name="pref_input_options">Input</string>
<string name="pref_enter_is_send">Skicka med enter</string>
<string name="pref_enter_is_send_summary">Använd enter-knappen för att skicka meddelande</string>
<string name="pref_display_enter_key">Visa enter-knappen</string>
<string name="pref_display_enter_key_summary">Byt ut emoticons-tangenten mot en enter-tangent</string>
<string name="audio">ljud</string>

View File

@ -271,7 +271,6 @@
<string name="until_further_notice">İkinci bildirime kadar</string>
<string name="pref_input_options">Girdi</string>
<string name="pref_enter_is_send">Enter=gönder</string>
<string name="pref_enter_is_send_summary">İleti göndermek için \"enter\" tuşunu kullan</string>
<string name="pref_display_enter_key">\"Enter\" tuşunu göster</string>
<string name="pref_display_enter_key_summary">İfade simgesi tuşunu \"enter\" tuşu olarak değiştir</string>
<string name="audio">ses</string>

View File

@ -401,7 +401,6 @@
<string name="mark_as_read">Позначити як прочитане</string>
<string name="pref_input_options">Введення</string>
<string name="pref_enter_is_send">Enter для надсилання</string>
<string name="pref_enter_is_send_summary">Використовувати Enter для надсилання повідомлення</string>
<string name="pref_display_enter_key">Показувати кнопку Enter</string>
<string name="pref_display_enter_key_summary">Змінити клавішу емоційок на кнопку Enter</string>
<string name="audio">аудіо</string>
@ -932,6 +931,7 @@
<string name="audio_call">Голосовий виклик</string>
<string name="video_call">Відеовиклик</string>
<string name="help">Допомога</string>
<string name="switch_to_conversation">Перейти до розмови</string>
<string name="microphone_unavailable">Недоступний мікрофон</string>
<string name="only_one_call_at_a_time">Водночас можливо здійснювати лише один виклик.</string>
<string name="return_to_ongoing_call">Назад до активного виклику</string>

View File

@ -246,7 +246,6 @@
<string name="never">Chưa từng</string>
<string name="until_further_notice">Cho đến thông báo tiếp theo</string>
<string name="pref_enter_is_send">Bấm Enter để gửi</string>
<string name="pref_enter_is_send_summary">Bấm nút Enter để gửi tin nhắn</string>
<string name="pref_display_enter_key">Hiện nút Enter</string>
<string name="pref_display_enter_key_summary">Đổi nút biểu tượng cảm xúc thành nút Enter</string>
<string name="audio">âm thanh</string>

View File

@ -1,9 +1,10 @@
<resources>
<!-- 384dp is the screen width of the Nexus 4. Something like a Moto G is smaller but a Nexus 5X is larger -->
<!-- https://material.io/devices/ -->
<dimen name="fineprint_size">12sp</dimen>
<dimen name="audio_player_width">288dp</dimen>
<dimen name="avatar_on_details_screen_size">72dp</dimen>
<dimen name="media_size">64dp</dimen> <!-- ideally not larger than avatar_on_details_screen -->
<dimen name="sd_label_max_width">288dp</dimen>
<!-- 384dp is the screen width of the Nexus 4. Something like a Moto G is smaller but a Nexus 5X is larger -->
<!-- https://material.io/devices/ -->
<dimen name="fineprint_size">12sp</dimen>
<dimen name="audio_player_width">288dp</dimen>
<dimen name="image_preview_width">288dp</dimen>
<dimen name="avatar_on_details_screen_size">72dp</dimen>
<dimen name="media_size">64dp</dimen> <!-- ideally not larger than avatar_on_details_screen -->
<dimen name="sd_label_max_width">288dp</dimen>
</resources>

View File

@ -401,7 +401,6 @@
<string name="mark_as_read">标记为已读</string>
<string name="pref_input_options">输入</string>
<string name="pref_enter_is_send">点击回车发送</string>
<string name="pref_enter_is_send_summary">回车键发送消息</string>
<string name="pref_display_enter_key">显示回车键</string>
<string name="pref_display_enter_key_summary">将表情键改为回车键</string>
<string name="audio">音频</string>

View File

@ -260,7 +260,6 @@
<string name="until_further_notice">直到新的通知</string>
<string name="pref_input_options">輸入</string>
<string name="pref_enter_is_send">回車是發送</string>
<string name="pref_enter_is_send_summary">用 Enter 鍵來發送訊息</string>
<string name="pref_display_enter_key">顯示回車鍵</string>
<string name="pref_display_enter_key_summary">改變表情鍵為回車鍵</string>
<string name="audio">音訊</string>

View File

@ -97,6 +97,7 @@
<attr name="icon_remove" format="reference" />
<attr name="icon_search" format="reference" />
<attr name="icon_help" format="reference" />
<attr name="icon_goto_chat" format="reference" />
<attr name="icon_secure" format="reference" />
<attr name="icon_settings" format="reference" />
<attr name="icon_share" format="reference" />

View File

@ -10,6 +10,7 @@
</dimen> <!-- icon width (24dp) + 2 * image button padding -->
<dimen name="fineprint_size">11sp</dimen>
<dimen name="audio_player_width">224dp</dimen>
<dimen name="image_preview_width">224dp</dimen>
<dimen name="avatar_item_distance">16dp</dimen>
<dimen name="media_preview_size">80dp</dimen>

View File

@ -401,7 +401,7 @@
<string name="mark_as_read">Mark as read</string>
<string name="pref_input_options">Input</string>
<string name="pref_enter_is_send">Enter is send</string>
<string name="pref_enter_is_send_summary">Use enter key to send message</string>
<string name="pref_enter_is_send_summary">Use Enter key to send message. You can always use Ctrl+Enter to send message, even if this option is disabled.</string>
<string name="pref_display_enter_key">Show enter key</string>
<string name="pref_display_enter_key_summary">Change the emoticons key to an enter key</string>
<string name="audio">audio</string>
@ -918,6 +918,7 @@
<string name="audio_call">Audio call</string>
<string name="video_call">Video call</string>
<string name="help">Help</string>
<string name="switch_to_conversation">Switch to conversation</string>
<string name="microphone_unavailable">Your microphone is unavailable</string>
<string name="only_one_call_at_a_time">You can only have one call at a time.</string>
<string name="return_to_ongoing_call">Return to ongoing call</string>

View File

@ -115,6 +115,7 @@
<item name="icon_remove" type="reference">@drawable/ic_delete_black_24dp</item>
<item name="icon_search" type="reference">@drawable/ic_search_white_24dp</item>
<item name="icon_help" type="reference">@drawable/ic_help_white_24dp</item>
<item name="icon_goto_chat" type="reference">@drawable/ic_question_answer_white_24dp</item>
<item name="icon_secure" type="reference">@drawable/ic_lock_open_white_24dp</item>
<item name="icon_settings" type="reference">@drawable/ic_settings_black_24dp</item>
<item name="icon_share" type="reference">@drawable/ic_share_white_24dp</item>
@ -269,6 +270,7 @@
<item name="icon_remove" type="reference">@drawable/ic_delete_white_24dp</item>
<item name="icon_search" type="reference">@drawable/ic_search_white_24dp</item>
<item name="icon_help" type="reference">@drawable/ic_help_white_24dp</item>
<item name="icon_goto_chat" type="reference">@drawable/ic_question_answer_white_24dp</item>
<item name="icon_secure" type="reference">@drawable/ic_lock_open_white_24dp</item>
<item name="icon_settings" type="reference">@drawable/ic_settings_white_24dp</item>
<item name="icon_share" type="reference">@drawable/ic_share_white_24dp</item>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="crash_report_title">В Quicksy произошёл сбой</string>
<string name="crash_report_message">Отправляя отчёты об ошибках, вы помогаете исправить и улучшить Quicksy\n<b>Предупреждение:</b> Отчёт будет отправлен разработчику с вашего XMPP аккаунта. </string>
<string name="openkeychain_required_long">Conversations использует стороннее приложение под названием <b>OpenKeychain</b> для шифровки и расшифровки сообщений и управления открытыми ключами.\nПрограмма OpenKeychain распространяется под лицензией GPLv3 и доступна через F-Droid или Google Play.\n\n<small>(Пожалуйста, перезапустите Quicksy после установки.)</small></string>
<string name="contact_has_no_pgp_key">Quicksy не может зашифровать сообщение, потому что ваш собеседник не анонсирует свой открытый ключ.\n\n<small>Пожалуйста, попросите вашего собеседника настроить OpenPGP.</small></string>
<string name="contacts_have_no_pgp_keys">Quicksy не может зашифровать сообщение, потому что ваши собеседники не анонсируют их открытые ключи.\n\n<small>Пожалуйста, попросите ваших собеседников настроить OpenPGP.</small></string>
<string name="pref_notification_grace_period_summary">Время, на которое уведомления от Quicksy будут отключены, когда вы пользуетесь аккаунтом на другом устройстве.</string>
<string name="pref_never_send_crash_summary">Отправляя отчёты об ошибках, вы помогаете в разработке Quicksy</string>
<string name="no_storage_permission">Quicksy требуется доступ к основной файловой системе</string>
<string name="no_camera_permission">Quicksy требуется доступ к камере</string>
<string name="battery_optimizations_enabled_explained">Ваше устройство использует агрессивную оптимизацию энергопотребления Quicksy, что может привести к задержке уведомлений и даже потере сообщений.\nРекомендуем ее отключить.</string>
<string name="battery_optimizations_enabled_dialog">Ваше устройство использует агрессивную оптимизацию энергопотребления Quicksy, что может привести к задержке уведомлений и даже потере сообщений.\nСейчас появится предложение ее отключить.</string>
<string name="pref_broadcast_last_activity_summary">Извещать собеседников, когда вы пользуетесь Quicksy</string>
<string name="data_saver_enabled_explained">Ваша операционная система не позволяет Quicksy доступ в Интернет в фоновом режиме. Для получения уведомлений вы должны разрешить Quicksy неограниченный доступ в Интернет в режиме экономии трафика.\nQuicksy будет использовать настолько мало трафика, насколько это возможно.</string>
<string name="device_does_not_support_data_saver">Ваше устройство не позволяет отключить режим экономии трафика для Quicksy.</string>
<string name="huawei_protected_apps_summary">Чтобы продолжать получать уведомления, даже если экран выключен, вам необходимо добавить Quicksy в список защищенных приложений.</string>
<string name="error_trustkey_general">Quicksy не удалось отправить зашифрованные сообщения для %1$s. Причиной этому может быть использование получателем устаревшего сервера или приложения, которые не поддерживают OMEMO.</string>
<string name="no_microphone_permission">Quicksy требуется доступ к микрофону</string>
<string name="foreground_service_channel_description">Эта категория уведомлений используется для отображения постоянного уведомления о том что Quicksy работает.</string>
<string name="set_profile_picture">Аватар для Quicksy</string>
<string name="not_available_in_your_country">Quicksy не доступно в вашем регионе</string>
<string name="unable_to_verify_server_identity">Не удалось подтвердить сервер.</string>
<string name="unknown_security_error">Неизвестная ошибка безопасности.</string>
<string name="timeout_while_connecting_to_server">Время ожидания подключения к серверу вышло.</string>
</resources>

View File

@ -1,26 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="crash_report_title">Програма дала збій</string>
<string name="crash_report_message">Надсилаючи траси стеків виклику Ви допомагаєте розробці цієї програми\n<b>Увага:</b> Це використовуватиме ваш обліковий запис XMPP, щоб надсилати траси стеків виклику розробнику.</string>
<string name="openkeychain_required_long">Ця програма використовує сторонній додаток, який називається <b>OpenKeychain</b>, для шифрування повідомлень і впорядкування ваших публічних ключів.\n\nOpenKeychain поширюється на умова ліцензії GPLv3 й доступна в F-Droid та Google Play.\n\n<small>(Будь ласка, перезапустіть цю програму після цього.)</small></string>
<string name="contact_has_no_pgp_key">Ця програма не змогла зашифрувати повідомлення, оскільки публічний ключ адресата не опубліковано.\n\n<small>Будь ласка, попросіть адресата налаштувати OpenPGP.</small></string>
<string name="contacts_have_no_pgp_keys">Ця програма не змогла зашифрувати повідомлення, оскільки публічні ключі адресатів не опубліковано.\n\n<small>Будь ласка, попросіть їх налаштувати OpenPGP.</small></string>
<string name="pref_notification_grace_period_summary">Час, протягом якого ця програма дотримується тиші після активності на іншому пристрої.</string>
<string name="pref_never_send_crash_summary">Надсилаючи траси стеків виклику, ви допомагаєте розробці цієї програми.</string>
<string name="no_storage_permission">Ця програма потребує доступу до зовнішнього сховища</string>
<string name="no_camera_permission">Ця програма потребує доступу до камери</string>
<string name="crash_report_title">Відмова застосунку</string>
<string name="crash_report_message">Надсилаючи траси стеків виклику, ви допомагаєте у розробці цього застосунку.\n<b>Увага:</b> Це використовуватиме ваш обліковий запис XMPP, щоб надсилати траси стеків виклику розробнику.</string>
<string name="openkeychain_required_long">Цей застосунок використовує сторонній застосунок, який називається <b>OpenKeychain</b>, для шифрування повідомлень і впорядкування ваших публічних ключів.\n\nOpenKeychain поширюється на умова ліцензії GPLv3 й доступна в F-Droid та Google Play.\n\n<small>(Будь ласка, перезапустіть цю програму після цього.)</small></string>
<string name="contact_has_no_pgp_key">Застосунок не зміг зашифрувати повідомлення, оскільки публічний ключ адресата не опубліковано.\n\n<small>Будь ласка, попросіть адресата налаштувати OpenPGP.</small></string>
<string name="contacts_have_no_pgp_keys">Застосунок не зміг зашифрувати повідомлення, оскільки публічні ключі адресатів не опубліковано.\n\n<small>Будь ласка, попросіть їх налаштувати OpenPGP.</small></string>
<string name="pref_notification_grace_period_summary">Час, протягом якого застосунок дотримується тиші після активності на іншому пристрої.</string>
<string name="pref_never_send_crash_summary">Надсилаючи траси стеків виклику, ви допомагаєте розробці цього застосунку.</string>
<string name="no_storage_permission">Цей застосунок потребує доступу до зовнішнього сховища</string>
<string name="no_camera_permission">Цей застосунок потребує доступу до камери</string>
<string name="battery_optimizations_enabled_explained">Ваш пристрій використовує посилену оптимізацію батареї для Quicksy, що може призвести до затримок у отриманні сповіщень або навіть втраті повідомлень.\nlt Рекомендується вимкнути цю функцію.</string>
<string name="battery_optimizations_enabled_dialog">Ваш пристрій використовує посилену оптимізацію батареї для Quicksy, що може призвести до затримок у отриманні сповіщень або навіть втраті повідомлень.\n\n Ми вас попросимо вимкнути цю функцію.</string>
<string name="pref_broadcast_last_activity_summary">Дозволити всім вашим контактам знати, коли ви використовуєте цю програму.</string>
<string name="data_saver_enabled_explained">Операційна система обмежує цю програму в доступі до Інтернету, коли вона не на екрані. Щоб отримувати сповіщення про нові повідомлення, слід дозволити цій програмі доступ, коли ввімкнено заощадження трафіку.\nЦя програма все одно намагатиметься заощадити трафік, коли можливо.</string>
<string name="device_does_not_support_data_saver">Цей пристрій не надає можливості зробити виняток щодо заощадження трафіку для цієї програми.</string>
<string name="huawei_protected_apps_summary">Щоб продовжувати отримувати сповіщення, навіть коли екран вимкнуто, потрібно додати цю програму до списку захищених.</string>
<string name="error_trustkey_general">Програма не може надіслати зашифроване повідомлення до %1$s. Можливо, це обумовлено тим, що адресат користується застарілим сервером або програмою, яка не підтримує OMEMO.</string>
<string name="no_microphone_permission">Програма потребує доступу до мікрофона</string>
<string name="error_trustkey_general">Застосунок не може надіслати зашифроване повідомлення до %1$s. Можливо, це обумовлено тим, що адресат користується застарілим сервером або програмою, яка не підтримує OMEMO.</string>
<string name="no_microphone_permission">Цей застосунок потребує доступу до мікрофона</string>
<string name="foreground_service_channel_description">Цей вид сповіщень показує постійне сповіщення про те, що ця програма працює.</string>
<string name="set_profile_picture">Зображення профілю для Quicksy</string>
<string name="not_available_in_your_country">Ця програма не доступна у Вашій країні.</string>
<string name="unable_to_verify_server_identity">Автентичність сервера не підтверджено</string>
<string name="not_available_in_your_country">Цей застосунок не доступний у вашій країні.</string>
<string name="unable_to_verify_server_identity">Автентичність сервера не підтверджено.</string>
<string name="unknown_security_error">Невідома помилка безпеки.</string>
<string name="timeout_while_connecting_to_server">Вичерпано час для встановлення з\'єднання із сервером.</string>
</resources>