Merge remote-tracking branch 'origin/develop' into feature/bma/protectLinkNewDevice

This commit is contained in:
Benoit Marty
2026-06-17 11:12:36 +02:00
147 changed files with 1662 additions and 1757 deletions
@@ -3,9 +3,15 @@
<string name="screen_create_room_action_create_room">"Новы пакой"</string>
<string name="screen_create_room_add_people_title">"Запрасіць карыстальнікаў"</string>
<string name="screen_create_room_error_creating_room">"Пры стварэнні пакоя адбылася памылка"</string>
<string name="screen_create_room_name_placeholder">"Дадаць імя…"</string>
<string name="screen_create_room_new_room_title">"Новы пакой"</string>
<string name="screen_create_room_new_space_title">"Новая прастора"</string>
<string name="screen_create_room_private_option_description">"Толькі запрошаныя людзі могуць атрымаць доступ да гэтага пакоя. Усе паведамленні абаронены end-to-end шыфраваннем."</string>
<string name="screen_create_room_public_option_description">"Любы можа знайсці гэты пакой.
Вы можаце змяніць гэта ў любы час у наладах пакоя."</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Папрасіце далучыцца"</string>
<string name="screen_create_room_room_address_section_title">"Адрас"</string>
<string name="screen_create_room_space_selection_sheet_title">"Дадаць у прастору"</string>
<string name="screen_create_room_topic_label">"Тэма (неабавязкова)"</string>
<string name="screen_create_room_topic_placeholder">"Дадаць апісанне…"</string>
</resources>
@@ -9,6 +9,6 @@
<string name="screen_deactivate_account_list_item_1_bold_part">"Отключить навсегда"</string>
<string name="screen_deactivate_account_list_item_2">"Вы будете удалены из всех чатов."</string>
<string name="screen_deactivate_account_list_item_3">"Данные Вашего аккаунта будут удалены с нашего сервера идентификации."</string>
<string name="screen_deactivate_account_list_item_4">"Ваши сообщения по-прежнему будут видны зарегистрированным пользователям, но не будут доступны новым или незарегистрированным пользователям, если вы решите удалить их."</string>
<string name="screen_deactivate_account_list_item_4">"Ваши сообщения по-прежнему будут видны зарегистрированным пользователям, но не будут доступны новым или незарегистрированным пользователям, если Вы решите удалить их."</string>
<string name="screen_deactivate_account_title">"Удалить аккаунт"</string>
</resources>
@@ -6,7 +6,7 @@
<string name="screen_identity_confirmation_title">"Подтвердите личность"</string>
<string name="screen_identity_confirmation_use_another_device">"Использовать другое устройство"</string>
<string name="screen_identity_confirmation_use_recovery_key">"Использовать ключ восстановления"</string>
<string name="screen_identity_confirmed_subtitle">"Теперь вы можете безопасно читать и отправлять сообщения, и все, с кем вы общаетесь в чате, также могут доверять этому устройству."</string>
<string name="screen_identity_confirmed_subtitle">"Теперь Вы можете безопасно читать и отправлять сообщения, и все, с кем Вы общаетесь в чате, также могут доверять этому устройству."</string>
<string name="screen_identity_confirmed_title">"Устройство проверено"</string>
<string name="screen_identity_use_another_device">"Использовать другое устройство"</string>
<string name="screen_identity_waiting_on_other_device">"Ожидание другого устройства…"</string>
@@ -21,7 +21,7 @@
<string name="screen_invites_decline_direct_chat_message">"Вы уверены, что хотите отказаться от личного общения с %1$s?"</string>
<string name="screen_invites_decline_direct_chat_title">"Отклонить чат"</string>
<string name="screen_invites_empty_list">"Нет приглашений"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) пригласил(а) вас"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) пригласил(а) Вас"</string>
<string name="screen_migration_message">"Это единоразовая процедура, спасибо, что подождали."</string>
<string name="screen_migration_title">"Настройка Вашего аккаунта."</string>
<string name="screen_roomlist_a11y_create_message">"Создать новую беседу или комнату"</string>
@@ -31,20 +31,20 @@
<string name="screen_roomlist_filter_favourites">"Избранные"</string>
<string name="screen_roomlist_filter_favourites_empty_state_subtitle">"Добавить чат в избранное можно в настройках чата.
На данный момент вы можете убрать фильтры, чтобы увидеть другие ваши чаты."</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"У вас пока нет избранных чатов"</string>
<string name="screen_roomlist_filter_favourites_empty_state_title">"У Вас пока нет избранных чатов"</string>
<string name="screen_roomlist_filter_invites">"Приглашения"</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"У вас нет приглашений."</string>
<string name="screen_roomlist_filter_invites_empty_state_title">"У Вас нет приглашений."</string>
<string name="screen_roomlist_filter_low_priority">"Низкий приоритет"</string>
<string name="screen_roomlist_filter_low_priority_empty_state_title">"У вас пока нет чатов с низким приоритетом."</string>
<string name="screen_roomlist_filter_low_priority_empty_state_title">"У Вас пока нет чатов с низким приоритетом."</string>
<string name="screen_roomlist_filter_mixed_empty_state_subtitle">"Вы можете убрать фильтры, чтобы увидеть другие ваши чаты."</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"У вас нет чатов, соответствующих фильтрам"</string>
<string name="screen_roomlist_filter_mixed_empty_state_title">"У Вас нет чатов, соответствующих фильтрам"</string>
<string name="screen_roomlist_filter_people">"Пользователи"</string>
<string name="screen_roomlist_filter_people_empty_state_title">"У вас пока нет личных сообщений"</string>
<string name="screen_roomlist_filter_people_empty_state_title">"У Вас пока нет личных сообщений"</string>
<string name="screen_roomlist_filter_rooms">"Комнаты"</string>
<string name="screen_roomlist_filter_rooms_empty_state_title">"Вас пока нет ни в одной комнате"</string>
<string name="screen_roomlist_filter_unreads">"Новые"</string>
<string name="screen_roomlist_filter_unreads_empty_state_title">"Поздравляем!
У вас нет непрочитанных сообщений!"</string>
У Вас нет непрочитанных сообщений!"</string>
<string name="screen_roomlist_knock_event_sent_description">"Запрос на присоединение отправлен"</string>
<string name="screen_roomlist_main_space_title">"Чаты"</string>
<string name="screen_roomlist_mark_as_read">"Пометить как прочитанное"</string>
@@ -10,7 +10,7 @@
<string name="screen_invites_decline_direct_chat_message">"Вы уверены, что хотите отказаться от личного общения с %1$s?"</string>
<string name="screen_invites_decline_direct_chat_title">"Отклонить чат"</string>
<string name="screen_invites_empty_list">"Нет приглашений"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) пригласил(а) вас"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) пригласил(а) Вас"</string>
<string name="screen_join_room_decline_and_block_alert_confirmation">"Да, отклонить и заблокировать"</string>
<string name="screen_join_room_decline_and_block_alert_message">"Вы действительно хотите отклонить приглашение в комнату? Также %1$s больше не сможет связаться с вами или приглашать в комнаты."</string>
<string name="screen_join_room_decline_and_block_alert_title">"Отклонить приглашение и заблокировать"</string>
@@ -3,7 +3,7 @@
<string name="screen_invite_users_already_a_member">"Уже участник"</string>
<string name="screen_invite_users_already_invited">"Уже приглашен(а)"</string>
<string name="screen_invite_users_confirm_dialog_subtitle_multiple_users">"У тебя пока нет чатов с этими контактами. Подтверди приглашение в эту комнату, прежде чем продолжить."</string>
<string name="screen_invite_users_confirm_dialog_subtitle_one_user">"У вас пока нет чатов с этим контактом. Подтверди приглашение в эту комнату, прежде чем продолжить."</string>
<string name="screen_invite_users_confirm_dialog_subtitle_one_user">"У Вас пока нет чатов с этим контактом. Подтвердите, что это действительно тот контакт, которого Вы хотите пригласить, прежде чем продолжить."</string>
<string name="screen_invite_users_confirm_dialog_title_mutiple_users">"Пригласить новых участников в эту комнату?"</string>
<string name="screen_invite_users_confirm_dialog_title_one_user">"Пригласить нового участника в эту комнату?"</string>
</resources>
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_join_room_cancel_knock_alert_confirmation">"Так, адмяніць"</string>
<string name="screen_join_room_decline_and_block_button_title">"Адхіліць і заблакіраваць"</string>
<string name="screen_join_room_join_action">"Далучыцца"</string>
<string name="screen_join_room_knock_action">"Націсніце, каб далучыцца"</string>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_join_room_ban_by_message">"%1$s заблокировал(а) вас в комнате."</string>
<string name="screen_join_room_ban_by_message">"%1$s заблокировал(а) Вас в комнате."</string>
<string name="screen_join_room_ban_message">"Вы были заблокированы в комнате"</string>
<string name="screen_join_room_ban_reason">"Причина: %1$s."</string>
<string name="screen_join_room_cancel_knock_action">"Отменить запрос"</string>
@@ -27,7 +27,7 @@
<string name="screen_join_room_loading_alert_title">"Не удалось показать предпросмотр комнаты"</string>
<string name="screen_join_room_space_not_supported_description">"%1$s еще не поддерживает пространства. Вы можете получить к ним доступ в веб-версии."</string>
<string name="screen_join_room_space_not_supported_title">"Пространства пока не поддерживаются"</string>
<string name="screen_join_room_subtitle_knock">"Нажмите кнопку ниже и администратор комнаты получит уведомление. После одобрения вы сможете присоединиться к обсуждению."</string>
<string name="screen_join_room_subtitle_knock">"Нажмите кнопку ниже и администратор комнаты получит уведомление. После одобрения Вы сможете присоединиться к беседе."</string>
<string name="screen_join_room_subtitle_no_preview">"Вы должны быть участником этой комнаты, чтобы просмотреть историю сообщений."</string>
<string name="screen_join_room_title_knock">"Хотите присоединиться к этой комнате?"</string>
<string name="screen_join_room_title_no_preview">"Предпросмотр недоступен"</string>
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_knock_requests_list_decline_alert_title">"Адхіліць доступ"</string>
<string name="screen_room_multiple_knock_requests_view_all_button_title">"Паглядзець усе"</string>
<string name="screen_room_single_knock_request_accept_button_title">"Прыняць"</string>
</resources>
@@ -4,10 +4,10 @@
<string name="screen_knock_requests_list_accept_all_alert_description">"Вы действительно хотите принять все заявки на присоединение?"</string>
<string name="screen_knock_requests_list_accept_all_alert_title">"Принять все запросы"</string>
<string name="screen_knock_requests_list_accept_all_button_title">"Принять все"</string>
<string name="screen_knock_requests_list_accept_all_failed_alert_description">"Мы не смогли принять все запросы. Попробовать еще раз?"</string>
<string name="screen_knock_requests_list_accept_all_failed_alert_description">"Мы не смогли принять все запросы. Попробовать ещё раз?"</string>
<string name="screen_knock_requests_list_accept_all_failed_alert_title">"Не удалось принять все запросы"</string>
<string name="screen_knock_requests_list_accept_all_loading_title">"Принять все заявки на присоединение"</string>
<string name="screen_knock_requests_list_accept_failed_alert_description">"Мы не смогли принять этот запрос. Хотите попробовать еще раз?"</string>
<string name="screen_knock_requests_list_accept_failed_alert_description">"Мы не смогли принять этот запрос. Хотите попробовать ещё раз?"</string>
<string name="screen_knock_requests_list_accept_failed_alert_title">"Не удалось принять запрос"</string>
<string name="screen_knock_requests_list_accept_loading_title">"Принятие заявки на присоединение"</string>
<string name="screen_knock_requests_list_ban_alert_confirm_button_title">"Да, отклонить и запретить"</string>
@@ -18,7 +18,7 @@
<string name="screen_knock_requests_list_decline_alert_description">"Вы уверены, что хотите отклонить запрос %1$s на присоединение к этой комнате?"</string>
<string name="screen_knock_requests_list_decline_alert_title">"Отклонить доступ"</string>
<string name="screen_knock_requests_list_decline_and_ban_action_title">"Отклонить и запретить"</string>
<string name="screen_knock_requests_list_decline_failed_alert_description">"Мы не смогли отклонить этот запрос. Попробовать еще раз?"</string>
<string name="screen_knock_requests_list_decline_failed_alert_description">"Мы не смогли отклонить этот запрос. Попробовать ещё раз?"</string>
<string name="screen_knock_requests_list_decline_failed_alert_title">"Не удалось отклонить запрос"</string>
<string name="screen_knock_requests_list_decline_loading_title">"Отклонение заявки на присоединение"</string>
<string name="screen_knock_requests_list_empty_state_description">"Вы сможете увидеть запрос, когда кто-то попросит присоединиться к комнате."</string>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="leave_conversation_alert_subtitle">"Вы уверены, что хотите покинуть беседу? Эта беседа не является общедоступной, и вы не сможете присоединиться к ней без приглашения."</string>
<string name="leave_room_alert_empty_subtitle">"Вы уверены, что хотите покинуть эту комнату? Здесь есть только вы. Если вы покинете комнату, никто не сможет присоединиться к ней в будущем, включая вас."</string>
<string name="leave_conversation_alert_subtitle">"Вы уверены, что хотите покинуть беседу? Эта беседа не является общедоступной, и Вы не сможете снова присоединиться к ней без приглашения."</string>
<string name="leave_room_alert_empty_subtitle">"Вы уверены, что хотите покинуть эту комнату? Здесь есть только Вы. Если Вы покинете комнату, никто не сможет присоединиться к ней в будущем, включая Вас."</string>
<string name="leave_room_alert_private_subtitle">"Вы уверены, что хотите покинуть эту комнату? Эта комната не является общедоступной, и Вы не сможете присоединиться к ней без приглашения."</string>
<string name="leave_room_alert_select_new_owner_action">"Назначить владельцев"</string>
<string name="leave_room_alert_select_new_owner_subtitle">"Вы единственный владелец этой комнаты. Перед тем, как её покинуть, необходимо передать владение кому-нибудь другому."</string>
@@ -15,7 +15,7 @@
<string name="screen_link_new_device_error_not_supported_title">"QR-код не поддерживается"</string>
<string name="screen_link_new_device_error_request_cancelled_subtitle">"Вход на другом устройстве был отменен."</string>
<string name="screen_link_new_device_error_request_cancelled_title">"Запрос на вход отменен"</string>
<string name="screen_link_new_device_error_request_timeout_subtitle">"Срок действия входа истек. Пожалуйста, попробуйте еще раз."</string>
<string name="screen_link_new_device_error_request_timeout_subtitle">"Срок действия входа истек. Пожалуйста, попробуйте ещё раз."</string>
<string name="screen_link_new_device_error_request_timeout_title">"Вход в систему не был выполнен вовремя"</string>
<string name="screen_link_new_device_mobile_step1">"Откройте %1$s на другом устройстве"</string>
<string name="screen_link_new_device_mobile_step2">"Выберите %1$s"</string>
@@ -26,7 +26,7 @@
<string name="screen_link_new_device_root_loading_qr_code">"Загрузка QR-кода…"</string>
<string name="screen_link_new_device_root_mobile_device">"Смартфон"</string>
<string name="screen_link_new_device_root_title">"Какое устройство вы хотите привязать?"</string>
<string name="screen_link_new_device_wrong_number_subtitle">"Пожалуйста, попробуйте еще раз и убедитесь, что вы правильно ввели 2-значный код. Если цифры по-прежнему не совпадают, обратитесь к администратору сервера."</string>
<string name="screen_link_new_device_wrong_number_subtitle">"Пожалуйста, попробуйте ещё раз и убедитесь, что Вы правильно ввели 2-значный код. Если цифры по-прежнему не совпадают, обратитесь к администратору сервера."</string>
<string name="screen_link_new_device_wrong_number_title">"Цифры не совпадают"</string>
<string name="screen_qr_code_login_connection_note_secure_state_description">"Не удалось установить безопасное соединение с новым устройством. Не беспокойтесь, существующие устройства в безопасности."</string>
<string name="screen_qr_code_login_connection_note_secure_state_list_header">"Что теперь?"</string>
@@ -40,9 +40,9 @@
<string name="screen_qr_code_login_error_cancelled_title">"Запрос на вход отменен"</string>
<string name="screen_qr_code_login_error_declined_subtitle">"Вход в систему был отклонен на другом устройстве."</string>
<string name="screen_qr_code_login_error_declined_title">"Вход отклонен"</string>
<string name="screen_qr_code_login_error_device_already_signed_in_subtitle">"На этом все."</string>
<string name="screen_qr_code_login_error_device_already_signed_in_subtitle">"На этом всё."</string>
<string name="screen_qr_code_login_error_device_already_signed_in_title">"Вход уже выполнен на другом устройстве"</string>
<string name="screen_qr_code_login_error_expired_subtitle">"Срок действия входа истек. Пожалуйста, попробуйте еще раз."</string>
<string name="screen_qr_code_login_error_expired_subtitle">"Срок действия входа истек. Пожалуйста, попробуйте ещё раз."</string>
<string name="screen_qr_code_login_error_expired_title">"Вход в систему не был выполнен вовремя"</string>
<string name="screen_qr_code_login_error_linking_not_suported_subtitle">"Другое устройство не поддерживает вход в %s с помощью QR-кода.
@@ -55,6 +55,6 @@
<string name="screen_qr_code_login_invalid_scan_state_subtitle">"Неверный QR-код"</string>
<string name="screen_qr_code_login_no_camera_permission_state_description">"Чтобы продолжить, вам необходимо разрешить %1$s использовать камеру вашего устройства."</string>
<string name="screen_qr_code_login_no_camera_permission_state_title">"Разрешите доступ к камере для сканирования QR-кода"</string>
<string name="screen_qr_code_login_unknown_error_description">"Произошла непредвиденная ошибка. Пожалуйста, попробуйте еще раз."</string>
<string name="screen_qr_code_login_unknown_error_description">"Произошла непредвиденная ошибка. Пожалуйста, попробуйте ещё раз."</string>
<string name="screen_qr_code_login_verify_code_loading">"Ожидание другого устройства"</string>
</resources>
@@ -10,6 +10,8 @@
package io.element.android.features.linknewdevice.impl.screens.scan
import androidx.activity.ComponentActivity
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.AndroidComposeUiTest
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
@@ -59,10 +61,12 @@ class ScanQrCodeViewTest {
onBackClick: () -> Unit = EnsureNeverCalled(),
) {
setContent {
ScanQrCodeView(
state = state,
onBackClick = onBackClick,
)
CompositionLocalProvider(LocalInspectionMode provides true) {
ScanQrCodeView(
state = state,
onBackClick = onBackClick,
)
}
}
}
}
@@ -2,4 +2,5 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_share_location_live_location_disclaimer_title">"Ваша історія поточного місцезнаходження зберігатиметься у кімнаті та буде доступна учасникам після завершення сеансу."</string>
<string name="screen_share_location_live_location_duration_picker_title">"Виберіть, як довго ділитися своїм місцезнаходженням."</string>
<string name="screen_share_location_live_location_missing_permissions">"У вас немає дозволу ділитися своїм місцезнаходженням у реальному часі в цій кімнаті"</string>
</resources>
@@ -35,5 +35,5 @@
</plurals>
<string name="screen_app_lock_use_biometric_android">"Выкарыстоўваць біяметрыю"</string>
<string name="screen_app_lock_use_pin_android">"Выкарыстоўваць PIN-код"</string>
<string name="screen_signout_in_progress_dialog_content">"Выхад…"</string>
<string name="screen_signout_in_progress_dialog_content">"Выдаленне прылады…"</string>
</resources>
@@ -15,9 +15,9 @@
<string name="screen_app_lock_setup_biometric_unlock_subtitle">"Используйте %1$s для разблокировки приложения"</string>
<string name="screen_app_lock_setup_choose_pin">"Выберите PIN-код"</string>
<string name="screen_app_lock_setup_confirm_pin">"Подтвердите PIN-код"</string>
<string name="screen_app_lock_setup_pin_context">"Установите блокировку на %1$s, чтобы повысить безопасность ваших чатов.
<string name="screen_app_lock_setup_pin_context">"Установите блокировку на %1$s, чтобы повысить безопасность Ваших чатов.
Выберите код, который трудно забыть. Если вы забудете PIN-код, вам придется выйти из приложения."</string>
Выберите код, который трудно забыть. Если Вы забудете PIN-код, Вам придется выйти из аккаунта."</string>
<string name="screen_app_lock_setup_pin_forbidden_dialog_content">"Этот PIN-код небезопасен."</string>
<string name="screen_app_lock_setup_pin_forbidden_dialog_title">"Выбрать другой PIN-код"</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Повторите PIN-код"</string>
@@ -25,14 +25,14 @@
<string name="screen_app_lock_signout_alert_message">"Чтобы продолжить, вам необходимо повторно войти в систему и создать новый PIN-код"</string>
<string name="screen_app_lock_signout_alert_title">"Это устройство удаляется"</string>
<plurals name="screen_app_lock_subtitle">
<item quantity="one">"У вас осталась %1$d попытка разблокировки"</item>
<item quantity="few">"У вас остались %1$d попытки разблокировки"</item>
<item quantity="many">"У вас осталось %1$d попыток разблокировки"</item>
<item quantity="one">"У Вас осталась %1$d попытка разблокировки"</item>
<item quantity="few">"У Вас остались %1$d попытки разблокировки"</item>
<item quantity="many">"У Вас осталось %1$d попыток разблокировки"</item>
</plurals>
<plurals name="screen_app_lock_subtitle_wrong_pin">
<item quantity="one">"Неверный PIN-код. У вас осталась %1$d попытка"</item>
<item quantity="few">"Неверный PIN-код. У вас остались %1$d попытки"</item>
<item quantity="many">"Неверный PIN-код. У вас осталось %1$d попыток"</item>
<item quantity="one">"Неверный PIN-код. У Вас осталась %1$d попытка"</item>
<item quantity="few">"Неверный PIN-код. У Вас остались %1$d попытки"</item>
<item quantity="many">"Неверный PIN-код. У Вас осталось %1$d попыток"</item>
</plurals>
<string name="screen_app_lock_use_biometric_android">"Использовать биометрию"</string>
<string name="screen_app_lock_use_pin_android">"Использовать PIN-код"</string>
@@ -20,7 +20,7 @@
<string name="screen_change_server_subtitle">"Які адрас вашага сервера?"</string>
<string name="screen_change_server_title">"Выберыце свой сервер"</string>
<string name="screen_create_account_title">"Стварыць уліковы запіс"</string>
<string name="screen_login_error_deactivated_account">"Гэты ўліковы запіс быў дэактываваны."</string>
<string name="screen_login_error_deactivated_account">"Гэты ўліковы запіс быў выдалены."</string>
<string name="screen_login_error_invalid_credentials">"Няправільнае імя карыстальніка і/або пароль"</string>
<string name="screen_login_error_invalid_user_id">"Гэта несапраўдны ідэнтыфікатар карыстальніка. Чаканы фармат: @user:homeserver.org"</string>
<string name="screen_login_error_refresh_tokens">"Гэты сервер настроены на выкарыстанне маркераў абнаўлення. Яны не падтрымліваюцца пры ўваходзе на аснове пароля."</string>
@@ -29,6 +29,7 @@
<string name="screen_login_subtitle">"Matrix - гэта адкрытая сетка для бяспечнай, дэцэнтралізаванай сувязі."</string>
<string name="screen_login_title">"Сардэчна запрашаем!"</string>
<string name="screen_login_title_with_homeserver">"Увайсці ў %1$s"</string>
<string name="screen_onboarding_app_version">"Версія %1$s"</string>
<string name="screen_onboarding_sign_in_manually">"Увайсці ўручную"</string>
<string name="screen_onboarding_sign_in_to">"Увайсці ў %1$s"</string>
<string name="screen_onboarding_sign_in_with_qr_code">"Увайсці з QR-кодам"</string>
@@ -35,7 +35,7 @@
<string name="screen_login_error_unsupported_authentication">"Выбранный сервер не поддерживает вход по паролю и OAuth. Пожалуйста, свяжитесь с администратором или выберите другой сервер."</string>
<string name="screen_login_form_header">"Введите свои данные"</string>
<string name="screen_login_subtitle">"Matrix — это открытая сеть для безопасной децентрализованной связи."</string>
<string name="screen_login_title">"Рады видеть вас снова!"</string>
<string name="screen_login_title">"Рады видеть Вас снова!"</string>
<string name="screen_login_title_with_homeserver">"Войти в %1$s"</string>
<string name="screen_missing_key_backup_open_element_classic">"Открыть Element Classic"</string>
<string name="screen_missing_key_backup_step_1">"Откройте Element Classic на своем устройстве."</string>
@@ -69,9 +69,9 @@
<string name="screen_qr_code_login_error_cancelled_title">"Запрос на вход отменен"</string>
<string name="screen_qr_code_login_error_declined_subtitle">"Вход в систему был отклонен на другом устройстве."</string>
<string name="screen_qr_code_login_error_declined_title">"Вход отклонен"</string>
<string name="screen_qr_code_login_error_device_already_signed_in_subtitle">"На этом все."</string>
<string name="screen_qr_code_login_error_device_already_signed_in_subtitle">"На этом всё."</string>
<string name="screen_qr_code_login_error_device_already_signed_in_title">"Вход уже выполнен на другом устройстве"</string>
<string name="screen_qr_code_login_error_expired_subtitle">"Срок действия входа истек. Пожалуйста, попробуйте еще раз."</string>
<string name="screen_qr_code_login_error_expired_subtitle">"Срок действия входа истек. Пожалуйста, попробуйте ещё раз."</string>
<string name="screen_qr_code_login_error_expired_title">"Вход в систему не был выполнен вовремя"</string>
<string name="screen_qr_code_login_error_linking_not_suported_subtitle">"Другое устройство не поддерживает вход в %s с помощью QR-кода.
@@ -95,7 +95,7 @@
<string name="screen_qr_code_login_no_camera_permission_state_title">"Разрешите доступ к камере для сканирования QR-кода"</string>
<string name="screen_qr_code_login_scanning_state_title">"Сканировать QR-код"</string>
<string name="screen_qr_code_login_start_over_button">"Начать заново"</string>
<string name="screen_qr_code_login_unknown_error_description">"Произошла непредвиденная ошибка. Пожалуйста, попробуйте еще раз."</string>
<string name="screen_qr_code_login_unknown_error_description">"Произошла непредвиденная ошибка. Пожалуйста, попробуйте ещё раз."</string>
<string name="screen_qr_code_login_verify_code_loading">"Ожидание другого устройства"</string>
<string name="screen_qr_code_login_verify_code_subtitle">"Сервер для аккаунта может запросить следующий код для подтверждения входа."</string>
<string name="screen_qr_code_login_verify_code_title">"Ваш код подтверждения"</string>
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_signout_confirmation_dialog_content">"Вы ўпэўнены, што хочаце выйсці?"</string>
<string name="screen_signout_confirmation_dialog_content">"Вы ўпэўненыя, што хочаце выдаліць гэту прыладу?"</string>
<string name="screen_signout_confirmation_dialog_submit">"Выйсці"</string>
<string name="screen_signout_confirmation_dialog_title">"Выйсці"</string>
<string name="screen_signout_in_progress_dialog_content">"Выхад…"</string>
<string name="screen_signout_in_progress_dialog_content">"Выдаленне прылады…"</string>
<string name="screen_signout_key_backup_disabled_subtitle">"Вы збіраецеся выйсці з апошняга сеанса. Калі вы выйдзеце з сістэмы зараз, вы страціце доступ да зашыфраваных паведамленняў."</string>
<string name="screen_signout_key_backup_disabled_title">"Вы адключылі рэзервовае капіраванне"</string>
<string name="screen_signout_key_backup_offline_subtitle">"Вашы ключы ўсё яшчэ захоўваліся, калі вы выйшлі з сеткі. Паўторна падключыцеся, каб можна было стварыць рэзервовую копію вашых ключоў перад выхадам."</string>
@@ -7,9 +7,9 @@
<string name="screen_signout_key_backup_disabled_subtitle">"Это ваше единственное устройство. Если вы его удалите, вам потребуется ключ восстановления, чтобы подтвердить свою цифровую личность и восстановить зашифрованные чаты при следующем входе в систему."</string>
<string name="screen_signout_key_backup_disabled_title">"Вы потеряете доступ к своим зашифрованным чатам"</string>
<string name="screen_signout_key_backup_offline_subtitle">"Когда вы отключились от сети, резервное копирование ваших ключей продолжалось. Подключитесь снова, чтобы резервная копия ваших ключей была создана, прежде чем вы отключите это устройство."</string>
<string name="screen_signout_key_backup_offline_title">"Резервное копирование ключей все еще продолжается"</string>
<string name="screen_signout_key_backup_offline_title">"Резервное копирование ключей всё ещё продолжается"</string>
<string name="screen_signout_key_backup_ongoing_subtitle">"Пожалуйста, дождитесь завершения процесса, прежде чем выходить из системы."</string>
<string name="screen_signout_key_backup_ongoing_title">"Резервное копирование ключей все еще продолжается"</string>
<string name="screen_signout_key_backup_ongoing_title">"Резервное копирование ключей всё ещё продолжается"</string>
<string name="screen_signout_preference_item">"Удалить это устройство"</string>
<string name="screen_signout_recovery_disabled_subtitle">"Это ваше единственное устройство. Если вы его удалите, вам потребуется ключ восстановления, чтобы подтвердить свою цифровую личность и восстановить зашифрованные чаты при следующем входе в систему."</string>
<string name="screen_signout_recovery_disabled_title">"Вы потеряете доступ к своим зашифрованным чатам"</string>
@@ -101,9 +101,10 @@ class ThreadsListPresenter(
coroutineScope.launch {
Timber.d("Paginating thread list: $paginationStatus")
threadsListService.paginate()
.onFailure { Timber.w(it, "Failed to paginate thread list") }
}
} else {
Timber.d("Not paginating since there is nothing else to load, current status: $paginationStatus")
Timber.d("Not paginating, current status: $paginationStatus")
}
}
}
@@ -162,14 +162,14 @@ private fun ScrollHelper(
val lastVisibleItemIndex by remember {
derivedStateOf { listState.firstVisibleItemIndex + listState.layoutInfo.visibleItemsInfo.size - 1 }
}
val needsPagination by remember {
val shouldPaginate by remember {
derivedStateOf {
val canLoadNewItems = listState.isScrollInProgress || listState.firstVisibleItemScrollOffset == 0
canLoadNewItems && lastVisibleItemIndex == listState.layoutInfo.totalItemsCount - 1
val canLoadNewItems = listState.isScrollInProgress || listState.layoutInfo.totalItemsCount == 0
canLoadNewItems && lastVisibleItemIndex >= listState.layoutInfo.totalItemsCount - 1
}
}
LaunchedEffect(needsPagination, lastVisibleItemIndex) {
if (needsPagination) {
LaunchedEffect(shouldPaginate, lastVisibleItemIndex) {
if (shouldPaginate) {
updatedOnPaginate()
delay(400L)
}
@@ -16,6 +16,8 @@
<string name="emoji_picker_category_symbols">"Сімвалы"</string>
<string name="screen_media_upload_preview_error_failed_processing">"Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Не атрымалася загрузіць медыяфайлы, паспрабуйце яшчэ раз."</string>
<string name="screen_media_upload_preview_processing">"Апрацоўка…"</string>
<string name="screen_media_upload_preview_title">"Дадаць медыя"</string>
<string name="screen_report_content_block_user">"Заблакіраваць карыстальніка"</string>
<string name="screen_report_content_block_user_hint">"Адзначце, ці хочаце вы схаваць усе бягучыя і будучыя паведамленні ад гэтага карыстальніка"</string>
<string name="screen_report_content_explanation">"Гэтае паведамленне будзе перададзена адміністратару вашага хатняга сервера. Яны не змогуць прачытаць зашыфраваныя паведамленні."</string>
@@ -25,7 +27,7 @@
<string name="screen_room_attachment_source_camera_video">"Запісаць відэа"</string>
<string name="screen_room_attachment_source_files">"Далучэнне"</string>
<string name="screen_room_attachment_source_gallery">"Бібліятэка фота &amp; відэа"</string>
<string name="screen_room_attachment_source_location">"Месцазнаходжанне"</string>
<string name="screen_room_attachment_source_location">"Абагуліць месцазнаходжанне"</string>
<string name="screen_room_attachment_source_poll">"Апытанне"</string>
<string name="screen_room_attachment_text_formatting">"Фармаціраванне тэксту"</string>
<string name="screen_room_encrypted_history_banner">"Гісторыя паведамленняў зараз недаступна."</string>
@@ -16,6 +16,12 @@
<string name="emoji_picker_category_places">"Путешествия и места"</string>
<string name="emoji_picker_category_recent">"Недавние эмодзи"</string>
<string name="emoji_picker_category_symbols">"Символы"</string>
<string name="screen_image_edition_a11y_flip_image_horizontally">"Перевернуть изображение по горизонтали"</string>
<string name="screen_image_edition_a11y_flip_image_horizontally_state_flipped">"Перевернуто по горизонтали"</string>
<string name="screen_image_edition_a11y_flip_image_horizontally_state_original">"Оригинал"</string>
<string name="screen_image_edition_a11y_flip_image_vertically">"Перевернуть изображение по вертикали"</string>
<string name="screen_image_edition_a11y_flip_image_vertically_state_flipped">"Перевернуто по вертикали"</string>
<string name="screen_image_edition_a11y_flip_image_vertically_state_original">"Оригинал"</string>
<string name="screen_image_edition_a11y_rotate_to_the_left">"Повернуть изображение влево."</string>
<plurals name="screen_image_edition_a11y_rotation_state">
<item quantity="one">"%1$d градус"</item>
@@ -26,8 +32,8 @@
<string name="screen_media_upload_preview_caption_warning">"Подпись может быть не видна пользователям старых приложений."</string>
<string name="screen_media_upload_preview_change_video_quality_prompt">"Нажмите, чтобы изменить качество загружаемого видео."</string>
<string name="screen_media_upload_preview_error_could_not_be_uploaded">"Файл не может быть загружен."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Не удалось обработать медиафайл для загрузки, попробуйте еще раз."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Не удалось загрузить медиафайлы, попробуйте еще раз."</string>
<string name="screen_media_upload_preview_error_failed_processing">"Не удалось обработать медиафайл для загрузки, попробуйте ещё раз."</string>
<string name="screen_media_upload_preview_error_failed_sending">"Не удалось загрузить медиафайлы, попробуйте ещё раз."</string>
<string name="screen_media_upload_preview_error_too_large_message">"Максимальный размер файла: %1$s."</string>
<string name="screen_media_upload_preview_error_too_large_title">"Файл слишком большой для загрузки."</string>
<string name="screen_media_upload_preview_item_count">"%1$d из %2$d"</string>
@@ -16,6 +16,13 @@
<string name="emoji_picker_category_places">"Подорожі та місця"</string>
<string name="emoji_picker_category_recent">"Нещодавні емодзі"</string>
<string name="emoji_picker_category_symbols">"Символи"</string>
<string name="screen_image_edition_a11y_rotate_to_the_left">"Повернути зображення вліво"</string>
<plurals name="screen_image_edition_a11y_rotation_state">
<item quantity="one">"%1$d градус"</item>
<item quantity="few">"%1$d градуси"</item>
<item quantity="many">"%1$d градусів"</item>
</plurals>
<string name="screen_image_edition_title">"Редагувати фото"</string>
<string name="screen_media_upload_preview_caption_warning">"Користувачі старих застосунків можуть не бачити підписи."</string>
<string name="screen_media_upload_preview_change_video_quality_prompt">"Натисніть, щоб змінити якість вивантажуваного відео"</string>
<string name="screen_media_upload_preview_error_could_not_be_uploaded">"Файл не може бути вивантажено."</string>
@@ -26,6 +33,7 @@
<string name="screen_media_upload_preview_item_count">"Елемент %1$d з %2$d"</string>
<string name="screen_media_upload_preview_optimize_image_quality_title">"Оптимізувати якість зображення"</string>
<string name="screen_media_upload_preview_processing">"Обробка…"</string>
<string name="screen_media_upload_preview_title">"Додати медіа"</string>
<string name="screen_report_content_block_user">"Заблокувати користувача"</string>
<string name="screen_report_content_block_user_hint">"Перевірте, чи хочете ви приховати всі поточні та майбутні повідомлення від цього користувача"</string>
<string name="screen_report_content_explanation">"Це повідомлення буде надіслано адміністраторам вашого домашнього сервера. Вони не зможуть прочитати зашифровані повідомлення."</string>
@@ -23,8 +23,8 @@
<string name="screen_advanced_settings_media_compression_description">"Загружайте фотографии и видео быстрее и сокращайте потребление трафика"</string>
<string name="screen_advanced_settings_media_compression_title">"Оптимизировать качество мультимедиа"</string>
<string name="screen_advanced_settings_moderation_and_safety_section_title">"Модерация и безопасность"</string>
<string name="screen_advanced_settings_optimise_image_upload_quality_description">"Автоматически оптимизируйте изображения для более быстрой загрузки и уменьшения размера файлов."</string>
<string name="screen_advanced_settings_optimise_image_upload_quality_title">"Оптимизируйте качество загрузки изображения"</string>
<string name="screen_advanced_settings_optimise_image_upload_quality_description">"Автоматически оптимизировать изображения для более быстрой загрузки и уменьшения размера файлов."</string>
<string name="screen_advanced_settings_optimise_image_upload_quality_title">"Оптимизировать качество загрузки изображений"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_description">"%1$s. Нажмите здесь, чтобы изменить."</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_high">"Высокое (1080p)"</string>
<string name="screen_advanced_settings_optimise_video_upload_quality_low">"Низкое (480p)"</string>
@@ -35,14 +35,14 @@
<string name="screen_advanced_settings_send_read_receipts">"Уведомления о прочтении"</string>
<string name="screen_advanced_settings_send_read_receipts_description">"Если этот параметр выключен, другие пользователи не будут видеть, прочитали ли вы сообщения. Вы по-прежнему будете видеть статус прочтения других пользователей."</string>
<string name="screen_advanced_settings_share_presence">"Делиться присутствием"</string>
<string name="screen_advanced_settings_share_presence_description">"Если выключено, вы не будете видеть, кто печатает и читает сообщения, а также другие пользователи не будут знать, когда вы печатаете или читаете сообщения."</string>
<string name="screen_advanced_settings_share_presence_description">"Если выключено, Вы не будете видеть, кто печатает и читает сообщения, а также другие пользователи не будут знать, когда Вы печатаете или читаете сообщения."</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Всегда скрывать"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Всегда показывать"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"В приватных комнатах"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"Скрытые медиа всегда можно просмотреть, нажав на них."</string>
<string name="screen_advanced_settings_show_media_timeline_title">"Показать медиа в истории сообщений"</string>
<string name="screen_advanced_settings_view_source_description">"Включить опцию просмотра источника сообщения в истории сообщений."</string>
<string name="screen_blocked_users_empty">"У вас нет заблокированных пользователей"</string>
<string name="screen_blocked_users_empty">"У Вас нет заблокированных пользователей"</string>
<string name="screen_blocked_users_unblock_alert_action">"Разблокировать"</string>
<string name="screen_blocked_users_unblock_alert_description">"Вы снова сможете увидеть все сообщения."</string>
<string name="screen_blocked_users_unblock_alert_title">"Разблокировать пользователя"</string>
@@ -59,11 +59,12 @@
<string name="screen_labs_header_title">"Хотите попробовать?"</string>
<string name="screen_labs_title">"Лаборатория"</string>
<string name="screen_notification_settings_additional_settings_section_title">"Дополнительные параметры"</string>
<string name="screen_notification_settings_call_ringtone_label">"Мелодия звонка"</string>
<string name="screen_notification_settings_calls_label">"Аудио и видео звонки"</string>
<string name="screen_notification_settings_configuration_mismatch">"Несоответствие конфигурации"</string>
<string name="screen_notification_settings_configuration_mismatch_description">"Мы упростили настройки уведомлений, чтобы упростить поиск опций. Некоторые пользовательские настройки, выбранные вами ранее, не отображаются в данном меню, но они все еще активны.
<string name="screen_notification_settings_configuration_mismatch_description">"Мы упростили настройки уведомлений, чтобы упростить поиск опций. Некоторые пользовательские настройки, выбранные вами ранее, не отображаются в данном меню, но они всё ещё активны.
Если вы продолжите, некоторые настройки могут быть изменены."</string>
Если Вы продолжите, некоторые настройки могут быть изменены."</string>
<string name="screen_notification_settings_direct_chats">"В личных чатах"</string>
<string name="screen_notification_settings_edit_custom_settings_section_title">"Отдельные настройки для каждого чата"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Произошла ошибка при обновлении настройки уведомления."</string>
@@ -72,22 +73,30 @@
<string name="screen_notification_settings_edit_screen_direct_section_header">"Уведомлять меня в личных чатах"</string>
<string name="screen_notification_settings_edit_screen_group_section_header">"Уведомлять меня в групповых чатах"</string>
<string name="screen_notification_settings_enable_notifications">"Включить уведомления на данном устройстве"</string>
<string name="screen_notification_settings_failed_fixing_configuration">"Конфигурация не была исправлена, попробуйте еще раз."</string>
<string name="screen_notification_settings_failed_fixing_configuration">"Конфигурация не была исправлена, попробуйте ещё раз."</string>
<string name="screen_notification_settings_group_chats">"В групповых чатах"</string>
<string name="screen_notification_settings_invite_for_me_label">"Приглашения"</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Ваш сервер не поддерживает эту опцию для зашифрованных комнат, вы можете не получать некоторые уведомления."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Ваш сервер не поддерживает эту опцию для зашифрованных комнат, Вы можете не получать некоторые уведомления."</string>
<string name="screen_notification_settings_mentions_section_title">"Упоминания"</string>
<string name="screen_notification_settings_message_sound_dialog_choose_other">"Выбрать другой звук…"</string>
<string name="screen_notification_settings_message_sound_dialog_current_subtitle">"Сейчас используется %1$s"</string>
<string name="screen_notification_settings_message_sound_dialog_title">"Звук сообщения"</string>
<string name="screen_notification_settings_message_sound_label">"Звук сообщения"</string>
<string name="screen_notification_settings_mode_all">"Все"</string>
<string name="screen_notification_settings_mode_mentions">"Упоминания"</string>
<string name="screen_notification_settings_notification_section_title">"Уведомлять меня"</string>
<string name="screen_notification_settings_room_mention_label">"Уведомлять меня при упоминании @room"</string>
<string name="screen_notification_settings_sound_custom_fallback">"Пользовательский"</string>
<string name="screen_notification_settings_sound_custom_sound_button_title">"Пользовательский звук…"</string>
<string name="screen_notification_settings_sound_delete_sound_error_title">"Ошибка при удалении файла"</string>
<string name="screen_notification_settings_sound_element_default">"Element по умолчанию"</string>
<string name="screen_notification_settings_sound_element_fade">"Element Fade"</string>
<string name="screen_notification_settings_sound_import_sound_error_title">"Ошибка импорта файла"</string>
<string name="screen_notification_settings_sound_preview_sound_error_title">"Ошибка предварительного прослушиванием звука"</string>
<string name="screen_notification_settings_sound_section_title">"Звук"</string>
<string name="screen_notification_settings_sound_set_sound_error_dismiss_a11y">"Отклонить ошибку о звуке"</string>
<string name="screen_notification_settings_sound_set_sound_error_title">"Проблема с настройкой звукового оповещения"</string>
<string name="screen_notification_settings_sound_silent">"Без звука"</string>
<string name="screen_notification_settings_sound_system_alert">"Оповещение"</string>
<string name="screen_notification_settings_sound_system_anticipate">"Anticipate"</string>
<string name="screen_notification_settings_sound_system_bell">"Bell"</string>
@@ -95,6 +104,7 @@
<string name="screen_notification_settings_sound_system_calypso">"Calypso"</string>
<string name="screen_notification_settings_sound_system_chime">"Chime"</string>
<string name="screen_notification_settings_sound_system_choo_choo">"Choo Choo"</string>
<string name="screen_notification_settings_sound_system_default">"Как в системе"</string>
<string name="screen_notification_settings_sound_system_descent">"Descent"</string>
<string name="screen_notification_settings_sound_system_electronic">"Electronic"</string>
<string name="screen_notification_settings_sound_system_fanfare">"Fanfare"</string>
@@ -124,5 +134,5 @@
<string name="theme_system">"Системное"</string>
<string name="troubleshoot_notifications_entry_point_push_history_title">"История уведомлений"</string>
<string name="troubleshoot_notifications_entry_point_section">"Устранение неполадок"</string>
<string name="troubleshoot_notifications_entry_point_title">"Уведомления об устранении неполадок"</string>
<string name="troubleshoot_notifications_entry_point_title">"Устранение неполадок с уведомлениями"</string>
</resources>
@@ -81,10 +81,45 @@
<string name="screen_notification_settings_mode_mentions">"Згадки"</string>
<string name="screen_notification_settings_notification_section_title">"Сповіщати мене про"</string>
<string name="screen_notification_settings_room_mention_label">"Сповіщати про @room"</string>
<string name="screen_notification_settings_sound_custom_sound_button_title">"Користувацький звук…"</string>
<string name="screen_notification_settings_sound_delete_sound_error_title">"Помилка видалення файлу"</string>
<string name="screen_notification_settings_sound_element_default">"За замовчуванням Element"</string>
<string name="screen_notification_settings_sound_element_fade">"Згасання Element"</string>
<string name="screen_notification_settings_sound_import_sound_error_title">"Помилка імпорту файлу"</string>
<string name="screen_notification_settings_sound_preview_sound_error_title">"Проблема з попереднім прослуховуванням звуку сповіщення"</string>
<string name="screen_notification_settings_sound_section_title">"Звук"</string>
<string name="screen_notification_settings_sound_set_sound_error_title">"Проблема налаштовування звуку сповіщення"</string>
<string name="screen_notification_settings_sound_system_alert">"Тривога"</string>
<string name="screen_notification_settings_sound_system_anticipate">"Передбачення"</string>
<string name="screen_notification_settings_sound_system_bell">"Дзвоник"</string>
<string name="screen_notification_settings_sound_system_bloom">"Цвітіння"</string>
<string name="screen_notification_settings_sound_system_calypso">"Каліпсо"</string>
<string name="screen_notification_settings_sound_system_chime">"Передзвін"</string>
<string name="screen_notification_settings_sound_system_choo_choo">"Чу-чу"</string>
<string name="screen_notification_settings_sound_system_descent">"Спуск"</string>
<string name="screen_notification_settings_sound_system_electronic">"Електроніка"</string>
<string name="screen_notification_settings_sound_system_fanfare">"Фанфари"</string>
<string name="screen_notification_settings_sound_system_glass">"Скло"</string>
<string name="screen_notification_settings_sound_system_horn">"Ріг"</string>
<string name="screen_notification_settings_sound_system_ladder">"Драбина"</string>
<string name="screen_notification_settings_sound_system_minuet">"Менует"</string>
<string name="screen_notification_settings_sound_system_news_flash">"Екстренні новини"</string>
<string name="screen_notification_settings_sound_system_noir">"Нуар"</string>
<string name="screen_notification_settings_sound_system_sherwood_forest">"Шервудський ліс"</string>
<string name="screen_notification_settings_sound_system_spell">"Чаклування"</string>
<string name="screen_notification_settings_sound_system_suspense">"Інтрига"</string>
<string name="screen_notification_settings_sound_system_swish">"Свист"</string>
<string name="screen_notification_settings_sound_system_telegraph">"Телеграф"</string>
<string name="screen_notification_settings_sound_system_tiptoes">"Навшпиньки"</string>
<string name="screen_notification_settings_sound_system_tri_tone">"Тритональний"</string>
<string name="screen_notification_settings_sound_system_tweet">"Твіт"</string>
<string name="screen_notification_settings_sound_system_typewriters">"Друкарські машинки"</string>
<string name="screen_notification_settings_sound_system_update">"Оновити"</string>
<string name="screen_notification_settings_system_notifications_action_required">"Щоб отримувати сповіщення змініть свої %1$s."</string>
<string name="screen_notification_settings_system_notifications_action_required_content_link">"системні налаштування"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Системні сповіщення вимкнені"</string>
<string name="screen_notification_settings_title">"Сповіщення"</string>
<string name="theme_black">"Темна"</string>
<string name="theme_dark">"Темна"</string>
<string name="theme_light">"Світла"</string>
<string name="theme_system">"Системна"</string>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="crash_detection_dialog_content">"Пры апошнім выкарыстанні %1$s адбыўся збой. Хочаце падзяліцца справаздачай аб збоі?"</string>
<string name="crash_detection_dialog_content">"Пры апошнім выкарыстанні %1$s адбыўся збой. Хочаце абагуліць справаздачу аб збоі?"</string>
<string name="rageshake_detection_dialog_content">"Падобна, што вы трасеце тэлефон. Хочаце адкрыць экран паведамлення пра памылку?"</string>
<string name="settings_rageshake">"Rageshake"</string>
<string name="settings_rageshake_detection_threshold">"Парог выяўлення"</string>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="crash_detection_dialog_content">"При последнем использовании %1$s произошел сбой. Хотите отправить отчет?"</string>
<string name="rageshake_detection_dialog_content">"Похоже, что вы трясете телефон. Хотите открыть экран сообщения об ошибке?"</string>
<string name="rageshake_detection_dialog_content">"Похоже, что Вы трясете телефон в раздражении. Хотите открыть экран сообщения об ошибке?"</string>
<string name="settings_rageshake">"Встряхните"</string>
<string name="settings_rageshake_detection_threshold">"Порог обнаружения"</string>
</resources>
@@ -12,6 +12,6 @@
<string name="screen_bug_report_include_logs">"Дазволіць журналы"</string>
<string name="screen_bug_report_include_screenshot">"Адправіць здымак экрана"</string>
<string name="screen_bug_report_logs_description">"Каб пераканацца, што ўсё працуе правільна, у паведамленне будуць уключаны часопісы. Каб адправіць паведамленне без часопісаў, адключыце гэтую наладу."</string>
<string name="screen_bug_report_rash_logs_alert_title">"Пры апошнім выкарыстанні %1$s адбыўся збой. Хочаце падзяліцца справаздачай аб збоі?"</string>
<string name="screen_bug_report_rash_logs_alert_title">"Пры апошнім выкарыстанні %1$s адбыўся збой. Хочаце абагуліць справаздачу аб збоі?"</string>
<string name="screen_bug_report_view_logs">"Прагляд журналаў"</string>
</resources>
@@ -8,13 +8,15 @@
<string name="screen_bug_report_editor_placeholder">"Опишите проблему…"</string>
<string name="screen_bug_report_editor_supporting">"Если возможно, пожалуйста, напишите описание на английском языке."</string>
<string name="screen_bug_report_error_description_too_short">"Описание слишком короткое, пожалуйста, расскажите подробнее о том, что произошло. Спасибо!"</string>
<string name="screen_bug_report_github_issue_description">"При наличии, Вы можете ввести номер связанной проблемы из GitHub."</string>
<string name="screen_bug_report_github_issue_label">"Проблема на GitHub"</string>
<string name="screen_bug_report_include_crash_logs">"Отправка журналов сбоев"</string>
<string name="screen_bug_report_include_logs">"Разрешить ведение журналов"</string>
<string name="screen_bug_report_include_logs_error">"Ваши журналы слишком большие для включения в этот отчет. Пожалуйста, отправьте их нам другим способом."</string>
<string name="screen_bug_report_include_screenshot">"Отправить скриншот"</string>
<string name="screen_bug_report_logs_description">"Чтобы убедиться, что всё работает правильно, к сообщению будут прикреплены журналы. Чтобы отправить сообщение без журналов, отключите эту настройку."</string>
<string name="screen_bug_report_rash_logs_alert_title">"При последнем использовании %1$s произошел сбой. Хотите отправить отчет?"</string>
<string name="screen_bug_report_send_notification_settings_description">"Если у вас возникли проблемы с уведомлениями, загрузка настроек уведомлений может помочь нам определить основную причину."</string>
<string name="screen_bug_report_send_notification_settings_description">"Если у Вас возникли проблемы с уведомлениями, загрузка настроек уведомлений может помочь нам определить причину проблемы. Обратите внимание, что эти правила могут содержать конфиденциальную информацию, такую ​​как Ваше отображаемое имя или ключевые слова, по которым должны срабатывать уведомления."</string>
<string name="screen_bug_report_send_notification_settings_title">"Настройки отправки уведомлений"</string>
<string name="screen_bug_report_view_logs">"Просмотр журналов"</string>
</resources>
@@ -8,6 +8,8 @@
<string name="screen_bug_report_editor_placeholder">"Опишіть проблему…"</string>
<string name="screen_bug_report_editor_supporting">"Якщо можливо, будь ласка, напишіть опис англійською мовою."</string>
<string name="screen_bug_report_error_description_too_short">"Опис закороткий, будь ласка, надайте докладнішу інформацію про те, що сталося. Дякуємо!"</string>
<string name="screen_bug_report_github_issue_description">"Ви можете ввести номер вже існуючої проблеми на GitHub, якщо така є."</string>
<string name="screen_bug_report_github_issue_label">"Тікет на GitHub"</string>
<string name="screen_bug_report_include_crash_logs">"Надіслати журнали збоїв"</string>
<string name="screen_bug_report_include_logs">"Дозволити журнали"</string>
<string name="screen_bug_report_include_logs_error">"Ваші журнали надмірно великі, тому їх не можна включити в цей звіт, будь ласка, надішліть їх нам іншим способом."</string>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_report_room_leave_failed_alert_message">"Ваша жалоба была успешно отправлена, но мы столкнулись с проблемой при попытке покинуть комнату. Пожалуйста, попробуйте еще раз."</string>
<string name="screen_report_room_leave_failed_alert_message">"Ваша жалоба была успешно отправлена, но мы столкнулись с проблемой при попытке покинуть комнату. Пожалуйста, попробуйте ещё раз."</string>
<string name="screen_report_room_leave_failed_alert_title">"Невозможно покинуть комнату"</string>
<string name="screen_report_room_reason_footer">"Пожалуйтесь на комнату администратору. Если сообщения зашифрованы, ваш администратор не сможет их прочитать."</string>
<string name="screen_report_room_reason_placeholder">"Опишите причину жалобы…"</string>
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_change_permissions_administrators">"Толькі адміністратары"</string>
<string name="screen_room_change_permissions_administrators">"Адміністратар"</string>
<string name="screen_room_change_permissions_ban_people">"Заблакіраваць людзей"</string>
<string name="screen_room_change_permissions_delete_messages">"Выдаліць паведамленні"</string>
<string name="screen_room_change_permissions_invite_people">"Запрашайце людзей і прымайце запыты на далучэнне"</string>
<string name="screen_room_change_permissions_everyone">"Удзельнік"</string>
<string name="screen_room_change_permissions_invite_people">"Запрасіць людзей"</string>
<string name="screen_room_change_permissions_messages_and_content">"Паведамленні і змест"</string>
<string name="screen_room_change_permissions_moderators">"Адміністратары і мадэратары"</string>
<string name="screen_room_change_permissions_moderators">"Мадэратар"</string>
<string name="screen_room_change_permissions_remove_people">"Выдаляйце людзей і адхіляйце запыты на далучэнне"</string>
<string name="screen_room_change_permissions_room_avatar">"Змяніць аватар пакоя"</string>
<string name="screen_room_change_permissions_room_details">"Рэдагаваць пакой"</string>
@@ -39,8 +40,8 @@
<string name="screen_room_member_list_manage_member_unban_message">"Яны змогуць зноў далучыцца да гэтага пакоя, калі іх запросяць."</string>
<string name="screen_room_member_list_mode_banned">"Заблакіраваныя"</string>
<string name="screen_room_member_list_mode_members">"Удзельнікі"</string>
<string name="screen_room_member_list_role_administrator">"Толькі адміністратары"</string>
<string name="screen_room_member_list_role_moderator">"Адміністратары і мадэратары"</string>
<string name="screen_room_member_list_role_administrator">"Адміністратар"</string>
<string name="screen_room_member_list_role_moderator">"Мадэратар"</string>
<string name="screen_room_member_list_room_members_header_title">"Удзельнікі пакоя"</string>
<string name="screen_room_member_list_unbanning_user">"Разблакіроўка %1$s"</string>
<string name="screen_room_roles_and_permissions_admins">"Адміністратары"</string>
@@ -20,7 +20,7 @@
<string name="screen_room_change_permissions_send_messages">"Отправлять сообщения"</string>
<string name="screen_room_change_permissions_title">"Разрешения"</string>
<string name="screen_room_change_role_administrators_title">"Редактировать администраторов"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Вы не сможете отменить это действие. Вы даете пользователю такой же уровень прав, как и у вас"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Вы не сможете отменить это действие. Вы даете пользователю такой же уровень прав, как и у Вас"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Добавить администратора?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Отменить данное действие будет невозможно. Права передадутся выбранным пользователям. После вашего выхода действие станет необратимым."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Передать владение?"</string>
@@ -36,7 +36,7 @@
<string name="screen_room_change_role_section_administrators">"Администраторы"</string>
<string name="screen_room_change_role_section_moderators">"Модераторы"</string>
<string name="screen_room_change_role_section_users">"Участники"</string>
<string name="screen_room_change_role_unsaved_changes_description">"У вас есть несохраненные изменения."</string>
<string name="screen_room_change_role_unsaved_changes_description">"У Вас есть несохраненные изменения."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Сохранить изменения?"</string>
<string name="screen_room_member_list_banned_empty">"В этой комнате нет заблокированных пользователей."</string>
<plurals name="screen_room_member_list_banned_header_title">
@@ -6,6 +6,7 @@
<string name="screen_room_change_permissions_delete_messages">"Вилучати повідомлення"</string>
<string name="screen_room_change_permissions_everyone">"Учасник"</string>
<string name="screen_room_change_permissions_invite_people">"Запрошувати людей"</string>
<string name="screen_room_change_permissions_live_location">"Ділитися місцезнаходженням у реальному часі"</string>
<string name="screen_room_change_permissions_manage_space">"Керувати простором"</string>
<string name="screen_room_change_permissions_manage_space_rooms">"Керувати кімнатами"</string>
<string name="screen_room_change_permissions_member_moderation">"Керувати учасниками"</string>
@@ -1,14 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_edit_room_address_title">"Рэдагаваць адрас"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Пры абнаўленні налад апавяшчэнняў адбылася памылка."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Ваш хатні сервер не падтрымлівае гэтую опцыю ў зашыфраваных пакоях, вы можаце не атрымаць апавяшчэнне ў некаторых пакоях."</string>
<string name="screen_polls_history_title">"Апытанні"</string>
<string name="screen_room_change_permissions_administrators">"Толькі адміністратары"</string>
<string name="screen_room_change_permissions_administrators">"Адміністратар"</string>
<string name="screen_room_change_permissions_ban_people">"Заблакіраваць людзей"</string>
<string name="screen_room_change_permissions_delete_messages">"Выдаліць паведамленні"</string>
<string name="screen_room_change_permissions_invite_people">"Запрашайце людзей і прымайце запыты на далучэнне"</string>
<string name="screen_room_change_permissions_everyone">"Удзельнік"</string>
<string name="screen_room_change_permissions_invite_people">"Запрасіць людзей"</string>
<string name="screen_room_change_permissions_messages_and_content">"Паведамленні і змест"</string>
<string name="screen_room_change_permissions_moderators">"Адміністратары і мадэратары"</string>
<string name="screen_room_change_permissions_moderators">"Мадэратар"</string>
<string name="screen_room_change_permissions_remove_people">"Выдаляйце людзей і адхіляйце запыты на далучэнне"</string>
<string name="screen_room_change_permissions_room_avatar">"Змяніць аватар пакоя"</string>
<string name="screen_room_change_permissions_room_details">"Рэдагаваць пакой"</string>
@@ -52,8 +54,9 @@
<string name="screen_room_details_pinned_events_row_title">"Замацаваныя паведамленні"</string>
<string name="screen_room_details_profile_row_title">"Профіль"</string>
<string name="screen_room_details_roles_and_permissions">"Ролі і дазволы"</string>
<string name="screen_room_details_room_name_label">"Імя"</string>
<string name="screen_room_details_security_title">"Бяспека"</string>
<string name="screen_room_details_share_room_title">"Падзяліцца пакоем"</string>
<string name="screen_room_details_share_room_title">"Абагуліць пакой"</string>
<string name="screen_room_details_title">"Інфармацыя аб пакоі"</string>
<string name="screen_room_details_topic_title">"Тэма"</string>
<string name="screen_room_details_updating_room">"Ідзе абнаўленне пакоя…"</string>
@@ -69,8 +72,8 @@
<string name="screen_room_member_list_manage_member_unban_message">"Яны змогуць зноў далучыцца да гэтага пакоя, калі іх запросяць."</string>
<string name="screen_room_member_list_mode_banned">"Заблакіраваныя"</string>
<string name="screen_room_member_list_mode_members">"Удзельнікі"</string>
<string name="screen_room_member_list_role_administrator">"Толькі адміністратары"</string>
<string name="screen_room_member_list_role_moderator">"Адміністратары і мадэратары"</string>
<string name="screen_room_member_list_role_administrator">"Адміністратар"</string>
<string name="screen_room_member_list_role_moderator">"Мадэратар"</string>
<string name="screen_room_member_list_room_members_header_title">"Удзельнікі пакоя"</string>
<string name="screen_room_member_list_unbanning_user">"Разблакіроўка %1$s"</string>
<string name="screen_room_notification_settings_allow_custom">"Дазволіць уласную наладу"</string>
@@ -102,4 +105,5 @@
<string name="screen_room_roles_and_permissions_title">"Ролі і дазволы"</string>
<string name="screen_roomlist_mark_as_read">"Пазначыць як прачытанае"</string>
<string name="screen_roomlist_mark_as_unread">"Пазначыць як непрачытанае"</string>
<string name="screen_security_and_privacy_room_address_section_header">"Адрас"</string>
</resources>
@@ -6,7 +6,7 @@
<string name="screen_edit_room_address_room_address_section_footer">"Необходимо задать адрес комнаты, чтобы опубликовать ее в каталоге комнат."</string>
<string name="screen_edit_room_address_title">"Редактировать адрес комнаты"</string>
<string name="screen_notification_settings_edit_failed_updating_default_mode">"Произошла ошибка при обновлении настройки уведомления."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Ваш сервер не поддерживает эту опцию для зашифрованных комнат, вы можете не получать некоторые уведомления."</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Ваш сервер не поддерживает эту опцию для зашифрованных комнат, Вы можете не получать некоторые уведомления."</string>
<string name="screen_polls_history_title">"Опросы"</string>
<string name="screen_room_change_permissions_administrators">"Администраторы"</string>
<string name="screen_room_change_permissions_ban_people">"Блокировать пользователей"</string>
@@ -23,7 +23,7 @@
<string name="screen_room_change_permissions_room_topic">"Менять тему комнаты"</string>
<string name="screen_room_change_permissions_send_messages">"Отправлять сообщения"</string>
<string name="screen_room_change_role_administrators_title">"Редактировать администраторов"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Вы не сможете отменить это действие. Вы даете пользователю такой же уровень прав, как и у вас"</string>
<string name="screen_room_change_role_confirm_add_admin_description">"Вы не сможете отменить это действие. Вы даете пользователю такой же уровень прав, как и у Вас"</string>
<string name="screen_room_change_role_confirm_add_admin_title">"Добавить администратора?"</string>
<string name="screen_room_change_role_confirm_change_owners_description">"Отменить данное действие будет невозможно. Права передадутся выбранным пользователям. После вашего выхода действие станет необратимым."</string>
<string name="screen_room_change_role_confirm_change_owners_title">"Передать владение?"</string>
@@ -39,7 +39,7 @@
<string name="screen_room_change_role_section_administrators">"Администраторы"</string>
<string name="screen_room_change_role_section_moderators">"Модераторы"</string>
<string name="screen_room_change_role_section_users">"Участники"</string>
<string name="screen_room_change_role_unsaved_changes_description">"У вас есть несохраненные изменения."</string>
<string name="screen_room_change_role_unsaved_changes_description">"У Вас есть несохраненные изменения."</string>
<string name="screen_room_change_role_unsaved_changes_title">"Сохранить изменения?"</string>
<string name="screen_room_details_add_topic_title">"Добавить тему"</string>
<string name="screen_room_details_badge_encrypted">"Зашифровано"</string>
@@ -51,8 +51,8 @@
<string name="screen_room_details_encryption_enabled_subtitle">"Сообщения зашифрованы. Только вы и ваши собеседники имеют доступ к сообщениям."</string>
<string name="screen_room_details_encryption_enabled_title">"Шифрование сообщений включено"</string>
<string name="screen_room_details_error_loading_notification_settings">"Произошла ошибка при загрузке настроек уведомлений."</string>
<string name="screen_room_details_error_muting">"Не удалось отключить звук в этой комнате, попробуйте еще раз."</string>
<string name="screen_room_details_error_unmuting">"Не удалось включить звук в эту комнату, попробуйте еще раз."</string>
<string name="screen_room_details_error_muting">"Не удалось отключить звук в этой комнате, попробуйте ещё раз."</string>
<string name="screen_room_details_error_unmuting">"Не удалось включить звук в этой комнате, попробуйте ещё раз."</string>
<string name="screen_room_details_invite_people_dont_close">"Не закрывайте приложение, пока не закончите."</string>
<string name="screen_room_details_invite_people_preparing">"Подготовка приглашений…"</string>
<string name="screen_room_details_invite_people_title">"Пригласить в комнату"</string>
@@ -63,7 +63,7 @@
<string name="screen_room_details_notification_mode_custom">"Пользовательские"</string>
<string name="screen_room_details_notification_mode_default">"По умолчанию"</string>
<string name="screen_room_details_notification_title">"Уведомления"</string>
<string name="screen_room_details_pinned_events_row_title">"Закрепленные сообщения"</string>
<string name="screen_room_details_pinned_events_row_title">"Закреплённые сообщения"</string>
<string name="screen_room_details_profile_row_title">"Профиль"</string>
<string name="screen_room_details_requests_to_join_title">"Запросы на вступление"</string>
<string name="screen_room_details_roles_and_permissions">"Роли и разрешения"</string>
@@ -113,8 +113,8 @@
<string name="screen_room_notification_settings_default_setting_title">"Настройка по умолчанию"</string>
<string name="screen_room_notification_settings_edit_remove_setting">"Удалить пользовательскую настройку"</string>
<string name="screen_room_notification_settings_error_loading_settings">"Произошла ошибка при загрузке настроек уведомлений."</string>
<string name="screen_room_notification_settings_error_restoring_default">"Не удалось восстановить режим по умолчанию, попробуйте еще раз."</string>
<string name="screen_room_notification_settings_error_setting_mode">"Не удалось настроить режим, попробуйте еще раз."</string>
<string name="screen_room_notification_settings_error_restoring_default">"Не удалось восстановить режим по умолчанию, попробуйте ещё раз."</string>
<string name="screen_room_notification_settings_error_setting_mode">"Не удалось настроить режим, попробуйте ещё раз."</string>
<string name="screen_room_notification_settings_mentions_only_disclaimer">"Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, вы не будете получать уведомления в этой комнате."</string>
<string name="screen_room_notification_settings_mode_all_messages">"О всех сообщениях"</string>
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Только упоминания и ключевые слова"</string>
@@ -153,7 +153,7 @@
<string name="screen_security_and_privacy_room_access_anyone_option_description">"Любой желающий может присоединиться"</string>
<string name="screen_security_and_privacy_room_access_anyone_option_title">"Кто угодно"</string>
<string name="screen_security_and_privacy_room_access_footer">"Выберите, участники каких пространств могут присоединиться к этой комнате без приглашения.%1$s"</string>
<string name="screen_security_and_privacy_room_access_footer_manage_spaces_action">"Управление пространством"</string>
<string name="screen_security_and_privacy_room_access_footer_manage_spaces_action">"Управление пространствами"</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Присоединиться могут только приглашенные люди."</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Только по приглашению"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Доступ"</string>
@@ -7,7 +7,7 @@
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Блакіроўка %1$s"</string>
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Яны змогуць зноў далучыцца да гэтага пакоя, калі іх запросяць."</string>
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Прагляд профілю"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Выдаліць удзельніка з пакоя"</string>
<string name="screen_bottom_sheet_manage_room_member_remove">"Выдаліць удзельніка"</string>
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Выдаліць удзельніка і забараніць далучацца ў будучыні?"</string>
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Выдаленне %1$s…"</string>
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Разблакіраваць"</string>
@@ -8,13 +8,13 @@
<string name="screen_chat_backup_key_storage_toggle_description">"Загрузить ключи с этого устройства"</string>
<string name="screen_chat_backup_key_storage_toggle_title">"Разрешить хранение ключей"</string>
<string name="screen_chat_backup_recovery_action_change">"Изменить ключ восстановления"</string>
<string name="screen_chat_backup_recovery_action_change_description">"Если вы потеряли доступ к другим устройствам, то сможете восстановить свою идентификацию и историю сообщений с помощью ключа восстановления."</string>
<string name="screen_chat_backup_recovery_action_change_description">"Ваши чаты автоматически резервируются с помощью сквозного шифрования. Для восстановления резервной копии и сохранения Вашей цифровой личности в случае потери доступа ко всем устройствам потребуется ключ восстановления."</string>
<string name="screen_chat_backup_recovery_action_confirm">"Введите ключ восстановления"</string>
<string name="screen_chat_backup_recovery_action_confirm_description">"В настоящее время резервная копия ваших чатов не синхронизирована."</string>
<string name="screen_chat_backup_recovery_action_setup">"Получить ключ восстановления"</string>
<string name="screen_chat_backup_recovery_action_setup_description">"Ваши чаты автоматически резервируются с использованием сквозного шифрования. Для восстановления этой резервной копии и сохранения вашей цифровой личности в случае потери доступа ко всем вашим устройствам вам потребуется ключ восстановления."</string>
<string name="screen_create_new_recovery_key_list_item_1">"Откройте %1$s на компьютере"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Войдите в свой аккаунт еще раз"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Войдите в свой аккаунт ещё раз"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Когда потребуется подтвердить устройство, выберите %1$s"</string>
<string name="screen_create_new_recovery_key_list_item_3_reset_all">"«Сбросить все»"</string>
<string name="screen_create_new_recovery_key_list_item_4">"Следуйте инструкциям, чтобы создать новый ключ восстановления"</string>
@@ -24,7 +24,7 @@
<string name="screen_encryption_reset_bullet_1">"Данные вашего аккаунта, контакты, настройки и список чатов будут сохранены"</string>
<string name="screen_encryption_reset_bullet_2">"Вы потеряете историю тех сообщений, которые хранятся только на сервере"</string>
<string name="screen_encryption_reset_bullet_3">"Вам нужно будет заново подтвердить все существующие устройства и контакты."</string>
<string name="screen_encryption_reset_footer">"Сбрасывайте ключ шифрования только в том случае, если у вас нет доступа к другому устройству, на котором выполнен вход, и вы потеряли ключ восстановления."</string>
<string name="screen_encryption_reset_footer">"Сбрасывайте ключ шифрования только в том случае, если у Вас нет доступа к другому устройству, на котором выполнен вход, и Вы потеряли ключ восстановления."</string>
<string name="screen_encryption_reset_title">"Не можете подтвердить? Вам потребуется сбросить свою цифровую идентификацию."</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Удалить"</string>
<string name="screen_key_backup_disable_confirmation_description">"Вы потеряете зашифрованные сообщения, если выйдете из всех устройств."</string>
@@ -40,9 +40,9 @@
<string name="screen_recovery_key_change_title">"Изменить ключ восстановления?"</string>
<string name="screen_recovery_key_confirm_create_new_recovery_key">"Создать новый ключ восстановления"</string>
<string name="screen_recovery_key_confirm_description">"Убедитесь, что никто не подсматривает)"</string>
<string name="screen_recovery_key_confirm_error_content">"Пожалуйста, попробуйте еще раз, чтобы подтвердить доступ к резервной копии чата."</string>
<string name="screen_recovery_key_confirm_error_content">"Пожалуйста, попробуйте ещё раз, чтобы подтвердить доступ к резервной копии чата."</string>
<string name="screen_recovery_key_confirm_error_title">"Неверный ключ восстановления"</string>
<string name="screen_recovery_key_confirm_key_description">"Если у вас есть пароль для восстановления или секретный пароль/ключ, это тоже сработает."</string>
<string name="screen_recovery_key_confirm_key_description">"Если у Вас есть ключ/фраза для восстановления, это тоже сработает."</string>
<string name="screen_recovery_key_confirm_key_placeholder">"Введите…"</string>
<string name="screen_recovery_key_confirm_lost_recovery_key">"Потеряли ключ восстановления?"</string>
<string name="screen_recovery_key_confirm_success">"Ключ восстановления подтвержден"</string>
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_edit_room_address_title">"Рэдагаваць адрас"</string>
<string name="screen_manage_authorized_spaces_your_spaces_section_title">"Вашы прасторы"</string>
<string name="screen_security_and_privacy_room_address_section_header">"Адрас"</string>
</resources>
@@ -3,7 +3,7 @@
<string name="screen_edit_room_address_room_address_section_footer">"Необходимо задать адрес комнаты, чтобы опубликовать ее в каталоге комнат."</string>
<string name="screen_edit_room_address_title">"Редактировать адрес комнаты"</string>
<string name="screen_manage_authorized_spaces_header">"Пространства, где участники могут присоединиться к комнате без приглашения."</string>
<string name="screen_manage_authorized_spaces_title">"Управление пространством"</string>
<string name="screen_manage_authorized_spaces_title">"Управление пространствами"</string>
<string name="screen_manage_authorized_spaces_unknown_space">"(неизвестное пространство)"</string>
<string name="screen_manage_authorized_spaces_unknown_spaces_section_title">"Другие пространства, в которых вы не состоите"</string>
<string name="screen_manage_authorized_spaces_your_spaces_section_title">"Ваши пространства"</string>
@@ -23,7 +23,7 @@
<string name="screen_security_and_privacy_room_access_anyone_option_description">"Любой желающий может присоединиться"</string>
<string name="screen_security_and_privacy_room_access_anyone_option_title">"Кто угодно"</string>
<string name="screen_security_and_privacy_room_access_footer">"Выберите, участники каких пространств могут присоединиться к этой комнате без приглашения.%1$s"</string>
<string name="screen_security_and_privacy_room_access_footer_manage_spaces_action">"Управление пространством"</string>
<string name="screen_security_and_privacy_room_access_footer_manage_spaces_action">"Управление пространствами"</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_description">"Присоединиться могут только приглашенные люди."</string>
<string name="screen_security_and_privacy_room_access_invite_only_option_title">"Только по приглашению"</string>
<string name="screen_security_and_privacy_room_access_section_header">"Доступ"</string>
@@ -7,6 +7,7 @@
package io.element.android.features.share.impl
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.net.Uri
@@ -15,13 +16,14 @@ import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.share.api.OnSharedData
import io.element.android.features.share.api.ShareIntentData
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.annotations.ApplicationContext
import timber.log.Timber
import kotlin.collections.forEach
@ContributesBinding(AppScope::class)
class DefaultOnSharedData(
@ApplicationContext private val context: Context,
private val buildMeta: BuildMeta,
) : OnSharedData {
override fun invoke(data: ShareIntentData) {
when (data) {
@@ -29,6 +31,13 @@ class DefaultOnSharedData(
// No-op, there is nothing to do for plain text intents.
}
is ShareIntentData.Uris -> {
val fileProvider = "${buildMeta.applicationId}.fileprovider"
for (sharedUri in data.uris) {
// Remove the local copy of the shared file, as it is not needed anymore
if (sharedUri.uri.scheme == ContentResolver.SCHEME_CONTENT && sharedUri.uri.host == fileProvider) {
context.contentResolver.delete(sharedUri.uri, null)
}
}
revokeUriPermissions(data.uris.map { it.uri })
}
}
@@ -3,6 +3,6 @@
<string name="screen_signed_out_reason_1">"Вы изменили свой пароль в другой сессии"</string>
<string name="screen_signed_out_reason_2">"Вы удалили сессию из другой сессии"</string>
<string name="screen_signed_out_reason_3">"Администратор вашего сервера аннулировал ваш доступ"</string>
<string name="screen_signed_out_subtitle">"Возможно, вы вышли из системы по одной из причин, перечисленных ниже. Пожалуйста, войдите в систему еще раз, чтобы продолжить использование %s."</string>
<string name="screen_signed_out_subtitle">"Возможно, Вы вышли из системы по одной из причин, перечисленных ниже. Пожалуйста, войдите в систему ещё раз, чтобы продолжить использовать %s."</string>
<string name="screen_signed_out_title">"Вы вышли из системы"</string>
</resources>
@@ -11,11 +11,11 @@
<string name="screen_identity_use_another_device">"Выкарыстоўвайце іншую прыладу"</string>
<string name="screen_identity_waiting_on_other_device">"Чаканне на іншай прыладзе…"</string>
<string name="screen_session_verification_cancelled_subtitle">"Здаецца, нешта не так. Альбо час чакання запыту скончыўся, альбо запыт быў адхілены."</string>
<string name="screen_session_verification_compare_emojis_subtitle">"Пераканайцеся, што прыведзеныя ніжэй эмодзі супадаюць з эмодзі, паказанымі ў вашым іншым сеансе."</string>
<string name="screen_session_verification_compare_emojis_subtitle">"Пераканайцеся, што эмодзі ніжэй супадаюць з тымі, што паказаны на вашай іншай прыладзе."</string>
<string name="screen_session_verification_compare_emojis_title">"Параўнайце эмодзі"</string>
<string name="screen_session_verification_compare_numbers_subtitle">"Пераканайцеся, што прыведзеныя ніжэй лічбы супадаюць з лічбамі, паказанымі ў іншым сеансе."</string>
<string name="screen_session_verification_compare_numbers_title">"Параўнайце лічбы"</string>
<string name="screen_session_verification_complete_subtitle">"Ваш новы сеанс пацверджаны. Ён мае доступ да вашых зашыфраваных паведамленняў, і іншыя карыстальнікі будуць лічыць яго давераным."</string>
<string name="screen_session_verification_complete_subtitle">"Цяпер вы можаце бяспечна чытаць або адпраўляць паведамленні на іншай прыладзе."</string>
<string name="screen_session_verification_device_verified">"Прылада праверана"</string>
<string name="screen_session_verification_enter_recovery_key">"Увядзіце ключ аднаўлення"</string>
<string name="screen_session_verification_open_existing_session_subtitle">"Дакажыце, што гэта вы, каб атрымаць доступ да вашай зашыфраванай гісторыі паведамленняў."</string>
@@ -25,11 +25,11 @@
<string name="screen_session_verification_positive_button_verifying_ongoing">"Чаканне супадзення"</string>
<string name="screen_session_verification_ready_subtitle">"Параўнайце ўнікальны набор эмодзі."</string>
<string name="screen_session_verification_request_accepted_subtitle">"Параўнайце ўнікальныя эмодзі, пераканаўшыся, што яны размешчаны ў тым жа парадку."</string>
<string name="screen_session_verification_request_success_subtitle">"Ваш новы сеанс пацверджаны. Ён мае доступ да вашых зашыфраваных паведамленняў, і іншыя карыстальнікі будуць лічыць яго давераным."</string>
<string name="screen_session_verification_request_success_subtitle">"Цяпер вы можаце бяспечна чытаць або адпраўляць паведамленні на іншай прыладзе."</string>
<string name="screen_session_verification_request_success_title">"Прылада праверана"</string>
<string name="screen_session_verification_they_dont_match">"Яны не супадаюць"</string>
<string name="screen_session_verification_they_match">"Яны супадаюць"</string>
<string name="screen_session_verification_waiting_to_accept_subtitle">"Для працягу працы прыміце запыт на запуск працэсу праверкі ў іншым сеансе."</string>
<string name="screen_session_verification_waiting_to_accept_title">"Чаканне прыняцця запыту"</string>
<string name="screen_signout_in_progress_dialog_content">"Выхад…"</string>
<string name="screen_signout_in_progress_dialog_content">"Выдаленне прылады…"</string>
</resources>
@@ -6,7 +6,7 @@
<string name="screen_identity_confirmation_title">"Подтвердите личность"</string>
<string name="screen_identity_confirmation_use_another_device">"Использовать другое устройство"</string>
<string name="screen_identity_confirmation_use_recovery_key">"Использовать ключ восстановления"</string>
<string name="screen_identity_confirmed_subtitle">"Теперь вы можете безопасно читать и отправлять сообщения, и все, с кем вы общаетесь в чате, также могут доверять этому устройству."</string>
<string name="screen_identity_confirmed_subtitle">"Теперь Вы можете безопасно читать и отправлять сообщения, и все, с кем Вы общаетесь в чате, также могут доверять этому устройству."</string>
<string name="screen_identity_confirmed_title">"Устройство проверено"</string>
<string name="screen_identity_use_another_device">"Использовать другое устройство"</string>
<string name="screen_identity_waiting_on_other_device">"Ожидание другого устройства…"</string>
+2 -2
View File
@@ -84,7 +84,7 @@ kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin
kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" }
ksp_gradle_plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
# https://firebase.google.com/docs/android/setup#available-libraries
google_firebase_bom = "com.google.firebase:firebase-bom:34.14.0"
google_firebase_bom = "com.google.firebase:firebase-bom:34.14.1"
firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" }
autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" }
ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
@@ -147,7 +147,7 @@ accompanist_permission = { module = "com.google.accompanist:accompanist-permissi
squareup_seismic = "com.squareup:seismic:1.0.3"
# network
network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:5.3.2"
network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:5.4.0"
network_okhttp_logging = { module = "com.squareup.okhttp3:logging-interceptor" }
network_okhttp_okhttp = { module = "com.squareup.okhttp3:okhttp" }
network_okhttp = { module = "com.squareup.okhttp3:okhttp" }
@@ -27,7 +27,7 @@
<string name="state_event_room_invite_accepted">"%1$s принял(а) приглашение"</string>
<string name="state_event_room_invite_accepted_by_you">"Вы приняли приглашение"</string>
<string name="state_event_room_invite_by_you">"Вы пригласили %1$s"</string>
<string name="state_event_room_invite_you">"%1$s пригласил(а) вас"</string>
<string name="state_event_room_invite_you">"%1$s пригласил(а) Вас"</string>
<string name="state_event_room_join">"%1$s присоединился(ась)"</string>
<string name="state_event_room_join_by_you">"Вы присоединились к комнате"</string>
<string name="state_event_room_knock">"%1$s хочет присоединиться"</string>
@@ -47,8 +47,8 @@
<string name="state_event_room_name_removed_by_you">"Вы удалили название"</string>
<string name="state_event_room_none">"%1$s не внес(ла) изменений"</string>
<string name="state_event_room_none_by_you">"Вы не внесли изменений"</string>
<string name="state_event_room_pinned_events_changed">"%1$s изменил(а) закрепленные сообщения"</string>
<string name="state_event_room_pinned_events_changed_by_you">"Вы изменили закрепленные сообщения"</string>
<string name="state_event_room_pinned_events_changed">"%1$s изменил(а) закреплённые сообщения"</string>
<string name="state_event_room_pinned_events_changed_by_you">"Вы изменили закреплённые сообщения"</string>
<string name="state_event_room_pinned_events_pinned">"%1$s закрепил(а) сообщение"</string>
<string name="state_event_room_pinned_events_pinned_by_you">"Вы закрепили сообщение"</string>
<string name="state_event_room_pinned_events_unpinned">"%1$s открепил(а) сообщение"</string>
@@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.media.MediaPreviewService
import io.element.android.libraries.matrix.api.notification.NotificationService
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.matrix.api.pusher.PushersService
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.JoinedRoom
@@ -53,6 +54,7 @@ import java.util.Optional
interface MatrixClient {
val sessionId: SessionId
val deviceId: DeviceId
val sessionPaths: SessionPaths
val userProfile: StateFlow<MatrixUser>
val roomListService: RoomListService
val spaceService: SpaceService
@@ -10,5 +10,5 @@ package io.element.android.libraries.matrix.api.analytics
import io.element.android.libraries.matrix.api.core.SessionId
fun interface GetDatabaseSizesUseCase {
suspend operator fun invoke(sessionId: SessionId): Result<SdkStoreSizes>
operator fun invoke(sessionId: SessionId): Result<SdkStoreSizes>
}
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.paths
import java.io.File
data class SessionPaths(
val fileDirectory: File,
val cacheDirectory: File,
) {
fun deleteRecursively() {
fileDirectory.deleteRecursively()
cacheDirectory.deleteRecursively()
}
}
@@ -32,6 +32,7 @@ import io.element.android.libraries.matrix.api.linknewdevice.LinkDesktopHandler
import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileHandler
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.room.JoinedRoom
@@ -81,7 +82,6 @@ import io.element.android.libraries.matrix.impl.spaces.RustSpaceService
import io.element.android.libraries.matrix.impl.sync.RustSyncService
import io.element.android.libraries.matrix.impl.sync.map
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
import io.element.android.libraries.matrix.impl.util.SessionPathsProvider
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService
@@ -137,6 +137,7 @@ import org.matrix.rustcomponents.sdk.RoomPreset as RustRoomPreset
import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService
class RustMatrixClient(
override val sessionPaths: SessionPaths,
private val innerClient: Client,
private val sessionStore: SessionStore,
private val sessionDelegate: RustClientSessionDelegate,
@@ -187,8 +188,6 @@ class RustMatrixClient(
sessionDispatcher = sessionDispatcher,
)
private val sessionPathsProvider = SessionPathsProvider(sessionStore)
private val roomSyncSubscriber: RoomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers)
override val roomListService: RoomListService = RustRoomListService(
@@ -824,17 +823,16 @@ class RustMatrixClient(
private suspend fun getCacheSize(
includeCryptoDb: Boolean = false,
): Long = withContext(sessionDispatcher) {
val sessionDirectory = sessionPathsProvider.provides(sessionId) ?: return@withContext 0L
val cacheSize = sessionDirectory.cacheDirectory.getSizeOfFiles()
val cacheSize = sessionPaths.cacheDirectory.getSizeOfFiles()
if (includeCryptoDb) {
cacheSize + sessionDirectory.fileDirectory.getSizeOfFiles()
cacheSize + sessionPaths.fileDirectory.getSizeOfFiles()
} else {
cacheSize + listOf(
"matrix-sdk-state.sqlite3",
"matrix-sdk-state.sqlite3-shm",
"matrix-sdk-state.sqlite3-wal",
).map { fileName ->
File(sessionDirectory.fileDirectory, fileName)
File(sessionPaths.fileDirectory, fileName)
}.sumOf { file ->
file.length()
}
@@ -843,7 +841,7 @@ class RustMatrixClient(
private suspend fun deleteSessionDirectory() = withContext(sessionDispatcher) {
// Delete all the files for this session
sessionPathsProvider.provides(sessionId)?.deleteRecursively()
sessionPaths.deleteRecursively()
}
private fun scheduleDatabaseVacuum() {
@@ -17,9 +17,9 @@ import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
import io.element.android.libraries.matrix.impl.paths.SessionPaths
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
import io.element.android.libraries.matrix.impl.room.TimelineEventFilterFactory
@@ -102,10 +102,10 @@ class RustMatrixClientFactory(
client.restoreSession(sessionData.toSession())
create(client)
create(client, sessionData)
}
suspend fun create(client: Client): RustMatrixClient {
suspend fun create(client: Client, sessionData: SessionData): RustMatrixClient {
val (anonymizedAccessToken, anonymizedRefreshToken) = client.session().anonymizedTokens()
// Must be called before creating the sync service, timelines etc.
@@ -121,6 +121,7 @@ class RustMatrixClientFactory(
.finish()
return RustMatrixClient(
sessionPaths = sessionData.getSessionPaths(),
innerClient = client,
sessionStore = sessionStore,
appCoroutineScope = appCoroutineScope,
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
@@ -9,19 +9,53 @@ package io.element.android.libraries.matrix.impl.analytics
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.core.data.bytes
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase
import io.element.android.libraries.matrix.api.analytics.SdkStoreSizes
import io.element.android.libraries.matrix.api.core.SessionId
import java.io.File
import kotlin.math.max
/**
* An implementation of [GetDatabaseSizesUseCase] that manually calculates the database sizes by checking the file system.
* This is not as accurate as the SDK's internal method, but is used as a fallback for Sentry's non-suspendable hook where
* calling the SDK's suspendable method blocks the main thread and causes ANRs.
*/
@ContributesBinding(AppScope::class)
class DefaultGetDatabaseSizesUseCase(
private val clientProvider: Lazy<MatrixClientProvider>,
) : GetDatabaseSizesUseCase {
override suspend fun invoke(sessionId: SessionId): Result<SdkStoreSizes> {
override fun invoke(sessionId: SessionId): Result<SdkStoreSizes> {
val client = clientProvider.value.getOrNull(sessionId)
?: return Result.failure(IllegalArgumentException("No MatrixClient for session $sessionId"))
return client.getDatabaseSizes()
val fileDir = client.sessionPaths.fileDirectory
val cacheDir = client.sessionPaths.cacheDirectory
val cryptoSize = getDatabaseSize(fileDir, "matrix-sdk-crypto")
val stateSize = getDatabaseSize(fileDir, "matrix-sdk-state")
val eventCacheSize = getDatabaseSize(cacheDir, "matrix-sdk-event-cache")
val mediaCacheSize = getDatabaseSize(cacheDir, "matrix-sdk-media")
return Result.success(SdkStoreSizes(
stateStore = stateSize.bytes,
cryptoStore = cryptoSize.bytes,
eventCacheStore = eventCacheSize.bytes,
mediaStore = mediaCacheSize.bytes,
))
}
private fun getDatabaseSize(baseDirectory: File, databaseFileName: String): Long {
// Check the sizes of both the main database file and the -wal file, and return the max of the two:
// The -wal file can contain additional data not yet merged into the main file, becoming larger than it.
val databaseFile = File(baseDirectory, "$databaseFileName.sqlite3")
val walFile = File(baseDirectory, "$databaseFileName.sqlite3-wal")
val databaseSize = if (databaseFile.exists()) databaseFile.length() else 0L
val walSize = if (walFile.exists()) walFile.length() else 0L
return max(databaseSize, walSize)
}
}
@@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginDat
import io.element.android.libraries.matrix.api.auth.qrlogin.QrCodeLoginStep
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.libraries.matrix.impl.ClientBuilderSlidingSync
import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
@@ -38,7 +39,6 @@ import io.element.android.libraries.matrix.impl.auth.qrlogin.toStep
import io.element.android.libraries.matrix.impl.exception.mapClientException
import io.element.android.libraries.matrix.impl.keys.SecretGenerator
import io.element.android.libraries.matrix.impl.mapper.toSessionData
import io.element.android.libraries.matrix.impl.paths.SessionPaths
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
import io.element.android.libraries.matrix.impl.toSession
import io.element.android.libraries.sessionstorage.api.LoginType
@@ -160,7 +160,7 @@ class RustMatrixAuthenticationService(
passphrase = pendingKey.formattedAsString(),
sessionPaths = currentSessionPaths,
)
val matrixClient = rustMatrixClientFactory.create(client)
val matrixClient = rustMatrixClientFactory.create(client, sessionData)
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
sessionStore.addSession(sessionData)
@@ -235,7 +235,7 @@ class RustMatrixAuthenticationService(
// We restore the client using the just retrieved session data
client.restoreSession(sessionData.toSession())
val matrixClient = rustMatrixClientFactory.create(client)
val matrixClient = rustMatrixClientFactory.create(client, sessionData)
// We wait for the verification state to be known
matrixClient.waitForKnownVerificationState()
@@ -325,7 +325,7 @@ class RustMatrixAuthenticationService(
passphrase = pendingKey.formattedAsString(),
sessionPaths = currentSessionPaths,
)
val matrixClient = rustMatrixClientFactory.create(client)
val matrixClient = rustMatrixClientFactory.create(client, sessionData)
matrixClient.waitForKnownVerificationState()
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
@@ -390,7 +390,7 @@ class RustMatrixAuthenticationService(
passphrase = pendingKey.formattedAsString(),
sessionPaths = emptySessionPaths,
)
val matrixClient = rustMatrixClientFactory.create(client)
val matrixClient = rustMatrixClientFactory.create(client, sessionData)
newMatrixClientObservers.forEach { it.invoke(matrixClient) }
sessionStore.addSession(sessionData)
@@ -9,7 +9,7 @@
package io.element.android.libraries.matrix.impl.mapper
import io.element.android.libraries.matrix.api.auth.external.ExternalSession
import io.element.android.libraries.matrix.impl.paths.SessionPaths
import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.sessionstorage.api.LoginType
import io.element.android.libraries.sessionstorage.api.SessionData
import org.matrix.rustcomponents.sdk.Session
@@ -8,19 +8,10 @@
package io.element.android.libraries.matrix.impl.paths
import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.sessionstorage.api.SessionData
import java.io.File
data class SessionPaths(
val fileDirectory: File,
val cacheDirectory: File,
) {
fun deleteRecursively() {
fileDirectory.deleteRecursively()
cacheDirectory.deleteRecursively()
}
}
internal fun SessionData.getSessionPaths(): SessionPaths {
return SessionPaths(
fileDirectory = File(sessionPath),
@@ -11,6 +11,7 @@ package io.element.android.libraries.matrix.impl.paths
import dev.zacsweers.metro.Inject
import io.element.android.libraries.di.BaseDirectory
import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.matrix.api.paths.SessionPaths
import java.io.File
import java.util.UUID
@@ -8,6 +8,7 @@
package io.element.android.libraries.matrix.impl.room
import io.element.android.appconfig.TimelineConfig
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.childScope
import io.element.android.libraries.core.extensions.mapFailure
@@ -222,7 +223,13 @@ class JoinedRustRoom(
)
is CreateTimelineParams.Focused,
CreateTimelineParams.PinnedOnly,
is CreateTimelineParams.Threaded -> TimelineFilter.All
is CreateTimelineParams.Threaded -> {
RustTimelineEventFilterFactory().create(
joinRule = roomInfoFlow.value.joinRule,
isEncrypted = roomInfoFlow.value.isEncrypted,
excludedStateTypes = TimelineConfig.excludedEvents,
)?.let(TimelineFilter::EventFilter) ?: TimelineFilter.All
}
}
val internalIdPrefix = when (createTimelineParams) {
@@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
import io.element.android.libraries.matrix.impl.room.join.map
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
@@ -41,6 +42,7 @@ import org.matrix.rustcomponents.sdk.TimelineConfiguration
import org.matrix.rustcomponents.sdk.TimelineFilter
import org.matrix.rustcomponents.sdk.TimelineFocus
import timber.log.Timber
import uniffi.matrix_sdk_base.EncryptionState
import uniffi.matrix_sdk_ui.TimelineReadReceiptTracking
import java.util.concurrent.atomic.AtomicBoolean
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService
@@ -66,12 +68,6 @@ class RustRoomFactory(
private val mutex = Mutex()
private val isDestroyed: AtomicBoolean = AtomicBoolean(false)
private val eventFilters = TimelineConfig.excludedEvents
.takeIf { it.isNotEmpty() }
?.let { listStateEventType ->
timelineEventFilterFactory.create(listStateEventType)
}
suspend fun destroy() {
withContext(NonCancellable + dispatcher) {
mutex.withLock {
@@ -128,10 +124,20 @@ class RustRoomFactory(
operation = "sdkRoom.timelineWithConfiguration",
description = "Get timeline from the SDK",
) {
val isEncrypted = when (roomInfo.encryptionState) {
EncryptionState.ENCRYPTED -> true
EncryptionState.NOT_ENCRYPTED -> false
EncryptionState.UNKNOWN -> null
}
val timelineFilter = timelineEventFilterFactory.create(
joinRule = roomInfo.joinRule?.map(),
isEncrypted = isEncrypted,
excludedStateTypes = TimelineConfig.excludedEvents,
)
sdkRoom.timelineWithConfiguration(
TimelineConfiguration(
focus = TimelineFocus.Live(hideThreadedEvents = hideThreadedEvents),
filter = eventFilters?.let(TimelineFilter::EventFilter) ?: TimelineFilter.All,
filter = timelineFilter?.let(TimelineFilter::EventFilter) ?: TimelineFilter.All,
internalIdPrefix = "live",
dateDividerMode = DateDividerMode.DAILY,
trackReadReceipts = TimelineReadReceiptTracking.ALL_EVENTS,
@@ -11,20 +11,45 @@ package io.element.android.libraries.matrix.impl.room
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.api.room.join.JoinRule
import org.matrix.rustcomponents.sdk.FilterTimelineEventCondition
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
import org.matrix.rustcomponents.sdk.TimelineEventFilter
import uniffi.matrix_sdk_ui.MembershipChangeFilter
interface TimelineEventFilterFactory {
fun create(listStateEventType: List<StateEventType>): TimelineEventFilter
fun create(
joinRule: JoinRule?,
isEncrypted: Boolean?,
excludedStateTypes: List<StateEventType>
): TimelineEventFilter?
}
@ContributesBinding(AppScope::class)
class RustTimelineEventFilterFactory : TimelineEventFilterFactory {
override fun create(listStateEventType: List<StateEventType>): TimelineEventFilter {
return TimelineEventFilter.excludeEventTypes(
listStateEventType.map { stateEventType ->
FilterTimelineEventType.State(stateEventType.map())
}
)
override fun create(
joinRule: JoinRule?,
isEncrypted: Boolean?,
excludedStateTypes: List<StateEventType>
): TimelineEventFilter? {
val excludedEventTypes = excludedStateTypes.map {
FilterTimelineEventCondition.EventType(FilterTimelineEventType.State(it.map()))
}
// If the room is publicly joinable and not encrypted, we also want to exclude membership changes and profile changes,
// as they will pollute the timelines since they're quite common and not add much value.
val excludedMembershipChanges = if (joinRule !is JoinRule.Invite && isEncrypted == false) {
listOf(
FilterTimelineEventCondition.MembershipChange(MembershipChangeFilter.JOIN),
FilterTimelineEventCondition.MembershipChange(MembershipChangeFilter.LEAVE),
FilterTimelineEventCondition.ProfileChange,
)
} else {
emptyList()
}
return if (excludedEventTypes.isNotEmpty() || excludedMembershipChanges.isNotEmpty()) {
TimelineEventFilter.exclude(excludedEventTypes + excludedMembershipChanges)
} else {
null
}
}
}
@@ -10,7 +10,7 @@ package io.element.android.libraries.matrix.impl.storage
import io.element.android.libraries.androidutils.crypto.ClientSecret
import io.element.android.libraries.core.data.ByteUnit
import io.element.android.libraries.core.data.megaBytes
import io.element.android.libraries.matrix.impl.paths.SessionPaths
import io.element.android.libraries.matrix.api.paths.SessionPaths
import org.matrix.rustcomponents.sdk.ClientBuilder
import org.matrix.rustcomponents.sdk.SqliteStoreBuilder as SdkSqliteStoreBuilder
@@ -9,7 +9,7 @@ package io.element.android.libraries.matrix.impl.storage
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.matrix.impl.paths.SessionPaths
import io.element.android.libraries.matrix.api.paths.SessionPaths
interface SqliteStoreBuilderProvider {
fun provide(sessionPaths: SessionPaths): SqliteStoreBuilder
@@ -38,8 +38,6 @@ import io.element.android.libraries.matrix.impl.room.location.into
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
import io.element.android.libraries.matrix.impl.timeline.postprocessor.FilterEmptyDayPostProcessor
import io.element.android.libraries.matrix.impl.timeline.postprocessor.FilterPublicMembershipChangesPostProcessor
import io.element.android.libraries.matrix.impl.timeline.postprocessor.LastForwardIndicatorsPostProcessor
import io.element.android.libraries.matrix.impl.timeline.postprocessor.LoadingIndicatorsPostProcessor
import io.element.android.libraries.matrix.impl.timeline.postprocessor.RoomBeginningPostProcessor
@@ -125,8 +123,6 @@ class RustTimeline(
private val loadingIndicatorsPostProcessor = LoadingIndicatorsPostProcessor(systemClock)
private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(mode)
private val typingNotificationPostProcessor = TypingNotificationPostProcessor(mode)
private val publicMembershipChangesPostProcessor = FilterPublicMembershipChangesPostProcessor()
private val emptyDayPostProcessor = FilterEmptyDayPostProcessor()
private data class RoomTimelineInfo(
val roomCreators: ImmutableList<UserId>,
@@ -252,18 +248,6 @@ class RustTimeline(
hasMoreToLoadBackwards = backwardPaginationStatus.hasMoreToLoad,
)
}
// This should be the first post processor after room beginning.
.let { items ->
publicMembershipChangesPostProcessor.process(
items = items,
joinRule = joinRule,
isEncrypted = isEncrypted,
)
}
// After removing public membership changes, we might end up with empty days, so we need to filter them out.
.let { items ->
emptyDayPostProcessor.process(items)
}
.let { items ->
loadingIndicatorsPostProcessor.process(
items = items,
@@ -1,40 +0,0 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.timeline.postprocessor
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
/**
* Post-processor to filter out day separators for days that don't contain any events.
*/
class FilterEmptyDayPostProcessor {
/**
* Filters out day separators from [items] for days that don't contain any events.
*/
fun process(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> = buildList {
// The timeline is ordered by ascending timestamp, so events that happened during a day appear after the day separator for that day.
// We can use this to determine if a day separator should be kept or not by traversing the list in reverse.
var hasEvent = false
for (item in items.asReversed()) {
if (item is MatrixTimelineItem.Event) {
hasEvent = true
add(item)
} else if (item is MatrixTimelineItem.Virtual && item.virtual is VirtualTimelineItem.DayDivider) {
if (hasEvent) {
add(item)
hasEvent = false
}
} else {
add(item)
}
}
}
// Then reverse the result to restore the original order, minus the empty day separators.
.asReversed()
}
@@ -1,43 +0,0 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.timeline.postprocessor
import io.element.android.libraries.matrix.api.room.join.JoinRule
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
/**
* Post-processor to filter out public membership changes for non-encrypted, publicly joinable rooms.
*/
class FilterPublicMembershipChangesPostProcessor {
/**
* Filters out public membership changes from [items] if the room is publicly joinable and not encrypted.
*/
fun process(
items: List<MatrixTimelineItem>,
joinRule: JoinRule?,
isEncrypted: Boolean?,
): List<MatrixTimelineItem> {
return if (joinRule !is JoinRule.Invite && isEncrypted == false) {
filterMembershipEvents(items)
} else {
items
}
}
private fun filterMembershipEvents(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> = items.filter { item ->
val eventContent = (item as? MatrixTimelineItem.Event)?.event?.content ?: return@filter true
when (eventContent) {
is RoomMembershipContent -> eventContent.change != null && eventContent.change !in listOf(MembershipChange.JOINED, MembershipChange.LEFT)
is ProfileChangeContent -> false
else -> true
}
}
}
@@ -1,23 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.util
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.impl.paths.SessionPaths
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
import io.element.android.libraries.sessionstorage.api.SessionStore
class SessionPathsProvider(
private val sessionStore: SessionStore,
) {
suspend fun provides(sessionId: SessionId): SessionPaths? {
val sessionData = sessionStore.getSession(sessionId.value) ?: return null
return sessionData.getSessionPaths()
}
}
@@ -13,6 +13,7 @@ package io.element.android.libraries.matrix.impl
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.core.data.bytes
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSyncService
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory
@@ -141,6 +142,7 @@ class RustMatrixClientTest {
),
) = RustMatrixClient(
innerClient = client,
sessionPaths = SessionPaths(fileDirectory = File("files"), cacheDirectory = File("cache")),
sessionStore = sessionStore,
appCoroutineScope = backgroundScope,
sessionDelegate = aRustClientSessionDelegate(
@@ -10,8 +10,8 @@ package io.element.android.libraries.matrix.impl.mapper
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.auth.external.ExternalSession
import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSession
import io.element.android.libraries.matrix.impl.paths.SessionPaths
import io.element.android.libraries.matrix.test.A_DEVICE_ID
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2
@@ -9,11 +9,12 @@
package io.element.android.libraries.matrix.impl.room
import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.api.room.join.JoinRule
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiTimelineEventFilter
import org.matrix.rustcomponents.sdk.TimelineEventFilter
class FakeTimelineEventFilterFactory : TimelineEventFilterFactory {
override fun create(listStateEventType: List<StateEventType>): TimelineEventFilter {
override fun create(joinRule: JoinRule?, isEncrypted: Boolean?, excludedStateTypes: List<StateEventType>): TimelineEventFilter {
return FakeFfiTimelineEventFilter()
}
}
@@ -7,7 +7,7 @@
package io.element.android.libraries.matrix.impl.storage
import io.element.android.libraries.matrix.impl.paths.SessionPaths
import io.element.android.libraries.matrix.api.paths.SessionPaths
class FakeSqliteStoreBuilderProvider : SqliteStoreBuilderProvider {
override fun provide(sessionPaths: SessionPaths): SqliteStoreBuilder {
@@ -1,118 +0,0 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.timeline.postprocessor
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.core.UniqueId
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
import org.junit.Test
private const val TODAY = 1_779_779_967_000
private const val YESTERDAY = TODAY - 24 * 60 * 60 * 1000
private const val DAY_BEFORE_YESTERDAY = YESTERDAY - 24 * 60 * 60 * 1000
class FilterEmptyDayPostProcessorTest {
private val anEvent = MatrixTimelineItem.Event(
uniqueId = UniqueId("event"),
event = anEventTimelineItem(),
)
private fun aDaySeparator(timestmap: Long) = MatrixTimelineItem.Virtual(
uniqueId = UniqueId("day_$timestmap"),
virtual = VirtualTimelineItem.DayDivider(timestmap)
)
@Test
fun `filterEmptyDaySeparators keeps day separator with events after it`() {
val items = listOf(
aDaySeparator(TODAY),
anEvent,
)
val result = FilterEmptyDayPostProcessor().process(items)
assertThat(result).hasSize(2)
assertThat(result[0]).isEqualTo(aDaySeparator(TODAY))
assertThat(result[1]).isEqualTo(anEvent)
}
@Test
fun `filterEmptyDaySeparators removes day separator with no events after it`() {
val items = listOf(
aDaySeparator(YESTERDAY),
aDaySeparator(TODAY),
)
val result = FilterEmptyDayPostProcessor().process(items)
assertThat(result).isEmpty()
}
@Test
fun `filterEmptyDaySeparators removes second day separator and keeps first when only first has events`() {
val items = listOf(
aDaySeparator(YESTERDAY),
anEvent,
aDaySeparator(TODAY),
)
val result = FilterEmptyDayPostProcessor().process(items)
assertThat(result).hasSize(2)
assertThat(result[0]).isEqualTo(aDaySeparator(YESTERDAY))
assertThat(result[1]).isEqualTo(anEvent)
}
@Test
fun `filterEmptyDaySeparators handles multiple day separators in a row with no events`() {
val items = listOf(
aDaySeparator(TODAY),
aDaySeparator(YESTERDAY),
aDaySeparator(DAY_BEFORE_YESTERDAY),
)
val result = FilterEmptyDayPostProcessor().process(items)
assertThat(result).isEmpty()
}
@Test
fun `filterEmptyDaySeparators keeps all items when no day separators`() {
val items = listOf(
anEvent.copy(uniqueId = UniqueId("event2")),
anEvent,
)
val result = FilterEmptyDayPostProcessor().process(items)
assertThat(result).hasSize(2)
}
@Test
fun `filterEmptyDaySeparators removes day separator preceded by non-event virtual item`() {
val readMarker = MatrixTimelineItem.Virtual(
uniqueId = UniqueId("readMarker"),
virtual = VirtualTimelineItem.ReadMarker
)
val items = listOf(
aDaySeparator(TODAY),
readMarker,
)
val result = FilterEmptyDayPostProcessor().process(items)
assertThat(result).hasSize(1)
assertThat(result[0]).isEqualTo(readMarker)
}
@Test
fun `filterEmptyDaySeparators keeps day separator when non-event virtual items are between separator and event`() {
val readMarker = MatrixTimelineItem.Virtual(
uniqueId = UniqueId("readMarker"),
virtual = VirtualTimelineItem.ReadMarker
)
val items = listOf(
aDaySeparator(TODAY),
readMarker,
anEvent,
)
val result = FilterEmptyDayPostProcessor().process(items)
assertThat(result).hasSize(3)
assertThat(result[0]).isEqualTo(aDaySeparator(TODAY))
}
}
@@ -1,75 +0,0 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.timeline.postprocessor
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.room.join.JoinRule
import org.junit.Test
class FilterPublicMembershipChangesPostProcessorTest {
@Test
fun `processor removes join, leave, and profile events in unencrypted public rooms`() {
val timelineItems = listOf(
roomCreateEvent,
roomCreatorJoinEvent,
otherMemberJoinEvent,
messageEvent,
otherMemberLeaveEvent,
profileChangeEvent,
)
val expected = listOf(
roomCreateEvent,
messageEvent,
)
val processor = FilterPublicMembershipChangesPostProcessor()
val processedItems = processor.process(
timelineItems,
joinRule = JoinRule.Public,
isEncrypted = false,
)
assertThat(processedItems).isEqualTo(expected)
}
@Test
fun `processor keeps all events in encrypted public rooms`() {
val timelineItems = listOf(
roomCreateEvent,
roomCreatorJoinEvent,
otherMemberJoinEvent,
messageEvent,
otherMemberLeaveEvent,
profileChangeEvent,
)
val processor = FilterPublicMembershipChangesPostProcessor()
val processedItems = processor.process(
timelineItems,
joinRule = JoinRule.Public,
isEncrypted = true,
)
assertThat(processedItems).isEqualTo(timelineItems)
}
@Test
fun `processor keeps membership events in invite-only rooms`() {
val timelineItems = listOf(
roomCreateEvent,
roomCreatorJoinEvent,
otherMemberJoinEvent,
messageEvent,
otherMemberLeaveEvent,
profileChangeEvent,
)
val processor = FilterPublicMembershipChangesPostProcessor()
val processedItems = processor.process(
timelineItems,
joinRule = JoinRule.Invite,
isEncrypted = null,
)
assertThat(processedItems).isEqualTo(timelineItems)
}
}
@@ -1,41 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.util
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.sessionstorage.test.InMemorySessionStore
import io.element.android.libraries.sessionstorage.test.aSessionData
import kotlinx.coroutines.test.runTest
import org.junit.Test
class SessionPathsProviderTest {
@Test
fun `if session is not found, provides returns null`() = runTest {
val sut = SessionPathsProvider(InMemorySessionStore())
val result = sut.provides(A_SESSION_ID)
assertThat(result).isNull()
}
@Test
fun `if session is found, provides returns the data`() = runTest {
val store = InMemorySessionStore(
initialList = listOf(
aSessionData(
sessionPath = "/a/path/to/a/session",
cachePath = "/a/path/to/a/cache",
)
)
)
val sut = SessionPathsProvider(store)
val result = sut.provides(A_SESSION_ID)!!
assertThat(result.fileDirectory.absolutePath).isEqualTo("/a/path/to/a/session")
assertThat(result.cacheDirectory.absolutePath).isEqualTo("/a/path/to/a/cache")
}
}
@@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.api.media.MediaPreviewService
import io.element.android.libraries.matrix.api.notification.NotificationService
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
import io.element.android.libraries.matrix.api.paths.SessionPaths
import io.element.android.libraries.matrix.api.pusher.PushersService
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.JoinedRoom
@@ -66,10 +67,12 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
import java.io.File
import java.util.Optional
class FakeMatrixClient(
override val sessionId: SessionId = A_SESSION_ID,
override val sessionPaths: SessionPaths = SessionPaths(fileDirectory = File("files"), cacheDirectory = File("cache")),
override val deviceId: DeviceId = A_DEVICE_ID,
override val sessionCoroutineScope: CoroutineScope = TestScope(),
private val userDisplayName: String? = A_USER_NAME,
@@ -5,5 +5,5 @@
<string name="screen_bottom_sheet_create_dm_title">"Отправить приглашение?"</string>
<string name="screen_bottom_sheet_create_dm_unknown_user_content">"У Вас нет других чатов с этим пользователем. Подтвердите, что это действительно кого Вы хотите пригласить, прежде чем продолжить."</string>
<string name="screen_bottom_sheet_create_dm_unknown_user_title">"Начать чат с этим новым контактом?"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) пригласил(а) вас"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) пригласил(а) Вас"</string>
</resources>
@@ -100,7 +100,11 @@ class AndroidLocalMediaActions(
override suspend fun share(localMedia: LocalMedia): Result<Unit> = withContext(coroutineDispatchers.io) {
require(localMedia.uri.scheme == ContentResolver.SCHEME_FILE)
runCatchingExceptions {
val shareableUri = localMedia.toShareableUri()
// Make a copy of the shared file in the cache directory, otherwise the original file will be gone once this screen is dismissed
// and will prevent sharing the media to another room inside the app.
val copiedFile = localMedia.uri.toFile()
.copyTo(File(context.cacheDir, "temp/media/" + (localMedia.uri.lastPathSegment ?: "shared_file")), true)
val shareableUri = copiedFile.toShareableUri()
val shareMediaIntent = Intent(Intent.ACTION_SEND)
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, shareableUri)
@@ -157,10 +161,13 @@ class AndroidLocalMediaActions(
}
}
private fun LocalMedia.toShareableUri(): Uri {
val mediaAsFile = this.toFile()
private fun File.toShareableUri(): Uri {
val authority = "${buildMeta.applicationId}.fileprovider"
return FileProvider.getUriForFile(context, authority, mediaAsFile).normalizeScheme()
return FileProvider.getUriForFile(context, authority, this).normalizeScheme()
}
private fun LocalMedia.toShareableUri(): Uri {
return this.toFile().toShareableUri()
}
@RequiresApi(Build.VERSION_CODES.Q)
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_media_details_title">"Інфармацыя пра файл"</string>
</resources>
@@ -15,8 +15,8 @@
<item quantity="few">"%d уведомления"</item>
<item quantity="many">"%d уведомлений"</item>
</plurals>
<string name="notification_error_unified_push_unregistered_android">"Не удалось зарегистрировать дистрибьютора уведомлений UnifiedPush, поэтому вы больше не будете получать уведомления. Проверьте настройки уведомлений в приложении и статус дистрибьютора push-уведомлений."</string>
<string name="notification_fallback_content">"У вас есть новые сообщения."</string>
<string name="notification_error_unified_push_unregistered_android">"Не удалось зарегистрировать дистрибьютора уведомлений UnifiedPush, поэтому Вы больше не будете получать уведомления. Проверьте настройки уведомлений в приложении и статус дистрибьютора push-уведомлений."</string>
<string name="notification_fallback_content">"У Вас есть новые сообщения."</string>
<plurals name="notification_fallback_n_content">
<item quantity="one">"У Вас %d новое сообщение."</item>
<item quantity="few">"У Вас %d новых сообщения."</item>
@@ -32,8 +32,8 @@
<item quantity="few">"%d приглашения"</item>
<item quantity="many">"%d приглашений"</item>
</plurals>
<string name="notification_invite_body">"Пригласил(а) вас в чат"</string>
<string name="notification_invite_body_with_sender">"%1$s пригласил(а) вас в чат"</string>
<string name="notification_invite_body">"Пригласил(а) Вас в чат"</string>
<string name="notification_invite_body_with_sender">"%1$s пригласил(а) Вас в чат"</string>
<string name="notification_mentioned_you_body">"Упомянул(а) вас: %1$s"</string>
<string name="notification_new_messages">"Новые сообщения"</string>
<plurals name="notification_new_messages_for_room">
@@ -44,12 +44,12 @@
<string name="notification_reaction_body">"Отреагировал(а) на %1$s"</string>
<string name="notification_room_action_mark_as_read">"Пометить как прочитанное"</string>
<string name="notification_room_action_quick_reply">"Быстрый ответ"</string>
<string name="notification_room_invite_body">"Пригласил(а) вас в комнату"</string>
<string name="notification_room_invite_body_with_sender">"%1$s пригласил(а) вас в комнату"</string>
<string name="notification_room_invite_body">"Пригласил(а) Вас в комнату"</string>
<string name="notification_room_invite_body_with_sender">"%1$s пригласил(а) Вас в комнату"</string>
<string name="notification_sender_me">"Я"</string>
<string name="notification_sender_mention_reply">"%1$s упомянул(а) или ответил(а)"</string>
<string name="notification_space_invite_body">"Пригласил(а) вас в пространство"</string>
<string name="notification_space_invite_body_with_sender">"%1$s пригласил(а) вас в пространство"</string>
<string name="notification_space_invite_body">"Пригласил(а) Вас в пространство"</string>
<string name="notification_space_invite_body_with_sender">"%1$s пригласил(а) Вас в пространство"</string>
<string name="notification_test_push_notification_content">"Вы просматриваете уведомление! Нажмите на меня!"</string>
<string name="notification_thread_in_room">"Обсуждение в %1$s"</string>
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
@@ -4,7 +4,9 @@
<string name="rich_text_editor_bullet_list">"Пераключыць маркіраваны спіс"</string>
<string name="rich_text_editor_close_formatting_options">"Закрыць параметры фарматавання"</string>
<string name="rich_text_editor_code_block">"Пераключыць блок кода"</string>
<string name="rich_text_editor_composer_encrypted_placeholder">"Зашыфраванае паведамленне…"</string>
<string name="rich_text_editor_composer_placeholder">"Паведамленне…"</string>
<string name="rich_text_editor_composer_unencrypted_placeholder">"Незашыфраванае паведамленне…"</string>
<string name="rich_text_editor_create_link">"Стварыць спасылку"</string>
<string name="rich_text_editor_edit_link">"Рэдагаваць спасылку"</string>
<string name="rich_text_editor_format_bold">"Ужыць тоўсты шрыфт"</string>
@@ -7,6 +7,6 @@
<string name="troubleshoot_notifications_screen_notice">"Выполните тесты, чтобы выявить проблемы в конфигурации, из-за которой уведомления могут работать не так, как ожидалось."</string>
<string name="troubleshoot_notifications_screen_quick_fix_action">"Попытаться исправить"</string>
<string name="troubleshoot_notifications_screen_success">"Все тесты прошли успешно."</string>
<string name="troubleshoot_notifications_screen_title">"Уведомления об устранении неполадок"</string>
<string name="troubleshoot_notifications_screen_title">"Устранение неполадок с уведомлениями"</string>
<string name="troubleshoot_notifications_screen_waiting">"Некоторые тесты требуют вашего внимания. Пожалуйста, проверьте подробности."</string>
</resources>
@@ -13,6 +13,7 @@
<string name="a11y_edit_room_address_hint">"Поўны адрас будзе %1$s"</string>
<string name="a11y_encryption_details">"Дэталі шыфравання"</string>
<string name="a11y_hide_password">"Схаваць пароль"</string>
<string name="a11y_info">"Інфармацыя"</string>
<string name="a11y_join_call">"Далучыцца да выкліку"</string>
<string name="a11y_jump_to_bottom">"Перайсці ўніз"</string>
<string name="a11y_notifications_mentions_only">"Толькі згадкі"</string>
@@ -72,12 +73,16 @@
<string name="action_deactivate_account">"Дэактываваць уліковы запіс"</string>
<string name="action_decline">"Адхіліць"</string>
<string name="action_decline_and_block">"Адхіліць і заблакіраваць"</string>
<string name="action_delete">"Выдаліць"</string>
<string name="action_delete_account">"Выдаліць уліковы запіс"</string>
<string name="action_delete_file">"Выдаліць файл"</string>
<string name="action_delete_poll">"Выдаліць апытанне"</string>
<string name="action_deselect_all">"Зняць выбар з усіх"</string>
<string name="action_disable">"Адключыць"</string>
<string name="action_discard">"Адмяніць"</string>
<string name="action_dismiss">"Aдхіліць"</string>
<string name="action_done">"Гатова"</string>
<string name="action_download">"Спампаваць"</string>
<string name="action_edit">"Рэдагаваць"</string>
<string name="action_edit_caption">"Рэдагаваць подпіс"</string>
<string name="action_edit_poll">"Рэдагаваць апытанне"</string>
@@ -119,6 +124,7 @@
<string name="action_react">"Рэакцыя"</string>
<string name="action_reject">"Адхіліць"</string>
<string name="action_remove">"Выдаліць"</string>
<string name="action_remove_message">"Выдаліць паведамленне"</string>
<string name="action_reply">"Адказаць"</string>
<string name="action_reply_in_thread">"Адказаць у гутаркі"</string>
<string name="action_report">"Паскардзіцца"</string>
@@ -133,7 +139,7 @@
<string name="action_select_all">"Выбраць усё"</string>
<string name="action_send">"Адправіць"</string>
<string name="action_send_message">"Адправіць паведамленне"</string>
<string name="action_share">"Падзяліцца"</string>
<string name="action_share">"Абагуліць"</string>
<string name="action_share_link">"Абагуліць спасылку"</string>
<string name="action_show">"Паказаць"</string>
<string name="action_sign_in_again">"Увайдзіце яшчэ раз"</string>
@@ -214,8 +220,10 @@
<string name="common_message_removed">"Паведамленне выдалена"</string>
<string name="common_modern">"Сучасны"</string>
<string name="common_mute">"Адкл. гук"</string>
<string name="common_name">"Імя"</string>
<string name="common_no_results">"Вынікаў няма"</string>
<string name="common_no_room_name">"Няма назвы пакоя"</string>
<string name="common_not_encrypted">"Не зашыфравана"</string>
<string name="common_offline">"Па-за сеткай"</string>
<string name="common_open_source_licenses">"Ліцэнзіі з адкрытым зыходным кодам"</string>
<string name="common_or">"або"</string>
@@ -227,6 +235,7 @@
<string name="common_please_wait">"Калі ласка, пачакайце…"</string>
<string name="common_poll_end_confirmation">"Вы ўпэўнены, што хочаце скончыць гэтае апытанне?"</string>
<string name="common_poll_summary">"Апытанне: %1$s"</string>
<string name="common_poll_summary_prefix">"Апытанне"</string>
<string name="common_poll_total_votes">"Усяго галасоў: %1$s"</string>
<string name="common_poll_undisclosed_text">"Вынікі будуць паказаны пасля завяршэння апытання"</string>
<plurals name="common_poll_votes_count">
@@ -241,6 +250,7 @@
<string name="common_reactions">"Рэакцыі"</string>
<string name="common_recovery_key">"Ключ аднаўлення"</string>
<string name="common_refreshing">"Абнаўленне…"</string>
<string name="common_removing">"Выдаленне…"</string>
<string name="common_replying_to">"Адказвае на %1$s"</string>
<string name="common_report_a_problem">"Паведаміць аб праблеме"</string>
<string name="common_report_submitted">"Скарга прынята"</string>
@@ -262,6 +272,7 @@
<string name="common_server_not_supported">"Сервер не падтрымліваецца"</string>
<string name="common_server_url">"URL-адрас сервера"</string>
<string name="common_settings">"Налады"</string>
<string name="common_share_space">"Абагуліць прастору"</string>
<string name="common_shared_location">"Абагулена месцазнаходжанне"</string>
<string name="common_signing_out">"Выхад"</string>
<string name="common_something_went_wrong">"Нешта пайшло не так"</string>
@@ -315,6 +326,11 @@
<string name="invite_friends_text">"Гэй, пагавары са мной у %1$s: %2$s"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"Паведаміць аб памылцы з дапамогай Rageshake"</string>
<string name="screen_bug_report_a11y_screenshot">"Здымак экрана"</string>
<string name="screen_create_poll_option_accessibility_label">"%1$s: %2$s"</string>
<string name="screen_create_poll_options_section_title">"Параметры"</string>
<string name="screen_create_poll_remove_accessibility_label">"Выдаліць %1$s"</string>
<string name="screen_create_poll_settings_section_title">"Налады"</string>
<string name="screen_media_picker_error_failed_selection">"Не ўдалося выбраць носьбіт, паўтарыце спробу."</string>
<string name="screen_pinned_timeline_empty_state_description">"Націсніце на паведамленне і абярыце «%1$s », каб уключыць сюды."</string>
<string name="screen_pinned_timeline_empty_state_headline">"Замацуеце важныя паведамленні, каб іх можна было лёгка знайсці"</string>
@@ -335,8 +351,8 @@
<string name="screen_room_pinned_banner_loading_description">"Загрузка паведамлення…"</string>
<string name="screen_room_pinned_banner_view_all_button_title">"Паглядзець усе"</string>
<string name="screen_room_title">"Чат"</string>
<string name="screen_share_location_title">"Падзяліцца месцазнаходжаннем"</string>
<string name="screen_share_my_location_action">"Падзяліцца маім месцазнаходжаннем"</string>
<string name="screen_share_location_title">"Абагуліць месцазнаходжанне"</string>
<string name="screen_share_my_location_action">"Абагуліць мае месцазнаходжанне"</string>
<string name="screen_share_open_apple_maps">"Адкрыць у Apple Maps"</string>
<string name="screen_share_open_google_maps">"Адкрыць у Google Maps"</string>
<string name="screen_share_open_osm_maps">"Адкрыць у OpenStreetMap"</string>
@@ -54,7 +54,7 @@
<string name="a11y_send_files">"Отправить файлы"</string>
<string name="a11y_sender_location">"Местоположение отправителя"</string>
<string name="a11y_sent_by_sender_at_date">"Отправлено пользователем %1$s в %2$s"</string>
<string name="a11y_session_verification_time_limited_action_required">"Требуется действие, на которое есть ограничение по времени, у вас есть одна минута для проверки"</string>
<string name="a11y_session_verification_time_limited_action_required">"Требуется действие, на которое есть ограничение по времени, у Вас есть одна минута для проверки"</string>
<string name="a11y_settings_with_required_action">"Настройки, требуется действие"</string>
<string name="a11y_show_password">"Показать пароль"</string>
<string name="a11y_start_call">"Начать звонок"</string>
@@ -101,6 +101,7 @@
<string name="action_decline_and_block">"Отклонить и заблокировать"</string>
<string name="action_delete">"Удалить"</string>
<string name="action_delete_account">"Удалить аккаунт"</string>
<string name="action_delete_file">"Удалить файл"</string>
<string name="action_delete_poll">"Удалить опрос"</string>
<string name="action_deselect_all">"Отменить выбор"</string>
<string name="action_disable">"Отключить"</string>
@@ -133,7 +134,7 @@
<string name="action_leave_conversation">"Покинуть беседу"</string>
<string name="action_leave_room">"Покинуть комнату"</string>
<string name="action_leave_space">"Покинуть пространство"</string>
<string name="action_load_more">"Загрузить еще"</string>
<string name="action_load_more">"Загрузить ещё"</string>
<string name="action_manage_account">"Настройки аккаунта"</string>
<string name="action_manage_account_and_devices">"Управление учетной записью и устройствами"</string>
<string name="action_manage_devices">"Управление устройствами"</string>
@@ -179,7 +180,7 @@
<string name="action_show">"Показать"</string>
<string name="action_sign_in_again">"Повторить вход"</string>
<string name="action_signout">"Удалить это устройство"</string>
<string name="action_signout_anyway">"Все равно удалить это устройство"</string>
<string name="action_signout_anyway">"Всё равно удалить это устройство"</string>
<string name="action_skip">"Пропустить"</string>
<string name="action_start">"Начать"</string>
<string name="action_start_chat">"Начать чат"</string>
@@ -196,8 +197,8 @@
<string name="action_view_in_timeline">"Просмотреть в хронологии"</string>
<string name="action_view_source">"Показать источник"</string>
<string name="action_yes">"Да"</string>
<string name="action_yes_try_again">"Да, попробовать еще раз"</string>
<string name="banner_migrate_to_native_sliding_sync_description">"Теперь ваш сервер поддерживает новый, более быстрый протокол. Чтобы обновить его прямо сейчас, выйдите и войдите в свою учётную запись снова. Сделав это сейчас, вы сможете избежать принудительного выхода из системы при последующем удалении старого протокола."</string>
<string name="action_yes_try_again">"Да, попробовать ещё раз"</string>
<string name="banner_migrate_to_native_sliding_sync_description">"Теперь Ваш сервер поддерживает новый, более быстрый протокол. Чтобы обновить его прямо сейчас, выйдите и войдите в свою учётную запись снова. Сделав это сейчас, Вы сможете избежать принудительного выхода из системы при последующем удалении старого протокола."</string>
<string name="banner_migrate_to_native_sliding_sync_title">"Доступно обновление"</string>
<string name="common_about">"О приложении"</string>
<string name="common_acceptable_use_policy">"Политика допустимого использования"</string>
@@ -377,7 +378,7 @@
<string name="common_shared_space">"Общее пространство"</string>
<string name="common_signing_out">"Удаление устройства"</string>
<string name="common_something_went_wrong">"Что-то пошло не так"</string>
<string name="common_something_went_wrong_message">"Мы столкнулись с проблемой. Пожалуйста, попробуйте еще раз."</string>
<string name="common_something_went_wrong_message">"Мы столкнулись с проблемой. Пожалуйста, попробуйте ещё раз."</string>
<string name="common_space">"Пространство"</string>
<string name="common_space_members">"Участники пространства"</string>
<string name="common_space_topic_placeholder">"О чём это пространство?"</string>
@@ -429,8 +430,8 @@
<string name="common_waiting_live_location">"Ожидание трансляции местоположения…"</string>
<string name="common_world_readable_history">"Любой может видеть историю"</string>
<string name="common_you">"Вы"</string>
<string name="crypto_event_key_forwarded_known_profile_dialog_content">"%1$s (%2$s) поделился этим сообщением, поскольку вас не было в комнате, когда оно было отправлено."</string>
<string name="crypto_event_key_forwarded_unknown_profile_dialog_content">"%1$s поделился этим сообщением, поскольку вас не было в комнате, когда оно было отправлено."</string>
<string name="crypto_event_key_forwarded_known_profile_dialog_content">"%1$s (%2$s) поделился(ась) этим сообщением, поскольку Вас не было в комнате, когда оно было отправлено."</string>
<string name="crypto_event_key_forwarded_unknown_profile_dialog_content">"%1$s поделился(ась) этим сообщением, поскольку Вас не было в комнате, когда оно было отправлено."</string>
<string name="crypto_history_visible">"Эта комната настроена так, что новые участники могут видеть историю. %1$s"</string>
<string name="crypto_identity_change_pin_violation">"Личность %1$s была сброшена. %2$s"</string>
<string name="crypto_identity_change_pin_violation_new">"Личность %1$s %2$s была сброшена. %3$s"</string>
@@ -453,11 +454,11 @@
<string name="dialog_title_error">"Ошибка"</string>
<string name="dialog_title_success">"Успешно"</string>
<string name="dialog_title_warning">"Предупреждение"</string>
<string name="dialog_unsaved_changes_description">"У вас есть несохраненные изменения."</string>
<string name="dialog_unsaved_changes_description">"У Вас есть несохраненные изменения."</string>
<string name="dialog_unsaved_changes_description_android">"Изменения не сохранены. Вы действительно хотите вернуться?"</string>
<string name="dialog_unsaved_changes_title">"Сохранить изменения?"</string>
<string name="dialog_video_quality_selector_subtitle_file_size">"Максимально допустимый размер файла: %1$s"</string>
<string name="dialog_video_quality_selector_subtitle_no_file_size">"Выберите качество для видео, которое вы хотите загрузить."</string>
<string name="dialog_video_quality_selector_subtitle_no_file_size">"Выберите качество видео, которое Вы хотите загрузить."</string>
<string name="dialog_video_quality_selector_title">"Выберите качество загружаемого видео"</string>
<string name="emoji_picker_search_placeholder">"Поиск эмодзи"</string>
<string name="error_account_already_logged_in">"Вы уже вошли на данном устройстве как %1$s."</string>
@@ -498,7 +499,7 @@
<item quantity="many">"%1$d людей"</item>
</plurals>
<string name="screen_live_location_sheet_title">"На карте"</string>
<string name="screen_media_picker_error_failed_selection">"Не удалось выбрать медиа, попробуйте еще раз."</string>
<string name="screen_media_picker_error_failed_selection">"Не удалось выбрать медиа, попробуйте ещё раз."</string>
<string name="screen_pinned_timeline_empty_state_description">"Нажмите на сообщение и выберите «%1$s», чтобы добавить его сюда."</string>
<string name="screen_pinned_timeline_empty_state_headline">"Закрепите важные сообщения, чтобы их можно было легко найти"</string>
<plurals name="screen_pinned_timeline_screen_title">
@@ -506,19 +507,19 @@
<item quantity="few">"%1$d закреплённых сообщения"</item>
<item quantity="many">"%1$d закреплённых сообщений"</item>
</plurals>
<string name="screen_pinned_timeline_screen_title_empty">"Закрепленные сообщения"</string>
<string name="screen_pinned_timeline_screen_title_empty">"Закреплённые сообщения"</string>
<string name="screen_reset_identity_confirmation_subtitle">"Вы собираетесь перейти в свой аккаунт %1$s, чтобы сбросить личность. После этого Вы вернётесь в приложение."</string>
<string name="screen_reset_identity_confirmation_title">"Не можете подтвердить? Перейдите в свой аккаунт, чтобы сбросить свою личность."</string>
<string name="screen_resolve_send_failure_changed_identity_primary_button_title">"Сбросить верификацию и отправить"</string>
<string name="screen_resolve_send_failure_changed_identity_subtitle">"Вы можете либо сбросить подтверждение и всё равно отправить это сообщение, либо отменить его сейчас и повторить попытку после повторного подтверждения %1$s."</string>
<string name="screen_resolve_send_failure_changed_identity_title">"Ваше сообщение не было отправлено, потому что подтвержденная идентификации %1$s была сброшена"</string>
<string name="screen_resolve_send_failure_unsigned_device_primary_button_title">"Все равно отправить сообщение"</string>
<string name="screen_resolve_send_failure_unsigned_device_subtitle">"У %1$s есть одно или несколько неподтвержденных устройств. Вы все равно можете отправить сообщение или отменить его пока и повторить попытку позже, когда %2$s подтвердить все свои устройства."</string>
<string name="screen_resolve_send_failure_unsigned_device_primary_button_title">"Всё равно отправить сообщение"</string>
<string name="screen_resolve_send_failure_unsigned_device_subtitle">"У %1$s есть одно или несколько неподтвержденных устройств. Вы всё равно можете отправить сообщение или отменить его и повторить попытку позже, когда %2$s подтвердит все свои устройства."</string>
<string name="screen_resolve_send_failure_unsigned_device_title">"Ваше сообщение не было отправлено, потому что %1$s имеет неподтвержденные устройства"</string>
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"Одно или несколько ваших устройств не подтверждены. Вы можете отправить сообщение в любом случае или отменить его пока и повторить попытку позже, подтвердив все свои устройства."</string>
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Ваше сообщение не было отправлено, поскольку вы не подтвердили одно или несколько своих устройств."</string>
<string name="screen_room_change_role_administrators_or_owners_title">"Редактировать владельцев и администраторов"</string>
<string name="screen_room_error_failed_processing_media">"Не удалось обработать медиафайл для загрузки, попробуйте еще раз."</string>
<string name="screen_room_error_failed_processing_media">"Не удалось обработать медиафайл для загрузки, попробуйте ещё раз."</string>
<string name="screen_room_error_failed_retrieving_user_details">"Не удалось получить данные о пользователе"</string>
<string name="screen_room_event_pill">"Сообщение в %1$s"</string>
<string name="screen_room_grouped_state_events_expand">"Развернуть"</string>
@@ -526,9 +527,9 @@
<string name="screen_room_live_location_banner">"Поделиться текущим местоположением…"</string>
<string name="screen_room_permalink_same_room_android">"Эта комната уже просматривается!"</string>
<string name="screen_room_pinned_banner_indicator">"%1$s из %2$s"</string>
<string name="screen_room_pinned_banner_indicator_description">"%1$s закрепленные сообщения"</string>
<string name="screen_room_pinned_banner_indicator_description">"%1$s Закреплённые сообщения"</string>
<string name="screen_room_pinned_banner_loading_description">"Загрузка сообщения…"</string>
<string name="screen_room_pinned_banner_view_all_button_title">"Посмотреть все"</string>
<string name="screen_room_pinned_banner_view_all_button_title">"Посмотреть Все"</string>
<string name="screen_room_title">"Чат"</string>
<string name="screen_share_location_title">"Поделиться местоположением"</string>
<string name="screen_share_my_location_action">"Поделиться моим местоположением"</string>
@@ -555,4 +556,6 @@
<string name="timeline_decryption_failure_historical_event_user_not_joined">"Вы не имеете доступа к этому сообщению"</string>
<string name="timeline_decryption_failure_unable_to_decrypt">"Не удалось расшифровать сообщение"</string>
<string name="timeline_decryption_failure_withheld_unverified">"Это сообщение было заблокировано либо потому, что вы не подтвердили свое устройство, либо потому, что отправителю необходимо подтвердить вашу цифровую личность."</string>
<string name="vulkan_not_supported_dialog_content_android">"Ваше устройство слишком старое, требуется устройство с Android 8 или более новой версией."</string>
<string name="vulkan_not_supported_dialog_title_android">"Отображение карт не поддерживается"</string>
</resources>
@@ -10,6 +10,7 @@
<item quantity="few">"Введено %1$d цифри"</item>
<item quantity="many">"Введено %1$d цифр"</item>
</plurals>
<string name="a11y_duration">"Тривалість: %1$s"</string>
<string name="a11y_edit_avatar">"Редагувати аватар"</string>
<string name="a11y_edit_room_address_hint">"Повна адреса буде %1$s"</string>
<string name="a11y_encryption_details">"Подробиці шифрування"</string>
@@ -18,6 +19,7 @@
<string name="a11y_info">"Інформація"</string>
<string name="a11y_join_call">"Приєднатися до виклику"</string>
<string name="a11y_jump_to_bottom">"Перейти вниз"</string>
<string name="a11y_jump_to_unread">"Перейти до непрочитаних"</string>
<string name="a11y_move_the_map_to_my_location">"Перемістити карту до мого місця перебування"</string>
<string name="a11y_notifications_mentions_only">"Тільки згадки"</string>
<string name="a11y_notifications_muted">"Звук вимкнено"</string>
@@ -34,6 +36,7 @@
<string name="a11y_playback_speed">"Швидкість програвання"</string>
<string name="a11y_poll">"Опитування"</string>
<string name="a11y_poll_end">"Опитування завершено"</string>
<string name="a11y_position">"Позиція: %1$s"</string>
<string name="a11y_qr_code">"QR-код"</string>
<string name="a11y_react_with">"Реагувати з%1$s"</string>
<string name="a11y_react_with_other_emojis">"Відреагувати іншими смайликами"</string>
@@ -50,12 +53,15 @@
<string name="a11y_room_avatar">"Аватар кімнати"</string>
<string name="a11y_send_files">"Надіслати файли"</string>
<string name="a11y_sender_location">"Локація відправника"</string>
<string name="a11y_sent_by_sender_at_date">"Надіслано %1$s о %2$s"</string>
<string name="a11y_session_verification_time_limited_action_required">"Необхідно виконати дію, обмежену в часі, у вас є одна хвилина для верифікації"</string>
<string name="a11y_settings_with_required_action">"Налаштування, потрібні дії"</string>
<string name="a11y_show_password">"Показати пароль"</string>
<string name="a11y_start_call">"Розпочати виклик"</string>
<string name="a11y_start_video_call">"Розпочати відеодзвінок"</string>
<string name="a11y_start_voice_call">"Розпочати голосовий дзвінок"</string>
<string name="a11y_thread_in_room">"Гілка в %1$s"</string>
<string name="a11y_threads_in_room">"Гілки в %1$s"</string>
<string name="a11y_tombstoned_room">"Кімната більше не використовується"</string>
<string name="a11y_user_avatar">"Аватар користувача"</string>
<string name="a11y_user_menu">"Меню користувача"</string>
@@ -95,6 +101,7 @@
<string name="action_decline_and_block">"Відхилити та заблокувати"</string>
<string name="action_delete">"Видалити"</string>
<string name="action_delete_account">"Видалити обліковий запис"</string>
<string name="action_delete_file">"Видалити файл"</string>
<string name="action_delete_poll">"Видалити опитування"</string>
<string name="action_deselect_all">"Скасувати вибір усіх"</string>
<string name="action_disable">"Вимкнути"</string>
@@ -302,6 +309,7 @@
<string name="common_please_wait">"Будь ласка, зачекайте…"</string>
<string name="common_poll_end_confirmation">"Ви впевнені, що хочете закінчити це опитування?"</string>
<string name="common_poll_summary">"Опитування: %1$s"</string>
<string name="common_poll_summary_prefix">"Опитування"</string>
<string name="common_poll_total_votes">"Всього голосів: %1$s"</string>
<string name="common_poll_undisclosed_text">"Результати будуть показані після завершення опитування"</string>
<plurals name="common_poll_votes_count">
@@ -473,6 +481,9 @@
<string name="error_unknown">"Вибачте, сталася помилка"</string>
<string name="invite_friends_rich_title">"🔐️ Приєднуйтеся до мене в %1$s"</string>
<string name="invite_friends_text">"Вітаю, поспілкуйтеся зі мною в %1$s: %2$s"</string>
<string name="live_location_sharing_foreground_service_channel_title_android">"Обмін місцезнаходженням у реальному часі"</string>
<string name="live_location_sharing_foreground_service_message_android">"Триває обмін місцезнаходженням"</string>
<string name="live_location_sharing_foreground_service_title_android">"%1$s Місцезнаходження в реальному часі"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"Повідомити про ваду за допомогою Rageshake"</string>
<string name="screen_bug_report_a11y_screenshot">"Знімок екрана"</string>
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4c0f9f0948da8553e64cbd3dd8643420dbbe0b2ee0655ef09b4d4869495f8f67
size 38036
oid sha256:4c764d8bd6dbf39c937b45eaa6a24b048ab9db1528494503494c02b6fcdf1523
size 38035
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:53f201aeace81ee0564d41542bfc8ea904d71527746ead6cbd6e9faddf29e699
size 38421
oid sha256:b047b01f0573eff0ae7798afbbed1df7e52c754df8d5a7744958eed939179443
size 38422
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:15200b3f62fbd9b0ecab51560faa4696f384daf7463a407445b2191f94c13645
size 38695
oid sha256:962d90cd71c5ed7ea19ad08bb641f197466defb85840ff7ed30d49656eee1b06
size 38679
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cb3267c007e01c5e691841911e4aa7bcfe319077a6b4f49d945e9bacc5474087
size 45041
oid sha256:ca9d69de30e7fa7008263c78f5586814d109ab8c043a19ed687f9327b955e03e
size 45033
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:30358430cd572face46c5d6e060fe0b93947670aa4c4caa091f8a50521ae36a7
size 35467
oid sha256:902411746e402c8c5e82393ba854d40a17f60b0ff5ab13ce3b8928449bf92fa4
size 35471
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:71f8a9d484a4056507b129f7fb390b2c67c99ec46a928da15caac4f95ffbd6fd
size 42006
oid sha256:41a80abb68a227320c18a797f73efe0fe9dc0ebed50fe8f6523c433fc566e6df
size 41989
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2c2e7d3a0b31388c8f373920e71ca23228fd5961a1705b3032b1407f35dc2e28
size 31051
oid sha256:93caba67729429e85bfe4a26846a1ea1c122f1b1aa7e0622dc11bdecb2741cd8
size 31032
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:91a83c3de10fea2e5edf3d96bc9ecc1fd81000ec957970696bca6c13d4d9dca3
size 31820
oid sha256:fc5e9086a7896b21d7a46fbad3ee5962be125fdd5f9609603dfe6f1779395c7a
size 31798
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e6f6d2946d194ba97c64e6749b1f9d8bf4ce52e685d06631cf22b47e156791d1
size 41526
oid sha256:b3cbbc047bbdfa813c4bb984a469370b33d8ceb44b1e57e2a40cf84c7b20f7a1
size 41492
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3b74c5fd9cb084ca2c391ea65dea23ecc027d1206510841ada0d28d5f6674995
size 32940
oid sha256:fcfcf26c1c2cb4e33e6709232f535331ce00fdde22455940e7b02b4dc727ecb2
size 32938
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:14a4be90563169ef442341a03c8add67df6ad2385b23221f7a484b296d47cab9
size 405385
oid sha256:ec642dfd92ffcd3ba4a9ed06518481935c4e38342622c4c5c74efdd68a4e7190
size 403979
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c15b4ac06b3327c95f4e40556e2c7cc0fdd0de6942dcc41a06545b03f3d7efb9
size 403700
oid sha256:45e5a33dc04d65050fd9098727f262dca526d5f55f2c64ecd235e9ae97aa8767
size 402313
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:14a4be90563169ef442341a03c8add67df6ad2385b23221f7a484b296d47cab9
size 405385
oid sha256:ec642dfd92ffcd3ba4a9ed06518481935c4e38342622c4c5c74efdd68a4e7190
size 403979
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:eb69e3738018bd6138e1f04478b5f7b5758816b9516f871dc8643ea037ba1c65
size 71377
oid sha256:4d3ecea8217faca0d45acfa0a04023efff0124ad0563857ec4ce81e826033f74
size 70311
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6bea145d686c295829e91a0a19e4172155ddd03ae62730241ee55e3c759d5f88
size 75505
oid sha256:fd092ba5b859abc14b18702a613aa4b7bbfad43c2e91e294ad876edbcc5f9960
size 74489
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7587dfe56eb8de6c4ed8117ad9bf1066cd53b5e89430be66a9b9f3ae357343fe
size 412192
oid sha256:5ae9aeabad16587e233b815e60c76856f30f7296562a2b4abb1149a8490435ee
size 410888

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