for 2.5.2 Merge branch 'master' into develop

This commit is contained in:
Martin/Geno 2019-05-25 00:43:16 +02:00
commit 14d4e6aec8
No known key found for this signature in database
GPG Key ID: 9D7D3C6BFF600C6A
16 changed files with 170 additions and 23 deletions

View File

@ -164,7 +164,7 @@ You can find a detailed description of how your server, the app server and FCM a
#### But why do I need a permanent notification if I use Google Push?
FCM (Google Push) allows an app to wake up from *Doze* which is (as the name suggests) a hibernation feature of the Android operating system that cuts the network connection and also reduces the number of times the app is allowed to wake up (to ping the server for example). The app can ask to be excluded from doze. Non push variants of the app (from F-Droid or if the server doesnt support it) will do this on first start up. So if you get exemption from *Doze*, or if you get regular push events sent to you, Doze should not pose a threat to Conversatons working properly. But even with *Doze* the app is still open in the background (kept in memory); it is just limited in the actions it can do. Conversations needs to stay in memory to hold certain session state (online status of concats, join status of group chats, …). However with Android 8 Google changed all of this again and now an App that wants to stay in memory needs to have a foreground service which is visible to the user via the annoying notification. But why does Conversations need to hold that state? XMPP is a stateful protocol that has a lot of per-session information; packets need to be counted, presence information needs to be held, some features like Message Carbons get activated once per session, MAM catchup happens once, service discovery happens only once; the list goes on. When Conversations was created in early 2014 none of this was a problem because apps were just allowed to stay in memory. Basically every XMPP client out there holds that information in memory because it would be a lot more complicated trying to persist it to disk. An entire rewrite of Conversations in the year 2019 would attempt to do that and would probably succeed however it would require exactly that; a complete rewrite which is not feasible right now. Thats by the way also the reason why it is difficult to write an XMPP client on iOS. Or more broadly put this is also the reason why other protocols are designed as or migrated to stateless protocols (often based on HTTP); take for example the migration of IMAP to [JMAP](https://jmap.io/).
FCM (Google Push) allows an app to wake up from *Doze* which is (as the name suggests) a hibernation feature of the Android operating system that cuts the network connection and also reduces the number of times the app is allowed to wake up (to ping the server for example). The app can ask to be excluded from doze. Non push variants of the app (from F-Droid or if the server doesnt support it) will do this on first start up. So if you get exemption from *Doze*, or if you get regular push events sent to you, Doze should not pose a threat to Conversatons working properly. But even with *Doze* the app is still open in the background (kept in memory); it is just limited in the actions it can do. Conversations needs to stay in memory to hold certain session state (online status of contacts, join status of group chats, …). However with Android 8 Google changed all of this again and now an App that wants to stay in memory needs to have a foreground service which is visible to the user via the annoying notification. But why does Conversations need to hold that state? XMPP is a stateful protocol that has a lot of per-session information; packets need to be counted, presence information needs to be held, some features like Message Carbons get activated once per session, MAM catchup happens once, service discovery happens only once; the list goes on. When Conversations was created in early 2014 none of this was a problem because apps were just allowed to stay in memory. Basically every XMPP client out there holds that information in memory because it would be a lot more complicated trying to persist it to disk. An entire rewrite of Conversations in the year 2019 would attempt to do that and would probably succeed however it would require exactly that; a complete rewrite which is not feasible right now. Thats by the way also the reason why it is difficult to write an XMPP client on iOS. Or more broadly put this is also the reason why other protocols are designed as or migrated to stateless protocols (often based on HTTP); take for example the migration of IMAP to [JMAP](https://jmap.io/).
#### Conversations doesnt work for me. Where can I get help?
@ -256,6 +256,10 @@ On the one hand Conversations supports Message Archive Management to keep a serv
As of version 2.4.0 an integrated Backup & Restore function will help with this, go to Settings → Expert settings → Create backup. A notification will pop-up during the creation process that will announce you when it's ready. After the files, one for each account, are created, you can move the **Conversations** folder *(if you want your old media files too)* or only the **Conversations/Backup** folder *(for OMEMO keys and history only)* to your new device (or to a storage place) where a freshly installed Conversations can restore each account. Don't forget to enable the accounts after a succesful restore.
This backup method will include your OMEMO keys. Due to forward secrecy you will not be able to recover messages sent and received between creating the backup and restoring it. If you have a server side archive (MAM) those messages will be retrieved but displayed as *unable to decrypt*. For technical reasons you might also lose the first message you either sent or receive after the restore; for each conversation you have. This message will then also show up as *unable to decrypt*, but this will automatically recover itself as long as both participants are on Conversations 2.3.11+. Note that this doesnt happen if you just transfer to a new phone and no messages have been exchanged between backup and restore.
In the vast, vast majority of cases you wont have to manually delete OMEMO keys or do anything like that. Conversations only introduced the offical backup feature in 2.4.0 after making sure the *OMEMO self healing* mechanism introduced in 2.3.11 works fine.
**WARNING**: Be sure to know your accounts passwords or find ways to reset them **before** doing the backup as the files are encrypted using those passwords and the Restore process will ask for them.
**WARNING**: Do not use the restore backup feature in an attempt to clone (run simultaneously) an installation. Restoring a backup is only meant for migrations or in case youve lost the original device.

View File

@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
classpath 'com.android.tools.build:gradle:3.4.1'
}
}
@ -81,8 +81,8 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 327
versionName "2.5.1"
versionCode 329
versionName "2.5.2"
archivesBaseName += "-$versionName"
applicationId "eu.sum7.conversations"
resValue "string", "applicationId", applicationId

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pick_a_server">Виберіть постачальника послуг обміну повідомленнями XMPP</string>
<string name="use_conversations.im">Скористатися conversations.im</string>
<string name="create_new_account">Створити новий обліковий запис</string>
<string name="do_you_have_an_account">Вже маєте обліковий запис XMPP? Можливо, користуєтеся іншою програмою XMPP або користувалися цією програмою раніше. Якщо ні, можете створити новий обліковий запис XMPP просто зараз.\nЗверніть увагу: Деякі постачальники електронної пошти водночас надають облікові записи XMPP.</string>
<string name="server_select_text">XMPP — це мережа обміну повідомленнями, незалежна від постачальників. Можете використовувати цю програму з будь-яким XMPP сервером, який оберете.\nПроте, для зручності, ми спростили створення облікового запису на conversations.im¹ — в постачальника, який спеціально налаштований на роботу з цією програмою.</string>
</resources>

