Merge branch 'develop' into feature/bma/scroll-to-unread-messages

This commit is contained in:
Benoit Marty
2026-06-16 14:28:44 +02:00
committed by GitHub
382 changed files with 2538 additions and 1947 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 KiB

@@ -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>
@@ -15,6 +15,7 @@ sealed class ClientException(message: String, val details: String?, cause: Throw
details = details,
cause = cause
)
class ContentScanner(message: String, val reason: ContentScannerErrorReason) : ClientException(message, null, null)
class Other(message: String, cause: Throwable? = null) : ClientException(message, null, cause)
}
@@ -0,0 +1,28 @@
/*
* 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.exception
enum class ContentScannerErrorReason {
MCS_MALFORMED_JSON,
MCS_MEDIA_FAILED_TO_DECRYPT,
M_MISSING_TOKEN,
M_UNKNOWN_TOKEN,
M_NOT_FOUND,
MCS_MEDIA_NOT_CLEAN,
MCS_MIME_TYPE_FORBIDDEN,
MCS_BAD_DECRYPTION,
M_UNKNOWN,
MCS_MEDIA_REQUEST_FAILED;
companion object;
fun isDangerous(): Boolean = when (this) {
MCS_MEDIA_NOT_CLEAN, MCS_MIME_TYPE_FORBIDDEN -> true
else -> false
}
}
@@ -9,6 +9,7 @@
package io.element.android.libraries.matrix.impl.exception
import io.element.android.libraries.matrix.api.exception.ClientException
import io.element.android.libraries.matrix.api.exception.ContentScannerErrorReason
import org.matrix.rustcomponents.sdk.ClientException as RustClientException
fun Throwable.mapClientException(): ClientException {
@@ -23,6 +24,10 @@ fun Throwable.mapClientException(): ClientException {
details = details,
cause = this,
)
is RustClientException.ContentScanner -> ClientException.ContentScanner(
message = message,
reason = ContentScannerErrorReason.fromRust(reason),
)
}
}
else -> ClientException.Other(message ?: "Unknown error", this)
@@ -0,0 +1,34 @@
/*
* 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.exception
import io.element.android.libraries.matrix.api.exception.ContentScannerErrorReason
import uniffi.matrix_sdk_contentscanner.ErrorReason
import uniffi.matrix_sdk_contentscanner.ErrorReason.MCS_BAD_DECRYPTION
import uniffi.matrix_sdk_contentscanner.ErrorReason.MCS_MALFORMED_JSON
import uniffi.matrix_sdk_contentscanner.ErrorReason.MCS_MEDIA_FAILED_TO_DECRYPT
import uniffi.matrix_sdk_contentscanner.ErrorReason.MCS_MEDIA_NOT_CLEAN
import uniffi.matrix_sdk_contentscanner.ErrorReason.MCS_MEDIA_REQUEST_FAILED
import uniffi.matrix_sdk_contentscanner.ErrorReason.MCS_MIME_TYPE_FORBIDDEN
import uniffi.matrix_sdk_contentscanner.ErrorReason.M_MISSING_TOKEN
import uniffi.matrix_sdk_contentscanner.ErrorReason.M_NOT_FOUND
import uniffi.matrix_sdk_contentscanner.ErrorReason.M_UNKNOWN
import uniffi.matrix_sdk_contentscanner.ErrorReason.M_UNKNOWN_TOKEN
fun ContentScannerErrorReason.Companion.fromRust(reason: ErrorReason) = when (reason) {
M_UNKNOWN -> ContentScannerErrorReason.M_UNKNOWN
M_MISSING_TOKEN -> ContentScannerErrorReason.M_MISSING_TOKEN
M_UNKNOWN_TOKEN -> ContentScannerErrorReason.M_UNKNOWN_TOKEN
MCS_MALFORMED_JSON -> ContentScannerErrorReason.MCS_MALFORMED_JSON
MCS_MEDIA_FAILED_TO_DECRYPT -> ContentScannerErrorReason.MCS_MEDIA_FAILED_TO_DECRYPT
M_NOT_FOUND -> ContentScannerErrorReason.M_NOT_FOUND
MCS_MEDIA_NOT_CLEAN -> ContentScannerErrorReason.MCS_MEDIA_NOT_CLEAN
MCS_MIME_TYPE_FORBIDDEN -> ContentScannerErrorReason.MCS_MIME_TYPE_FORBIDDEN
MCS_BAD_DECRYPTION -> ContentScannerErrorReason.MCS_BAD_DECRYPTION
MCS_MEDIA_REQUEST_FAILED -> ContentScannerErrorReason.MCS_MEDIA_REQUEST_FAILED
}
@@ -51,6 +51,10 @@ class RustLinkMobileHandler(
)
// We emit Done in case the progress listener was deallocated before generate() sent the Done
_linkMobileStep.emit(LinkMobileStep.Done)
} catch (e: HumanQrGrantLoginException.Expired) {
// Note: the SDK does not return this error, but is returning `.NotFound` instead when the QR code expires. We catch both just in case.
Timber.tag(tag.value).w(e, "QR code has expired")
_linkMobileStep.emit(LinkMobileStep.QrRotating)
} catch (e: HumanQrGrantLoginException.NotFound) {
Timber.tag(tag.value).w(e, "Error during QR login grant")
// Catch timeout here?
@@ -147,6 +147,32 @@ class RustLinkMobileHandlerTest {
}
}
@Test
fun `when start throws HumanQrGrantLoginException_Expired, the handler emits QrRotating step`() = runTest {
val completable = CompletableDeferred<Unit>()
val handler = FakeFfiGrantLoginWithQrCodeHandler(
generateResult = {
completable.await()
throw HumanQrGrantLoginException.Expired("Expired")
}
)
val sut = createRustLinkMobileHandler(
handler,
)
sut.linkMobileStep.test {
val initialItem = awaitItem()
assertThat(initialItem).isEqualTo(LinkMobileStep.Uninitialized)
backgroundScope.launch {
sut.start()
}
runCurrent()
// generate returns, error is emitted
completable.complete(Unit)
val qrRotatingState = awaitItem()
assertThat(qrRotatingState).isEqualTo(LinkMobileStep.QrRotating)
}
}
private fun TestScope.createRustLinkMobileHandler(
handler: FakeFfiGrantLoginWithQrCodeHandler = FakeFfiGrantLoginWithQrCodeHandler(),
) = RustLinkMobileHandler(
@@ -78,7 +78,7 @@ fun SpaceRoomItemView(
indication = ripple(),
interactionSource = remember { MutableInteractionSource() }
)
.onKeyboardContextMenuAction { onLongClick }
.onKeyboardContextMenuAction(onLongClick)
Column(
modifier = modifier
.then(clickModifier)
@@ -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>
@@ -258,7 +258,7 @@ class ExoPlayerForPreview(
override fun setPriorityTaskManager(priorityTaskManager: PriorityTaskManager?) {}
override fun isSleepingForOffload(): Boolean = throw NotImplementedError()
override fun isTunnelingEnabled(): Boolean = throw NotImplementedError()
override fun isReleased(): Boolean = throw NotImplementedError()
override fun isReleased(): Boolean = false
override fun setImageOutput(imageOutput: ImageOutput?) {}
override fun setAudioCodecParameters(codecParameters: CodecParameters) {}
override fun addAudioCodecParametersChangeListener(listener: CodecParametersChangeListener, keys: List<String>) {}
@@ -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>
+1
View File
@@ -24,6 +24,7 @@ android {
setupDependencyInjection()
dependencies {
implementation(libs.coroutines.core)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.di)
@@ -12,6 +12,7 @@ import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.matrix.api.auth.OAuthRedirectUrlProvider
import io.element.android.libraries.oauth.api.OAuthAction
import timber.log.Timber
fun interface OAuthUrlParser {
fun parse(url: String): OAuthAction?
@@ -38,7 +39,8 @@ class DefaultOAuthUrlParser(
if (url.contains("error=access_denied")) return OAuthAction.GoBack()
if (url.contains("code=")) return OAuthAction.Success(url)
// Other case not supported, let's crash the app for now
error("Not supported: $url")
// Other cases are not supported, log an error and return null
Timber.w("Unsupported OAuth url")
return null
}
}
@@ -13,7 +13,6 @@ import androidx.core.net.toUri
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.auth.FakeOAuthRedirectUrlProvider
import io.element.android.libraries.oauth.api.OAuthAction
import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@@ -54,9 +53,8 @@ class DefaultOAuthIntentResolverTest {
action = Intent.ACTION_VIEW
data = "io.element.android:/invalid".toUri()
}
assertThrows(IllegalStateException::class.java) {
sut.resolve(intent)
}
val result = sut.resolve(intent)
assertThat(result).isNull()
}
private fun createDefaultOAuthIntentResolver(): DefaultOAuthIntentResolver {
@@ -11,7 +11,6 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.test.auth.FAKE_REDIRECT_URL
import io.element.android.libraries.matrix.test.auth.FakeOAuthRedirectUrlProvider
import io.element.android.libraries.oauth.api.OAuthAction
import org.junit.Assert
import org.junit.Test
class DefaultOAuthUrlParserTest {
@@ -45,9 +44,7 @@ class DefaultOAuthUrlParserTest {
fun `test unknown url`() {
val sut = createDefaultOAuthUrlParser()
val anUnknownUrl = "$FAKE_REDIRECT_URL?state=IFF1UETGye2ZA8pO&goat=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB"
Assert.assertThrows(IllegalStateException::class.java) {
assertThat(sut.parse(anUnknownUrl))
}
assertThat(sut.parse(anUnknownUrl)).isNull()
}
private fun createDefaultOAuthUrlParser(): DefaultOAuthUrlParser {
@@ -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>
@@ -19,6 +19,10 @@ plugins {
android {
namespace = "io.element.android.libraries.pushproviders.firebase"
buildFeatures {
resValues = true
}
buildTypes {
getByName("release") {
consumerProguardFiles("consumer-proguard-rules.pro")
@@ -18,6 +18,7 @@ android {
setupDependencyInjection()
dependencies {
implementation(libs.coroutines.core)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.preferences.api)
implementation(projects.libraries.core)
@@ -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>