View File

@ -576,7 +576,9 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
values.put(CREATED, created);
values.put(STATUS, status);
values.put(MODE, mode);
values.put(ATTRIBUTES, attributes.toString());
synchronized (this.attributes) {
values.put(ATTRIBUTES, attributes.toString());
}
return values;
}

View File

@ -94,7 +94,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
mMenuSearchView.expandActionView();
mSearchEditText.append(initialSearchValue);
mSearchEditText.requestFocus();
if (optedIn) {
if (optedIn && xmppConnectionService != null) {
xmppConnectionService.discoverChannels(initialSearchValue, this);
}
}

View File

@ -436,6 +436,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
private void updateView() {
invalidateOptionsMenu();
if (mConversation == null) {
return;
}
final MucOptions mucOptions = mConversation.getMucOptions();
final User self = mucOptions.getSelf();
String account;

View File

@ -2254,7 +2254,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
public void updateSendButton() {
boolean hasAttachments = mediaPreviewAdapter != null && mediaPreviewAdapter.hasAttachments();
boolean useSendButtonToIndicateStatus = PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("send_button_status", getResources().getBoolean(R.bool.send_button_status));
boolean useSendButtonToIndicateStatus = activity != null && PreferenceManager.getDefaultSharedPreferences(activity).getBoolean("send_button_status", getResources().getBoolean(R.bool.send_button_status));
final Conversation c = this.conversation;
final Presence.Status status;
final String text = this.binding.textinput == null ? "" : this.binding.textinput.getText().toString();
@ -2265,7 +2265,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
action = SendButtonTool.getAction(getActivity(), c, text);
}
if (useSendButtonToIndicateStatus && c.getAccount().getStatus() == Account.State.ONLINE) {
if (activity.xmppConnectionService != null && activity.xmppConnectionService.getMessageArchiveService().isCatchingUp(c)) {
if (activity != null && activity.xmppConnectionService != null && activity.xmppConnectionService.getMessageArchiveService().isCatchingUp(c)) {
status = Presence.Status.OFFLINE;
} else if (c.getMode() == Conversation.MODE_SINGLE) {
status = c.getContact().getShownStatus();
@ -2606,7 +2606,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
service.sendChatState(conversation);
}
if (storeNextMessage()) {
runOnUiThread(() -> activity.onConversationsListItemUpdated());
runOnUiThread(() -> {
if (activity == null) {
return;
}
activity.onConversationsListItemUpdated();
});
}
runOnUiThread(this::updateSendButton);
}

View File

@ -15,6 +15,7 @@ import android.widget.ArrayAdapter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import eu.siacs.conversations.Config;
@ -41,6 +42,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
private static final String SANITY_CHECK_JID = "sanity_check_jid";
private KnownHostsAdapter knownHostsAdapter;
private Collection<String> whitelistedDomains = Collections.emptyList();
private EnterJidDialogBinding binding;
private AlertDialog dialog;
@ -155,19 +157,19 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
try {
contactJid = Jid.of(binding.jid.getText().toString());
} catch (final IllegalArgumentException e) {
binding.jid.setError(getActivity().getString(R.string.invalid_jid));
binding.jidLayout.setError(getActivity().getString(R.string.invalid_jid));
return;
}
if (!issuedWarning && sanityCheckJid) {
if (contactJid.isDomainJid()) {
binding.jid.setError(getActivity().getString(R.string.this_looks_like_a_domain));
binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_a_domain));
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
issuedWarning = true;
return;
}
if (suspiciousSubDomain(contactJid.getDomain())) {
binding.jid.setError(getActivity().getString(R.string.this_looks_like_channel));
binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_channel));
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
issuedWarning = true;
return;
@ -180,7 +182,9 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
dialog.dismiss();
}
} catch (JidError error) {
binding.jid.setError(error.toString());
binding.jidLayout.setError(error.toString());
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
issuedWarning = false;
}
}
}
@ -199,6 +203,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
if (activity instanceof XmppActivity) {
Collection<String> hosts = ((XmppActivity) activity).xmppConnectionService.getKnownHosts();
this.knownHostsAdapter.refresh(hosts);
this.whitelistedDomains = hosts;
}
}
@ -216,6 +221,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
public void afterTextChanged(Editable s) {
if (issuedWarning) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add);
binding.jidLayout.setError(null);
issuedWarning = false;
}
}
@ -245,7 +251,10 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
super.onDestroyView();
}
private static boolean suspiciousSubDomain(String domain) {
private boolean suspiciousSubDomain(String domain) {
if (this.whitelistedDomains.contains(domain)) {
return false;
}
final String[] parts = domain.split("\\.");
return parts.length >= 3 && SUSPICIOUS_DOMAINS.contains(parts[0]);
}

View File

@ -25,7 +25,6 @@ import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.AsyncTask;
@ -39,7 +38,6 @@ import android.preference.PreferenceManager;
import android.support.annotation.BoolRes;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AlertDialog.Builder;
import android.support.v7.app.AppCompatDelegate;
@ -393,7 +391,6 @@ public abstract class XmppActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setVolumeControlStream(AudioManager.STREAM_NOTIFICATION);
metrics = getResources().getDisplayMetrics();
ExceptionHelper.init(getApplicationContext());
new EmojiService(this).init();

View File

@ -23,6 +23,8 @@ import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@ -46,6 +48,8 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
private final Sensor proximitySensor;
private final PendingItem<WeakReference<ImageButton>> pendingOnClickView = new PendingItem<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final Handler handler = new Handler();
public AudioPlayer(MessageAdapter adapter) {
@ -87,7 +91,7 @@ public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompleti
audioPlayer.setTag(message);
if (init(ViewHolder.get(audioPlayer), message)) {
this.audioPlayerLayouts.addWeakReferenceTo(audioPlayer);
this.stopRefresher(true);
executor.execute(()-> this.stopRefresher(true));
} else {
this.audioPlayerLayouts.removeWeakReferenceTo(audioPlayer);
}

View File

@ -60,6 +60,10 @@ public class JingleCandidate {
}
public void setType(String type) {
if (type == null) {
this.type = TYPE_UNKNOWN;
return;
}
switch (type) {
case "proxy":
this.type = TYPE_PROXY;

View File

@ -23,7 +23,7 @@
android:layout_height="wrap_content"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/account_jid_layout"
android:id="@+id/jid_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/account_settings_jabber_id"

View File

@ -852,14 +852,14 @@
<string name="search_participants">Buscar participantes</string>
<string name="file_too_large">Ficheiro demasiado grande</string>
<string name="attach">Anexar</string>
<string name="discover_channels">Descubrir canles</string>
<string name="search_channels">Buscar canles</string>
<string name="discover_channels">Descubrir canales</string>
<string name="search_channels">Buscar canales</string>
<string name="channel_discovery_opt_in_title">Posible intrusión na intimidade!</string>
<string name="channel_discover_opt_in_message"><![CDATA[O descubrimento de canles utiliza un servizo de terceiros chamado <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Ao utilizar esta característica transmitirá o seu enderezo IP e os termos de busca a ese servizo. Lea a súa <a href="https://search.jabbercat.org/privacy">Política de Intimidade</a> para máis información.]]></string>
<string name="channel_discover_opt_in_message"><![CDATA[O descubrimento de canales utiliza un servizo de terceiros chamado <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Ao utilizar esta característica transmitirá o seu enderezo IP e os termos de busca a ese servizo. Lea a súa <a href="https://search.jabbercat.org/privacy">Política de Intimidade</a> para máis información.]]></string>
<string name="i_already_have_an_account">Xa teño unha conta</string>
<string name="add_existing_account">Engadir conta existente</string>
<string name="register_new_account">Rexistrar unha nova conta</string>
<string name="this_looks_like_a_domain">Esto semella un enderezo de dominio</string>
<string name="add_anway">Engadir igualmente</string>
<string name="this_looks_like_channel">Esto semella o enderezo de unha canle</string>
<string name="this_looks_like_channel">Esto semella o enderezo de un canal</string>
</resources>

View File

@ -7,6 +7,7 @@
<string name="action_end_conversation">この会話を閉じる</string>
<string name="action_contact_details">連絡先の詳細</string>
<string name="action_muc_details">談話室の詳細</string>
<string name="channel_details">チャンネルの詳細</string>
<string name="action_secure">安全に会話</string>
<string name="action_add_account">アカウントを追加</string>
<string name="action_edit_contact">名前の編集</string>
@ -171,8 +172,11 @@
<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>
@ -217,6 +221,8 @@
<string name="select">選択</string>
<string name="contact_already_exists">連絡先はすでに存在します</string>
<string name="join">参加</string>
<string name="channel_full_jid_example">channel@conference.example.com/nick</string>
<string name="channel_bare_jid_example">channel@conference.example.com</string>
<string name="save_as_bookmark">ブックマークとして保存</string>
<string name="delete_bookmark">ブックマークを削除</string>
<string name="destroy_room">グループチャットを破棄する</string>
@ -640,6 +646,7 @@
<string name="search_messages">メッセージを検索</string>
<string name="gif">GIF</string>
<string name="copy_link">ウェブアドレスをコピー</string>
<string name="copy_jabber_id">XMPP アドレスをコピー</string>
<string name="yes">はい</string>
<string name="no">いいえ</string>
<string name="verifying">検証しています…</string>
@ -647,4 +654,8 @@
<string name="something_went_wrong_processing_your_request">要求の処理中に、何か問題が発生しました。</string>
<string name="reject_request">要求を拒否</string>
<string name="set_profile_picture">Conversations プロフィール写真</string>
<string name="enter_jabber_id">XMPP アドレスを入力</string>
<string name="create_dialog_channel_name">チャンネル名</string>
<string name="xmpp_address">XMPP アドレス</string>
<string name="channel_already_exists">このチャンネルは既に存在しています</string>
</resources>

View File

@ -7,6 +7,7 @@
<string name="action_end_conversation">Закрити цю розмову</string>
<string name="action_contact_details">Деталі контакту</string>
<string name="action_muc_details">Деталі групи</string>
<string name="channel_details">Деталі каналу</string>
<string name="action_secure">Захищена розмова</string>
<string name="action_add_account">Додати обліковий запис</string>
<string name="action_edit_contact">Редагувати ім\'я</string>
@ -51,6 +52,7 @@
<string name="share_with">Поділитися з…</string>
<string name="start_conversation">Почати розмову</string>
<string name="invite_contact">Запросити контакт</string>
<string name="invite">Запросити</string>
<string name="contacts">Контакти</string>
<string name="contact">Контакт</string>
<string name="cancel">Скасувати</string>
@ -210,6 +212,7 @@
<string name="fetching_keys">Отримую ключі…</string>
<string name="done">Зроблено</string>
<string name="decrypt">Розшифрувати</string>
<string name="bookmarks">Закладки</string>
<string name="search">Пошук</string>
<string name="enter_contact">Увести контакт</string>
<string name="delete_contact">Видалити контакт</string>
@ -220,12 +223,17 @@
<string name="select">Вибрати</string>
<string name="contact_already_exists">Контакт уже існує</string>
<string name="join">Долучитися</string>
<string name="channel_full_jid_example">channel@conference.example.com/nick</string>
<string name="channel_bare_jid_example">channel@conference.example.com</string>
<string name="save_as_bookmark">Зберегти як закладку</string>
<string name="delete_bookmark">Видалити закладку</string>
<string name="destroy_room">Видалити груповий чат</string>
<string name="destroy_channel">Знищити канал</string>
<string name="destroy_room_dialog">Ви впевнені що бажаєте видалити цей груповий чат?\n\n 1
Увага: 1 Груповий чат буде видалений з сервера.</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="bookmark_already_exists">Ця закладка вже існує</string>
<string name="action_edit_subject">Редагувати тему групи</string>
<string name="topic">Тема</string>
@ -298,6 +306,7 @@
<string name="send_again">Надіслати знову</string>
<string name="file_url">URL файла</string>
<string name="url_copied_to_clipboard">URL скопійовано до комірки обміну</string>
<string name="jabber_id_copied_to_clipboard">Адресу XMPP скопійовано до комірки обміну</string>
<string name="error_message_copied_to_clipboard">Текст повідомлення про помилку скопійовано до комірки обміну</string>
<string name="web_address">веб адреса</string>
<string name="scan_qr_code">Розпізнати QR-код</string>
@ -308,6 +317,14 @@
<string name="try_again">Спробуйте ще</string>
<string name="pref_keep_foreground_service">Підтримувати сервіс на першому плані</string>
<string name="pref_keep_foreground_service_summary">Не дає операційній системі припиняти Ваш зв\'язок</string>
<string name="pref_create_backup">Створити резервну копію</string>
<string name="pref_create_backup_summary">Резервні копії зберігатимуться до %s</string>
<string name="notification_create_backup_title">Створення резервних копій</string>
<string name="notification_backup_created_title">Резервну копію створено</string>
<string name="notification_backup_created_subtitle">Резервні копії збережено до %s</string>
<string name="restoring_backup">Відтворення з резервної копії</string>
<string name="notification_restored_backup_title">Відтворено з резервної копії</string>
<string name="notification_restored_backup_subtitle">Не забудьте включити обліковий запис</string>
<string name="choose_file">Вибрати файл</string>
<string name="receiving_x_file">Отримання %1$s (%2$d%% завершено)</string>
<string name="download_x_file">Завантажити %s</string>
@ -357,13 +374,18 @@
<string name="remove_membership">Відкликати право участі</string>
<string name="grant_admin_privileges">Дати права адміністратора</string>
<string name="remove_admin_privileges">Відкликати права адміністратора</string>
<string name="grant_owner_privileges">Надати права власника</string>
<string name="remove_owner_privileges">Відкликати права власника</string>
<string name="remove_from_room">Видалити з групи</string>
<string name="remove_from_channel">Прибрати з каналу</string>
<string name="could_not_change_affiliation">Не можу змінити пов\'язаність з %s</string>
<string name="ban_from_conference">Заборонити доступ до групи</string>
<string name="ban_from_channel">Заборонити в каналі</string>
<string name="removing_from_public_conference">Щоб видалити %s з публічної групи, забороніть йому доступ назавжди.</string>
<string name="ban_now">Вигнати зараз</string>
<string name="could_not_change_role">Не можу змінити роль %s</string>
<string name="conference_options">Налаштування приватного чату</string>
<string name="channel_options">Налаштування публічного каналу</string>
<string name="members_only">Приватно, лише для членів</string>
<string name="non_anonymous">Зробити XMPP адрес доступним для всіх</string>
<string name="moderated">Зробити канал модерованим</string>
@ -401,6 +423,8 @@
<string name="no_application_found_to_display_location">Не знайдено програми, щоб показати місцезнаходження</string>
<string name="location">Місцезнаходження</string>
<string name="title_undo_swipe_out_conversation">Розмову закрито</string>
<string name="title_undo_swipe_out_group_chat">Полишити приватну групу обміну повідомленнями</string>
<string name="title_undo_swipe_out_channel">Полишити публічний канал</string>
<string name="pref_dont_trust_system_cas_title">Не довіряти системним центрам сертифікації</string>
<string name="pref_dont_trust_system_cas_summary">Усі сертифікати мають бути підтверджені вручну</string>
<string name="pref_remove_trusted_certificates_title">Видалити сертифікати</string>
@ -421,6 +445,7 @@
<string name="recently_used">Остання, що використана</string>
<string name="choose_quick_action">Вибрати швидку дію</string>
<string name="search_contacts">Шукати в контактах</string>
<string name="search_bookmarks">Шукати закладки</string>
<string name="send_private_message">Відправити приватне повідомлення</string>
<string name="user_has_left_conference">%1$s залишила групу!</string>
<string name="username">Ім\'я користувача</string>
@ -506,7 +531,10 @@
<string name="security_error_invalid_file_access">Помилка безпеки: Недійсний доступ до файлу</string>
<string name="no_application_to_share_uri">Не знайдено програми, щоб поділитися URI</string>
<string name="share_uri_with">Поділитися URI з…</string>
<string name="welcome_text_quicksy"><![CDATA[Quicksy походить від популярної програми-XMPP клієнта Conversations і автоматизує додавання контактів.<br><br>Реєструєтеся за номером телефона, а Quicksy автоматично за номерами телефонів зі списку контактів на пристрої запропонує Вам можливі контакти.<br><br>Підписуючись на цю послугу Ви погоджуєтеся з нашою <a href="https://quicksy.im/#privacy">політикою конфіденційності</a>.]]></string>
<string name="agree_and_continue">Прийняти й продовжити</string>
<string name="magic_create_text">Ми проведемо Вас через створення облікового запису на conversations.im.¹\nОбираючи conversations.im в якості постачальника, Ви зможете спілкуватися з користувачами інших постачальників, надаючи їм свою повну XMPP адресу.</string>
<string name="your_full_jid_will_be">Ваша повна адреса XMPP буде: %s</string>
<string name="create_account">Створити обліковий запис</string>
<string name="use_own_provider">Використати мого власного провайдера</string>
<string name="pick_your_username">Оберіть Ваше ім\'я користувача</string>
@ -739,6 +767,8 @@
<string name="pref_more_notification_settings_summary">Важливість, звук, вібрація</string>
<string name="video_compression_channel_name">Стиснення відео</string>
<string name="view_media">Перегляд медіа</string>
<string name="view_users">Переглянути учасників</string>
<string name="group_chat_members">Учасники</string>
<string name="media_browser">Переглядач медіа</string>
<string name="security_violation_not_attaching_file">Файл пропущено через порушення безпеки.</string>
<string name="pref_video_compression">Якість відео</string>
@ -797,9 +827,56 @@
<string name="install_orbot">Встановити Orbot</string>
<string name="start_orbot">Запустити Orbot</string>
<string name="no_market_app_installed">Не знайдено програми для пошуку й встановлення нових програм.</string>
<string name="group_chat_will_make_your_jabber_id_public">Цей канал опублікує вашу адресу XMPP</string>
<string name="ebook">електронна книга</string>
<string name="video_original">Оригінал (не стиснений)</string>
<string name="open_with">Відкрити за допомогою...</string>
<string name="set_profile_picture">Фото профілю чату</string>
<string name="choose_account">Виберіть обліковий запис</string>
<string name="restore_backup">Відновити з резервної копії</string>
<string name="restore">Відновити</string>
<string name="enter_password_to_restore">Введіть пароль для облікового запису %s, щоб відновити з резервної копії.</string>
<string name="restore_warning">Не використовуйте відновлення з резервної копії з метою клонувати інсталяцію (запускати одночасно ще один примірник). Відновлення з резервної копії призначене виключно для перенесення або на випадок втрати оригінального пристрою.</string>
<string name="unable_to_restore_backup">Не можу відновити з резервної копії.</string>
<string name="unable_to_decrypt_backup">Не можу розшифрувати резервну копію. Пароль правильний?</string>
<string name="backup_channel_name">Створити або відновити резервну копію</string>
<string name="enter_jabber_id">Ввести адресу XMPP</string>
<string name="create_group_chat">Створити групу обміну повідомленнями</string>
<string name="join_public_channel">Приєднатися до публічного каналу</string>
<string name="create_private_group_chat">Створити приватну групу обміну повідомленнями</string>
<string name="create_public_channel">Створити публічний канал</string>
<string name="create_dialog_channel_name">Назва каналу</string>
<string name="xmpp_address">Адреса XMPP</string>
<string name="please_enter_name">Будь ласка, дайте назву для каналу</string>
<string name="please_enter_xmpp_address">Будь ласка, надайте адресу XMPP</string>
<string name="this_is_an_xmpp_address">Це адреса XMPP. Будь ласка, дайте назву.</string>
<string name="creating_channel">Створення публічного каналу…</string>
<string name="channel_already_exists">Цей канал уже існує</string>
<string name="joined_an_existing_channel">Ви приєдналися до наявного каналу</string>
<string name="unable_to_set_channel_configuration">Не можу налаштувати канал</string>
<string name="allow_participants_to_edit_subject">Дозволити будь-кому редагувати тему</string>
<string name="allow_participants_to_invite_others">Дозволити будь-кому запрошувати інших</string>
<string name="anyone_can_edit_subject">Будь-хто може редагувати тему.</string>
<string name="owners_can_edit_subject">Власники можуть редагувати тему.</string>
<string name="admins_can_edit_subject">Адміністратори можуть редагувати тему.</string>
<string name="owners_can_invite_others">Власники можуть запрошувати інших.</string>
<string name="anyone_can_invite_others">Будь-хто може запрошувати інших.</string>
<string name="jabber_ids_are_visible_to_admins">Адміністратори бачать адреси XMPP.</string>
<string name="jabber_ids_are_visible_to_anyone">Будь-хто бачить адреси XMPP.</string>
<string name="no_users_hint_channel">Цей публічний канал без учасників. Запросіть Ваші контакти або скористайтеся кнопкою поширення, щоб поділитися адресою XMPP.</string>
<string name="no_users_hint_group_chat">Ця приватна група обміну повідомленнями без учасників.</string>
<string name="manage_permission">Управляти правами</string>
<string name="search_participants">Шукати учасників</string>
<string name="file_too_large">Файл надто великий</string>
<string name="attach">Долучити</string>
<string name="discover_channels">Знайти канали</string>
<string name="search_channels">Шукати канали</string>
<string name="channel_discovery_opt_in_title">Можливе порушення приватності!</string>
<string name="channel_discover_opt_in_message"><![CDATA[Пошук каналів використовує сторонній сервіс з назвою <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Використання цієї функції передає Вашу IP адресу та пошукові запити цьому сервісу. Перегляньте їхню <a href="https://search.jabbercat.org/privacy">політику конфіденційності</a>, щоб отримати більше інформації.]]></string>
<string name="i_already_have_an_account">Я вже маю обліковий запис</string>
<string name="add_existing_account">Додати наявний обліковий запис</string>
<string name="register_new_account">Зареєструвати новий обліковий запис</string>
<string name="this_looks_like_a_domain">Це схоже на ім\'я домену</string>
<string name="add_anway">Додати все одно</string>
<string name="this_looks_like_channel">Це схоже на адресу каналу</string>
</resources>

View File

@ -0,0 +1,22 @@
<?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="battery_optimizations_enabled_explained">Пристрій застосовує до цієї програми правила оптимізації використання батареї, які можуть призвести до затримки сповіщень або навіть втрати повідомлень.\nРекомендовано їх відключити.</string>
<string name="battery_optimizations_enabled_dialog">Пристрій застосовує до цієї програми правила оптимізації використання батареї, які можуть призвести до затримки сповіщень або навіть втрати повідомлень.\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="foreground_service_channel_description">Цей вид сповіщень показує постійне сповіщення про те, що ця програма працює.</string>
<string name="set_profile_picture">Зображення профілю для Quicksy</string>
</resources>