Merge branch 'develop' into renovate/zizmorcore-zizmor-action-0.x

This commit is contained in:
ganfra
2026-06-30 18:38:30 +02:00
committed by GitHub
436 changed files with 2788 additions and 2028 deletions
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="call_foreground_service_channel_title_android">"通话进行中"</string>
<string name="call_foreground_service_channel_title_android">"正在进行的通话"</string>
<string name="call_foreground_service_message_android">"点击以返回通话"</string>
<string name="call_foreground_service_title_android">"☎️ 通话中"</string>
<string name="call_invalid_audio_device_bluetooth_devices_disabled">"Element Call 不支持在此 Android 版本中使用蓝牙音频设备。请选择其它音频设备。"</string>
@@ -31,6 +31,6 @@
<string name="screen_create_room_space_selection_no_space_option">"Не додавати до простору"</string>
<string name="screen_create_room_space_selection_no_space_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_label">"Тематика обговорень (необовязково)"</string>
<string name="screen_create_room_topic_placeholder">"Додати опис…"</string>
</resources>
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_deactivate_account_confirmation_dialog_content">"Будь ласка, підтвердіть, що ви хочете деактивувати свій обліковий запис. Ця дія не може бути скасована."</string>
<string name="screen_deactivate_account_confirmation_dialog_content">"Підтвердьте, що хочете видалити свій обліковий запис. Цю дію неможливо скасувати."</string>
<string name="screen_deactivate_account_delete_all_messages">"Видалити всі мої повідомлення"</string>
<string name="screen_deactivate_account_delete_all_messages_notice">"Попередження: майбутні користувачі можуть бачити неповні розмови."</string>
<string name="screen_deactivate_account_description">"Деактивація вашого облікового запису%1$s , це буде:"</string>
<string name="screen_deactivate_account_description">"Видалення вашого облікового запису %1$s призведе до:"</string>
<string name="screen_deactivate_account_description_bold_part">"незворотні"</string>
<string name="screen_deactivate_account_list_item_1">"%1$sваш обліковий запис (ви не можете знову увійти, а ваш ідентифікатор не може бути використаний повторно)."</string>
<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_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_title">"Відключити обліковий запис"</string>
<string name="screen_deactivate_account_title">"Видалити обліковий запис"</string>
</resources>
@@ -2,7 +2,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_identity_confirmation_cannot_confirm">"Tidak dapat mengonfirmasi?"</string>
<string name="screen_identity_confirmation_create_new_recovery_key">"Buat kunci pemulihan baru"</string>
<string name="screen_identity_confirmation_subtitle">"Verifikasi perangkat ini untuk menyiapkan perpesanan aman."</string>
<string name="screen_identity_confirmation_subtitle">"Pilih cara verifikasi untuk mengatur pengiriman pesan yang aman."</string>
<string name="screen_identity_confirmation_title">"Konfirmasi bahwa ini Anda"</string>
<string name="screen_identity_confirmation_use_another_device">"Gunakan perangkat lain"</string>
<string name="screen_identity_confirmation_use_recovery_key">"Gunakan kunci pemulihan"</string>
@@ -2,12 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_identity_confirmation_cannot_confirm">"Не можете підтвердити?"</string>
<string name="screen_identity_confirmation_create_new_recovery_key">"Створити новий ключ відновлення"</string>
<string name="screen_identity_confirmation_subtitle">"Верифікуйте цей пристрій, щоб налаштувати безпечний обмін повідомленнями."</string>
<string name="screen_identity_confirmation_title">"Підтвердьте, що це ви"</string>
<string name="screen_identity_confirmation_subtitle">"Виберіть спосіб звіряння, щоб налаштувати безпечний обмін повідомленнями."</string>
<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_title">"Пристрій перевірено"</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>
<string name="screen_notification_optin_subtitle">"Ви можете змінити свої налаштування пізніше."</string>
+1
View File
@@ -57,6 +57,7 @@ dependencies {
implementation(libs.androidx.datastore.preferences)
implementation(libs.haze)
implementation(libs.haze.materials)
implementation(projects.features.preferences.impl)
implementation(projects.features.reportroom.api)
implementation(projects.features.rolesandpermissions.api)
implementation(projects.libraries.previewutils)
@@ -41,6 +41,7 @@ import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteE
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteState
import io.element.android.features.leaveroom.api.LeaveRoomEvent
import io.element.android.features.leaveroom.api.LeaveRoomState
import io.element.android.features.preferences.impl.tasks.MarkRoomAsRead
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.featureflag.api.FeatureFlagService
@@ -51,11 +52,8 @@ import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.api.roomlist.RoomList
import io.element.android.libraries.matrix.api.roomlist.RoomListFilter
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.push.api.battery.BatteryOptimizationState
import io.element.android.libraries.push.api.notifications.NotificationCleaner
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.api.watchers.AnalyticsColdStartWatcher
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
@@ -65,7 +63,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -79,12 +76,11 @@ class RoomListPresenter(
private val roomListDataSource: RoomListDataSource,
private val filtersPresenter: Presenter<RoomListFiltersState>,
private val searchPresenter: Presenter<RoomListSearchState>,
private val sessionPreferencesStore: SessionPreferencesStore,
private val analyticsService: AnalyticsService,
private val acceptDeclineInvitePresenter: Presenter<AcceptDeclineInviteState>,
private val fullScreenIntentPermissionsPresenter: Presenter<FullScreenIntentPermissionsState>,
private val batteryOptimizationPresenter: Presenter<BatteryOptimizationState>,
private val notificationCleaner: NotificationCleaner,
private val markRoomAsRead: MarkRoomAsRead,
private val seenInvitesStore: SeenInvitesStore,
private val announcementService: AnnouncementService,
private val coldStartWatcher: AnalyticsColdStartWatcher,
@@ -308,19 +304,10 @@ class RoomListPresenter(
}
private fun CoroutineScope.markAsRead(roomId: RoomId) = launch {
notificationCleaner.clearMessagesForRoom(client.sessionId, roomId)
client.getRoom(roomId)?.use { room ->
room.setUnreadFlag(isUnread = false)
val receiptType = if (sessionPreferencesStore.isSendPublicReadReceiptsEnabled().first()) {
ReceiptType.READ
} else {
ReceiptType.READ_PRIVATE
markRoomAsRead(roomId)
.onSuccess {
analyticsService.captureInteraction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle)
}
room.markAsRead(receiptType)
.onSuccess {
analyticsService.captureInteraction(name = Interaction.Name.MobileRoomListRoomContextMenuUnreadToggle)
}
}
}
private fun CoroutineScope.markAsUnread(roomId: RoomId) = launch {
@@ -4,8 +4,8 @@
<string name="banner_battery_optimization_submit_android">"Nonaktifkan optimasi"</string>
<string name="banner_battery_optimization_title_android">"Notifikasi tidak masuk?"</string>
<string name="banner_set_up_recovery_content">"Buat kunci pemulihan baru yang dapat digunakan untuk memulihkan riwayat pesan terenkripsi Anda jika Anda kehilangan akses ke perangkat Anda."</string>
<string name="banner_set_up_recovery_submit">"Siapkan pemulihan"</string>
<string name="banner_set_up_recovery_title">"Siapkan pemulihan"</string>
<string name="banner_set_up_recovery_submit">"Siapkan Kunci pemulihan"</string>
<string name="banner_set_up_recovery_title">"Cadangkan obrolan Anda"</string>
<string name="confirm_recovery_key_banner_message">"Konfirmasikan kunci pemulihan Anda untuk mempertahankan akses ke penyimpanan kunci dan riwayat pesan Anda."</string>
<string name="confirm_recovery_key_banner_primary_button_title">"Masukkan kunci pemulihan Anda"</string>
<string name="confirm_recovery_key_banner_secondary_button_title">"Lupa kunci pemulihan Anda?"</string>
@@ -6,7 +6,7 @@
<string name="banner_new_sound_message">"O seu ping de notificação foi atualizado—mais suave, mais rápido, e menos disruptivo."</string>
<string name="banner_new_sound_title">"Recarregamos seus sons"</string>
<string name="banner_set_up_recovery_content">"Recupere sua identidade criptográfica e o histórico de mensagens com uma chave de recuperação caso você perda todos os dispositivos existentes."</string>
<string name="banner_set_up_recovery_submit">"Configurar a recuperação"</string>
<string name="banner_set_up_recovery_submit">"Receber chave de recuperação"</string>
<string name="banner_set_up_recovery_title">"Configure a recuperação para proteger sua conta"</string>
<string name="confirm_recovery_key_banner_message">"Confirme sua chave de recuperação para manter o acesso ao seu armazenamento de chaves e histórico de mensagens."</string>
<string name="confirm_recovery_key_banner_primary_button_title">"Digite sua chave de recuperação"</string>
@@ -5,15 +5,15 @@
<string name="banner_battery_optimization_title_android">"Не надходять сповіщення?"</string>
<string name="banner_new_sound_message">"Ваш сигнал сповіщень оновлено — він став чіткішим, швидшим і не таким надокучливим."</string>
<string name="banner_new_sound_title">"Ми оновили ваші звуки"</string>
<string name="banner_set_up_recovery_content">"Відновіть свою криптографічну ідентичність та історію повідомлень за допомогою ключа відновлення, якщо ви втратили всі наявні пристрої."</string>
<string name="banner_set_up_recovery_submit">"Налаштувати відновлення"</string>
<string name="banner_set_up_recovery_title">"Налаштуйте відновлення для захисту свого облікового запису"</string>
<string name="banner_set_up_recovery_content">"Ваші бесіди автоматично резервуються з використанням наскрізного шифрування. Щоб відновити цю резервну копію та зберегти свою цифрову ідентичність у разі втрати доступу до всіх своїх пристроїв, вам знадобиться ключ відновлення."</string>
<string name="banner_set_up_recovery_submit">"Отримати ключ відновлення"</string>
<string name="banner_set_up_recovery_title">"Створити резервну копію своїх бесід"</string>
<string name="confirm_recovery_key_banner_message">"Підтвердіть свій ключ відновлення, щоб мати доступ до сховища ключів та історії повідомлень."</string>
<string name="confirm_recovery_key_banner_primary_button_title">"Введіть ключ відновлення"</string>
<string name="confirm_recovery_key_banner_secondary_button_title">"Забули ключ відновлення?"</string>
<string name="confirm_recovery_key_banner_title">"Ваше сховище ключів не синхронізовано"</string>
<string name="full_screen_intent_banner_message">"Щоб ніколи не пропустити важливий виклик, змініть налаштування, щоб увімкнути повноекранні сповіщення, коли телефон заблоковано."</string>
<string name="full_screen_intent_banner_title">"Покращуйте досвід дзвінків"</string>
<string name="full_screen_intent_banner_title">"Покращуйте якість викликів"</string>
<string name="screen_home_tab_chats">"Бесіди"</string>
<string name="screen_home_tab_spaces">"Простори"</string>
<string name="screen_invites_decline_chat_message">"Ви впевнені, що хочете відхилити запрошення приєднатися до %1$s?"</string>
@@ -51,6 +51,6 @@
<string name="screen_roomlist_mark_as_unread">"Позначити непрочитаним"</string>
<string name="screen_roomlist_tombstoned_room_description">"Цю кімнату оновлено"</string>
<string name="screen_roomlist_your_spaces">"Ваші простори"</string>
<string name="session_verification_banner_message">"Схоже, ви використовуєте новий пристрій. Щоб отримати доступ до зашифрованих повідомлень, підтвердьте особу за допомогою іншого пристрою."</string>
<string name="session_verification_banner_message">"Здається, ви використовуєте новий пристрій. Звірте його за допомогою іншого пристрою, щоб отримати доступ до своїх зашифрованих повідомлень."</string>
<string name="session_verification_banner_title">"Підтвердьте, що це ви"</string>
</resources>
@@ -0,0 +1,22 @@
/*
* 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.features.home.impl.roomlist
import io.element.android.features.preferences.impl.tasks.MarkRoomAsRead
import io.element.android.libraries.matrix.api.core.RoomId
class FakeMarkRoomAsRead(
private val invokeLambda: suspend (RoomId) -> Result<Unit> = { Result.success(Unit) },
) : MarkRoomAsRead {
val invokedRoomIds = mutableListOf<RoomId>()
override suspend fun invoke(roomId: RoomId): Result<Unit> {
invokedRoomIds.add(roomId)
return invokeLambda(roomId)
}
}
@@ -30,6 +30,7 @@ import io.element.android.features.invite.api.acceptdecline.anAcceptDeclineInvit
import io.element.android.features.invite.test.InMemorySeenInvitesStore
import io.element.android.features.leaveroom.api.LeaveRoomEvent
import io.element.android.features.leaveroom.api.LeaveRoomState
import io.element.android.features.preferences.impl.tasks.MarkRoomAsRead
import io.element.android.features.rageshake.test.logs.FakeAnnouncementService
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.dateformatter.api.DateFormatter
@@ -427,11 +428,17 @@ class RoomListPresenterTest {
val notificationCleaner = FakeNotificationCleaner(
clearMessagesForRoomLambda = clearMessagesForRoomLambda,
)
val markRoomAsRead = createTestMarkRoomAsRead(
client = matrixClient,
notificationCleaner = notificationCleaner,
sessionPreferencesStore = sessionPreferencesStore,
)
val presenter = createRoomListPresenter(
client = matrixClient,
sessionPreferencesStore = sessionPreferencesStore,
analyticsService = analyticsService,
notificationCleaner = notificationCleaner,
markRoomAsRead = markRoomAsRead,
)
presenter.test {
val initialState = awaitItem()
@@ -623,6 +630,24 @@ class RoomListPresenterTest {
}
}
private fun createTestMarkRoomAsRead(
client: MatrixClient,
notificationCleaner: NotificationCleaner,
sessionPreferencesStore: SessionPreferencesStore,
): MarkRoomAsRead = FakeMarkRoomAsRead { roomId ->
notificationCleaner.clearMessagesForRoom(client.sessionId, roomId)
val room = client.getRoom(roomId) ?: return@FakeMarkRoomAsRead Result.failure(IllegalStateException("Room not found"))
room.use {
it.setUnreadFlag(isUnread = false)
val receiptType = if (sessionPreferencesStore.isSendPublicReadReceiptsEnabled().first()) {
ReceiptType.READ
} else {
ReceiptType.READ_PRIVATE
}
it.markAsRead(receiptType)
}
}
private fun TestScope.createRoomListPresenter(
client: MatrixClient = FakeMatrixClient(),
leaveRoomState: LeaveRoomState = aLeaveRoomState(),
@@ -638,6 +663,7 @@ class RoomListPresenterTest {
seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(),
announcementService: AnnouncementService = FakeAnnouncementService(),
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
markRoomAsRead: MarkRoomAsRead? = null,
) = RoomListPresenter(
client = client,
leaveRoomPresenter = { leaveRoomState },
@@ -654,14 +680,17 @@ class RoomListPresenterTest {
analyticsService = FakeAnalyticsService(),
),
searchPresenter = searchPresenter,
sessionPreferencesStore = sessionPreferencesStore,
filtersPresenter = filtersPresenter,
spaceFiltersPresenter = spaceFiltersPresenter,
analyticsService = analyticsService,
acceptDeclineInvitePresenter = acceptDeclineInvitePresenter,
fullScreenIntentPermissionsPresenter = { aFullScreenIntentPermissionsState() },
batteryOptimizationPresenter = { aBatteryOptimizationState() },
notificationCleaner = notificationCleaner,
markRoomAsRead = markRoomAsRead ?: createTestMarkRoomAsRead(
client = client,
notificationCleaner = notificationCleaner,
sessionPreferencesStore = sessionPreferencesStore,
),
seenInvitesStore = seenInvitesStore,
announcementService = announcementService,
coldStartWatcher = FakeAnalyticsColdStartWatcher(),
@@ -2,8 +2,8 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<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_multiple_users">"Наразі у вас немає бесід з цими контактами. Підтвердьте запрошення їх до цієї кімнати, перш ніж продовжувати."</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>
@@ -15,6 +15,10 @@
<string name="screen_join_room_fail_reason">"Ruangan ini hanya untuk undangan atau mungkin ada pembatasan akses pada tingkat space."</string>
<string name="screen_join_room_forget_action">"Lupa"</string>
<string name="screen_join_room_invite_required_message">"Anda memerlukan undangan untuk bergabung dalam ruangan ini"</string>
<string name="screen_join_room_invited_by">
"Diundang oleh "
<userName/>
</string>
<string name="screen_join_room_join_action">"Gabung"</string>
<string name="screen_join_room_join_restricted_message">"Anda mungkin perlu diundang atau menjadi anggota space untuk bergabung."</string>
<string name="screen_join_room_knock_action">"Ketuk untuk bergabung"</string>
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="leave_conversation_alert_subtitle">"Czy na pewno chcesz opuścić tę konwersację? Konwersacja nie jest publiczna i nie będziesz mógł dołączyć ponownie bez zaproszenia."</string>
<string name="leave_room_alert_empty_subtitle">"Jesteś pewien, że chcesz opuścić ten pokój? Jesteś tu jedyną osobą. Jeśli wyjdziesz, nikt nie będzie mógł dołączyć, w tym Ty."</string>
<string name="leave_room_alert_empty_subtitle">"Czy na pewno chcesz opuścić ten pokój? Jesteś tu jedyną osobą. Jeśli wyjdziesz, nikt nie będzie mógł dołączyć, w tym Ty."</string>
<string name="leave_room_alert_private_subtitle">"Czy na pewno chcesz opuścić ten pokój? Ten pokój nie jest publiczny i nie będziesz mógł do niego wrócić bez zaproszenia."</string>
<string name="leave_room_alert_select_new_owner_action">"Wybierz właścicieli"</string>
<string name="leave_room_alert_select_new_owner_subtitle">"Jesteś jedynym właścicielem tego pokoju. Musisz przenieść własność na kogoś innego, zanim go opuścisz."</string>
<string name="leave_room_alert_select_new_owner_title">"Przenieś własność"</string>
<string name="leave_room_alert_subtitle">"Jesteś pewien, że chcesz wyjść z tego pokoju?"</string>
<string name="leave_room_alert_subtitle">"Czy na pewno chcesz wyjść z tego pokoju?"</string>
</resources>
@@ -9,6 +9,7 @@
package io.element.android.features.linknewdevice.impl.screens.scan
import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
import androidx.activity.ComponentActivity
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalInspectionMode
@@ -25,9 +26,11 @@ import io.element.android.tests.testutils.ensureCalledOnce
import io.element.android.tests.testutils.pressBackKey
import io.element.android.tests.testutils.robolectric.RobolectricTest
import org.junit.Test
import org.robolectric.annotation.Config
class ScanQrCodeViewTest : RobolectricTest() {
@Test
@Config(sdk = [UPSIDE_DOWN_CAKE])
fun `on back pressed - calls the expected callback`() = runAndroidComposeUiTest {
val eventRecorder = EventsRecorder<ScanQrCodeEvent>(expectEvents = false)
ensureCalledOnce { callback ->
@@ -42,6 +45,7 @@ class ScanQrCodeViewTest : RobolectricTest() {
}
@Test
@Config(sdk = [UPSIDE_DOWN_CAKE])
fun `try again button clicked - emits the expected event`() = runAndroidComposeUiTest {
val eventRecorder = EventsRecorder<ScanQrCodeEvent>()
setView(
@@ -23,7 +23,7 @@ Pilih sesuatu yang mudah untuk diingat. Jika Anda lupa PIN ini, Anda akan dikelu
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Silakan masukkan PIN yang sama dua kali"</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"PIN tidak cocok"</string>
<string name="screen_app_lock_signout_alert_message">"Anda harus masuk ulang dan membuat PIN baru untuk melanjutkan"</string>
<string name="screen_app_lock_signout_alert_title">"Anda sedang dikeluarkan"</string>
<string name="screen_app_lock_signout_alert_title">"Perangkat ini sedang dilepas"</string>
<plurals name="screen_app_lock_subtitle">
<item quantity="other">"Anda memiliki %1$d percobaan lagi untuk membuka kunci"</item>
</plurals>
@@ -23,7 +23,7 @@ Escolha algo memorável. Se você esquecer este PIN, você será desconectado do
<string name="screen_app_lock_setup_pin_mismatch_dialog_content">"Por favor, digite o mesmo PIN duas vezes"</string>
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"Os PINs não correspondem"</string>
<string name="screen_app_lock_signout_alert_message">"Você terá que entrar novamente e criar um PIN novo para continuar"</string>
<string name="screen_app_lock_signout_alert_title">"Você está sendo desconectado"</string>
<string name="screen_app_lock_signout_alert_title">"Este dispositivo está sendo removido"</string>
<plurals name="screen_app_lock_subtitle">
<item quantity="one">"Você tem %1$d tentativa de desbloqueio"</item>
<item quantity="other">"Você tem %1$d tentativas de desbloqueio"</item>
@@ -34,5 +34,5 @@ Escolha algo memorável. Se você esquecer este PIN, você será desconectado do
</plurals>
<string name="screen_app_lock_use_biometric_android">"Usar biometria"</string>
<string name="screen_app_lock_use_pin_android">"Usar PIN"</string>
<string name="screen_signout_in_progress_dialog_content">"Saindo…"</string>
<string name="screen_signout_in_progress_dialog_content">"Removendo dispositivo…"</string>
</resources>
@@ -15,15 +15,15 @@
<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>
<string name="screen_app_lock_setup_pin_mismatch_dialog_title">"PIN-коди не збігаються"</string>
<string name="screen_app_lock_signout_alert_message">"Щоб продовжити, вам потрібно повторно ввійти та створити новий PIN-код"</string>
<string name="screen_app_lock_signout_alert_title">"Ви виходите з системи"</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>
@@ -45,6 +45,7 @@
<string name="screen_missing_key_backup_step_5">"Επιστρέψτε στο %1$s"</string>
<string name="screen_missing_key_backup_title">"Ενεργοποιήστε την αποθήκευση κλειδιών σας πριν προχωρήσετε στο %1$s"</string>
<string name="screen_onboarding_app_version">"Έκδοση %1$s"</string>
<string name="screen_onboarding_checking_account">"Έλεγχος λογαριασμού"</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>
@@ -28,7 +28,7 @@
<string name="screen_change_server_subtitle">"Qual é o endereço do seu servidor?"</string>
<string name="screen_change_server_title">"Selecione seu servidor"</string>
<string name="screen_create_account_title">"Criar conta"</string>
<string name="screen_login_error_deactivated_account">"Essa conta foi desativada."</string>
<string name="screen_login_error_deactivated_account">"Essa conta foi apagada."</string>
<string name="screen_login_error_invalid_credentials">"Nome de usuário e/ou senha incorretos"</string>
<string name="screen_login_error_invalid_user_id">"Esse não é um identificador de usuário válido. Formato esperado: \'@usuário:servidor.org\'"</string>
<string name="screen_login_error_refresh_tokens">"Este servidor está configurado para usar tokens recarregados. Não há suporte a eles ao entrar por uma senha."</string>
@@ -51,9 +51,9 @@
<string name="screen_onboarding_sign_in_with_qr_code">"Увійти за допомогою QR-коду"</string>
<string name="screen_onboarding_sign_up">"Створити обліковий запис"</string>
<string name="screen_onboarding_welcome_back">"З поверненням!"</string>
<string name="screen_onboarding_welcome_message">"Ласкаво просимо до найшвидшого %1$s. Заряджений для швидкості та простоти."</string>
<string name="screen_onboarding_welcome_subtitle">"Ласкаво просимо до %1$s. Заряджений, для швидкості та простоти."</string>
<string name="screen_onboarding_welcome_title">"Будьте у своєму element"</string>
<string name="screen_onboarding_welcome_message">"Вітаємо в %1$s. Сфокусованому на швидкості та простоті."</string>
<string name="screen_onboarding_welcome_subtitle">"Вітаємо у %1$s. Сфокусованому на швидкості та простоті."</string>
<string name="screen_onboarding_welcome_title">"Спілкуйтесь елементарно"</string>
<string name="screen_qr_code_login_connecting_subtitle">"Встановлення безпечного з\'єднання"</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>
@@ -5,14 +5,14 @@
<string name="screen_signout_confirmation_dialog_title">"Hapus device dari akun"</string>
<string name="screen_signout_in_progress_dialog_content">"Mengeluarkan device dari akun…"</string>
<string name="screen_signout_key_backup_disabled_subtitle">"Anda akan keluar dari sesi terakhir Anda. Jika Anda keluar sekarang, Anda akan kehilangan akses ke pesan terenkripsi Anda."</string>
<string name="screen_signout_key_backup_disabled_title">"Anda telah menonaktifkan pencadangan"</string>
<string name="screen_signout_key_backup_offline_subtitle">"Kunci Anda masih dicadangkan saat Anda luring. Sambungkan kembali sehingga kunci Anda dapat dicadangkan sebelum keluar."</string>
<string name="screen_signout_key_backup_disabled_title">"Anda akan segera kehilangan akses ke obrolan terenkripsi Anda."</string>
<string name="screen_signout_key_backup_offline_subtitle">"Kunci Anda masih dalam proses pencadangan saat Anda offline. Sambungkan kembali agar kunci Anda dapat dicadangkan sebelum Anda menghapus perangkat ini."</string>
<string name="screen_signout_key_backup_offline_title">"Kunci Anda masih dicadangkan"</string>
<string name="screen_signout_key_backup_ongoing_subtitle">"Mohon tunggu hingga proses ini selesai sebelum keluar."</string>
<string name="screen_signout_key_backup_ongoing_subtitle">"Harap tunggu hingga proses ini selesai sebelum melepas perangkat ini."</string>
<string name="screen_signout_key_backup_ongoing_title">"Kunci Anda masih dicadangkan"</string>
<string name="screen_signout_preference_item">"Hapus device dari akun"</string>
<string name="screen_signout_recovery_disabled_subtitle">"Anda akan keluar dari sesi Anda yang terakhir. Jika Anda keluar sekarang, Anda akan kehilangan akses ke pesan terenkripsi Anda."</string>
<string name="screen_signout_recovery_disabled_title">"Pemulihan belum disiapkan"</string>
<string name="screen_signout_recovery_disabled_title">"Anda akan segera kehilangan akses ke obrolan terenkripsi Anda."</string>
<string name="screen_signout_save_recovery_key_subtitle">"Anda akan keluar dari sesi terakhir Anda. Jika Anda keluar sekarang, Anda mungkin kehilangan akses ke pesan terenkripsi Anda."</string>
<string name="screen_signout_save_recovery_key_title">"Apakah Anda sudah menyimpan kunci pemulihan Anda?"</string>
<string name="screen_signout_save_recovery_key_title">"Pastikan Anda memiliki akses ke kunci pemulihan sebelum mencabut perangkat ini."</string>
</resources>
@@ -1,18 +1,18 @@
<?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">"Você tem certeza que deseja sair?"</string>
<string name="screen_signout_confirmation_dialog_submit">"Sair"</string>
<string name="screen_signout_confirmation_dialog_title">"Sair"</string>
<string name="screen_signout_in_progress_dialog_content">"Saindo…"</string>
<string name="screen_signout_confirmation_dialog_content">"Você tem certeza que deseja remover este dispositivo?"</string>
<string name="screen_signout_confirmation_dialog_submit">"Remover este dispositivo"</string>
<string name="screen_signout_confirmation_dialog_title">"Remover este dispositivo"</string>
<string name="screen_signout_in_progress_dialog_content">"Removendo dispositivo…"</string>
<string name="screen_signout_key_backup_disabled_subtitle">"Você está prestes a sair da sua última sessão. Se você sair agora, perderá o acesso às suas mensagens criptografadas."</string>
<string name="screen_signout_key_backup_disabled_title">"Você desativou o backup"</string>
<string name="screen_signout_key_backup_offline_subtitle">"O backup das suas chaves ainda estava sendo feito quando você ficou off-line. Reconecte-se para que você possa fazer o backup de suas chaves antes de sair."</string>
<string name="screen_signout_key_backup_offline_title">"O backup das suas chaves ainda está em andamento"</string>
<string name="screen_signout_key_backup_ongoing_subtitle">"Aguarde até que isso seja concluído antes de sair."</string>
<string name="screen_signout_key_backup_ongoing_title">"O backup das suas chaves ainda está em andamento"</string>
<string name="screen_signout_preference_item">"Sair"</string>
<string name="screen_signout_preference_item">"Remover este dispositivo"</string>
<string name="screen_signout_recovery_disabled_subtitle">"Você está prestes a sair da sua última sessão. Se você sair agora, perderá o acesso às suas mensagens criptografadas."</string>
<string name="screen_signout_recovery_disabled_title">"A recuperação não está configurada"</string>
<string name="screen_signout_recovery_disabled_title">"Você está prestes a perder o acesso às suas conversas criptografadas"</string>
<string name="screen_signout_save_recovery_key_subtitle">"Você está prestes a sair da sua última sessão. Se você sair agora, poderá perder o acesso às suas mensagens criptografadas."</string>
<string name="screen_signout_save_recovery_key_title">"Você salvou sua chave de recuperação?"</string>
</resources>
@@ -1,18 +1,18 @@
<?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_submit">"Вийти"</string>
<string name="screen_signout_confirmation_dialog_title">"Вийти"</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_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_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_ongoing_subtitle">"Дочекайтеся завершення процесу, перш ніж вийти."</string>
<string name="screen_signout_key_backup_ongoing_subtitle">"Дочекайтесь завершення, перш ніж вилучити цей пристрій."</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>
<string name="screen_signout_save_recovery_key_subtitle">"Ви збираєтеся вийти зі свого останнього сеансу. Якщо вийти зараз, ви можете втратити доступ до зашифрованих повідомлень."</string>
<string name="screen_signout_save_recovery_key_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>
<string name="screen_signout_save_recovery_key_subtitle">"Це ваш єдиний пристрій. Якщо ви його вилучите, вам знадобиться ключ відновлення, щоб підтвердити свою цифрову ідентичність і відновити зашифровані бесіди під час наступного входу."</string>
<string name="screen_signout_save_recovery_key_title">"Переконайтеся, що у вас є доступ до ключа відновлення, перш ніж вилучити цей пристрій"</string>
</resources>
@@ -43,6 +43,7 @@ fun TimelineEventTimestampView(
event: TimelineItem.Event,
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
modifier: Modifier = Modifier,
isLayoutDirectionMismatched: Boolean = false,
) {
val formattedTime = event.sentTime
val hasError = event.failedToSend
@@ -77,9 +78,16 @@ fun TimelineEventTimestampView(
else -> Modifier
}
}
val padding = if (!isLayoutDirectionMismatched) {
PaddingValues(start = TimelineEventTimestampViewDefaults.spacing)
} else {
PaddingValues(end = TimelineEventTimestampViewDefaults.spacing)
}
Row(
modifier = Modifier
.padding(PaddingValues(start = TimelineEventTimestampViewDefaults.spacing))
.padding(padding)
// For a better click target, make the corners rounded
.clip(RoundedCornerShape(8.dp))
.then(clickableModifier)
@@ -39,7 +39,9 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.res.pluralStringResource
@@ -49,9 +51,12 @@ import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.traversalIndex
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.constraintlayout.compose.ConstrainScope
@@ -68,12 +73,14 @@ import io.element.android.features.messages.impl.timeline.components.receipt.Rea
import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions
import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
@@ -91,6 +98,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.modifiers.niceClickable
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewWithExtraLargeHeight
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.USER_NAME_ALICE
import io.element.android.libraries.designsystem.swipe.SwipeableActionsState
@@ -106,6 +114,7 @@ import io.element.android.libraries.matrix.api.timeline.item.EmbeddedEventInfo
import io.element.android.libraries.matrix.api.timeline.item.ThreadSummary
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileDetails
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.getAvatarUrl
@@ -122,7 +131,9 @@ import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonPlurals
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
import io.element.android.libraries.ui.utils.text.detect
import io.element.android.wysiwyg.link.Link
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.launch
import kotlin.math.abs
import kotlin.math.roundToInt
@@ -649,23 +660,44 @@ private fun MessageEventBubbleContent(
.padding(horizontal = 4.dp, vertical = 2.dp)
)
}
TimestampPosition.Aligned ->
ContentAvoidingLayout(
modifier = modifier,
// The spacing is negative to make the content overlap the empty space at the start of the timestamp
spacing = (-4).dp,
overlayOffset = DpOffset(0.dp, -1.dp),
shrinkContent = canShrinkContent,
content = { content(this::onContentLayoutChange) },
overlay = {
TimelineEventTimestampView(
event = event,
eventSink = eventSink,
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 4.dp)
)
TimestampPosition.Aligned -> @Composable {
val originalLayoutDirection = LocalLayoutDirection.current
// Detect if the direction of the text content (if any) does not match the layout direction, to place the content and timestamp correctly
val contentDirection = if (event.content is TimelineItemTextContent) {
remember(event.content.body) {
when (TextDirection.detect(event.content.body)) {
TextDirection.Ltr, TextDirection.ContentOrLtr -> LayoutDirection.Ltr
TextDirection.Rtl, TextDirection.ContentOrRtl -> LayoutDirection.Rtl
else -> originalLayoutDirection
}
}
)
} else {
originalLayoutDirection
}
CompositionLocalProvider(LocalLayoutDirection provides contentDirection) {
ContentAvoidingLayout(
modifier = modifier,
// The spacing is negative to make the content overlap the empty space at the start of the timestamp
spacing = (-4).dp,
overlayOffset = DpOffset(0.dp, -1.dp),
shrinkContent = canShrinkContent,
content = { content(this::onContentLayoutChange) },
overlay = {
// Use the original layout direction for the timestamp
CompositionLocalProvider(LocalLayoutDirection provides originalLayoutDirection) {
TimelineEventTimestampView(
event = event,
eventSink = eventSink,
isLayoutDirectionMismatched = originalLayoutDirection != contentDirection,
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 4.dp)
)
}
}
)
}
}
TimestampPosition.Below ->
Column(modifier) {
content {}
@@ -879,6 +911,149 @@ internal fun TimelineItemEventRowWithThreadSummaryPreview() = ElementPreview {
}
}
@PreviewWithExtraLargeHeight
@Composable
internal fun TimelineItemEventRowRtlContentPreview() = ElementPreview {
Column {
Text(
modifier = Modifier
.fillMaxWidth()
.background(Color.LightGray)
.padding(8.dp),
text = "LTR layout direction",
textAlign = TextAlign.Center,
style = ElementTheme.typography.fontHeadingMdBold,
)
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
sequenceOf(false, true).forEach { isMine ->
ATimelineItemEventRow(
event = aTimelineItemEvent(
senderDisplayName = "Sender with a super long name that should ellipsize",
isMine = isMine,
content = aTimelineItemTextContent(
body = "ظَة وَدَاع يَسْتَغْرِب فِيهَ"
),
timelineItemReactions = TimelineItemReactions(persistentListOf()),
groupPosition = TimelineItemGroupPosition.First,
)
)
ATimelineItemEventRow(
event = aTimelineItemEvent(
senderDisplayName = "Sender with a super long name that should ellipsize",
isMine = isMine,
content = aTimelineItemTextContent(
body = "ظَة وَدَاع يَسْتَغْرِب فِيهَ",
isEdited = true,
),
timelineItemReactions = TimelineItemReactions(persistentListOf()),
groupPosition = TimelineItemGroupPosition.Middle,
messageShield = MessageShield.UnknownDevice(isCritical = true),
)
)
ATimelineItemEventRow(
event = aTimelineItemEvent(
senderDisplayName = "Sender with a super long name that should ellipsize",
isMine = isMine,
content = aTimelineItemTextContent(
body = "ظَة وَدَاع \nيَسْتَغْرِب فِيهَ"
),
timelineItemReactions = TimelineItemReactions(persistentListOf()),
groupPosition = TimelineItemGroupPosition.Middle,
)
)
ATimelineItemEventRow(
event = aTimelineItemEvent(
senderDisplayName = "Sender with a super long name that should ellipsize",
isMine = isMine,
content = aTimelineItemTextContent(
body = "ظَة وَدَاع يَسْتَغْرِب فِيهَا اَلشَّاعِر أَنْ لَا يَبْكِي مِنْ أَلَم اَلْفِرَاق،" +
" وَيَصِف حَالَة اَلْمُودِعِينَ"
),
timelineItemReactions = TimelineItemReactions(persistentListOf()),
groupPosition = TimelineItemGroupPosition.Last,
)
)
}
}
Spacer(modifier = Modifier.height(16.dp))
Text(
modifier = Modifier
.fillMaxWidth()
.background(Color.LightGray)
.padding(8.dp),
text = "RTL layout direction",
textAlign = TextAlign.Center,
style = ElementTheme.typography.fontHeadingMdBold,
)
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
sequenceOf(false, true).forEach { isMine ->
ATimelineItemEventRow(
event = aTimelineItemEvent(
senderDisplayName = "Sender with a super long name that should ellipsize",
isMine = isMine,
content = aTimelineItemTextContent(
body = "ظَة وَدَاع يَسْتَغْرِب فِيهَا اَلشَّاعِر أَنْ لَا يَبْكِي مِنْ أَلَم اَلْفِرَاق،" +
" وَيَصِف حَالَة اَلْمُودِعِينَ",
),
timelineItemReactions = TimelineItemReactions(persistentListOf()),
groupPosition = TimelineItemGroupPosition.First,
)
)
ATimelineItemEventRow(
event = aTimelineItemEvent(
senderDisplayName = "Sender with a super long name that should ellipsize",
isMine = isMine,
content = aTimelineItemTextContent(
body = "Testing\nLTR Line\nBreaks.",
),
timelineItemReactions = TimelineItemReactions(persistentListOf()),
groupPosition = TimelineItemGroupPosition.Middle,
)
)
ATimelineItemEventRow(
event = aTimelineItemEvent(
senderDisplayName = "Sender with a super long name that should ellipsize",
isMine = isMine,
content = aTimelineItemTextContent(
body = "Testing a very long LTR text in an RTL layout."
),
timelineItemReactions = TimelineItemReactions(persistentListOf()),
groupPosition = TimelineItemGroupPosition.Middle,
)
)
ATimelineItemEventRow(
event = aTimelineItemEvent(
senderDisplayName = "Sender with a super long name that should ellipsize",
isMine = isMine,
content = aTimelineItemTextContent(
body = "Testing LTR in RTL layout.",
),
timelineItemReactions = TimelineItemReactions(persistentListOf()),
groupPosition = TimelineItemGroupPosition.Last,
messageShield = MessageShield.UnknownDevice(isCritical = true),
)
)
ATimelineItemEventRow(
event = aTimelineItemEvent(
senderDisplayName = "Sender with a super long name that should ellipsize",
isMine = isMine,
content = aTimelineItemTextContent(
body = "Testing LTR in RTL layout.",
isEdited = true,
),
timelineItemReactions = TimelineItemReactions(persistentListOf()),
groupPosition = TimelineItemGroupPosition.Last,
messageShield = MessageShield.UnknownDevice(isCritical = true),
)
)
}
}
}
}
@PreviewsDayNight
@Composable
internal fun ThreadSummaryViewPreview() {
@@ -15,13 +15,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.style.ResolvedTextDirection
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.designsystem.text.roundToPx
import io.element.android.wysiwyg.compose.EditorStyledText
import kotlin.math.max
@@ -149,13 +149,13 @@ object ContentAvoidingLayout {
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
extraWidth: Dp = 0.dp,
): ((TextLayoutResult) -> Unit) {
val layoutDirection = LocalLayoutDirection.current
val extraWidthPx = extraWidth.roundToPx()
return { textLayout: TextLayoutResult ->
// We need to add the external extra width so it's not taken into account as 'free space'
val lastLineWidth = when (layoutDirection) {
LayoutDirection.Ltr -> textLayout.getLineRight(textLayout.lineCount - 1).roundToInt()
LayoutDirection.Rtl -> textLayout.getLineLeft(textLayout.lineCount - 1).roundToInt()
val textDirection = tryOrNull { textLayout.getParagraphDirection(0) }
val lastLineWidth = when (textDirection) {
ResolvedTextDirection.Rtl -> textLayout.getLineLeft(textLayout.lineCount - 1).roundToInt()
else -> textLayout.getLineRight(textLayout.lineCount - 1).roundToInt()
}
val lastLineHeight = textLayout.getLineBottom(textLayout.lineCount - 1).roundToInt()
onContentLayoutChange(
@@ -23,6 +23,7 @@
<string name="screen_media_upload_preview_error_failed_sending">"Gagal mengunggah media, silakan coba lagi."</string>
<string name="screen_media_upload_preview_error_too_large_message">"Ukuran file maksimum yang diizinkan adalah%1$s ."</string>
<string name="screen_media_upload_preview_error_too_large_title">"Ukuran file terlalu besar untuk diunggah."</string>
<string name="screen_media_upload_preview_item_count">"Item %1$d dari %2$d"</string>
<string name="screen_media_upload_preview_optimize_image_quality_title">"Optimalkan kualitas gambar"</string>
<string name="screen_media_upload_preview_processing">"Memproses…"</string>
<string name="screen_report_content_block_user">"Blokir pengguna"</string>
@@ -35,7 +35,7 @@
<string name="screen_room_attachment_source_camera_video">"Gravar vídeo"</string>
<string name="screen_room_attachment_source_files">"Anexo"</string>
<string name="screen_room_attachment_source_gallery">"Biblioteca de fotos e vídeos"</string>
<string name="screen_room_attachment_source_location">"Localização"</string>
<string name="screen_room_attachment_source_location">"Compartilhar localização"</string>
<string name="screen_room_attachment_source_poll">"Enquete"</string>
<string name="screen_room_attachment_text_formatting">"Formatação de texto"</string>
<string name="screen_room_encrypted_history_banner">"O histórico de mensagens não está disponível no momento."</string>
@@ -2,11 +2,11 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="crypto_event_authenticity_mismatched_sender">"Відправник події не збігається з власником пристрою, який його надіслав."</string>
<string name="crypto_event_authenticity_not_guaranteed">"Автентичність цього зашифрованого повідомлення не може бути гарантована на цьому пристрої."</string>
<string name="crypto_event_authenticity_previously_verified">"Зашифровано попередньо перевіреним користувачем."</string>
<string name="crypto_event_authenticity_previously_verified">"Зашифровано звіреним раніше користувачем."</string>
<string name="crypto_event_authenticity_sent_in_clear">"Не зашифровано."</string>
<string name="crypto_event_authenticity_unknown_device">"Зашифровано невідомим або видаленим пристроєм."</string>
<string name="crypto_event_authenticity_unsigned_device">"Зашифровано пристроєм, який не верифіковано його власником."</string>
<string name="crypto_event_authenticity_unverified_identity">"Зашифровано неверифікованим користувачем."</string>
<string name="crypto_event_authenticity_unsigned_device">"Зашифровано пристроєм, який не звірено його власником."</string>
<string name="crypto_event_authenticity_unverified_identity">"Зашифровано не звіреним користувачем."</string>
<string name="emoji_picker_category_activity">"Дії"</string>
<string name="emoji_picker_category_flags">"Прапори"</string>
<string name="emoji_picker_category_foods">"Їжа та напої"</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>
@@ -47,7 +53,7 @@
<string name="screen_room_attachment_source_poll">"Опитування"</string>
<string name="screen_room_attachment_text_formatting">"Форматування тексту"</string>
<string name="screen_room_encrypted_history_banner">"Історія повідомлень наразі недоступна."</string>
<string name="screen_room_encrypted_history_banner_unverified">"Історія повідомлень недоступна в цій кімнаті. Перевірте цей пристрій, щоб побачити історію повідомлень."</string>
<string name="screen_room_encrypted_history_banner_unverified">"Історія повідомлень недоступна в цій кімнаті. Звірте цей пристрій, щоб побачити історію повідомлень."</string>
<string name="screen_room_invite_again_alert_message">"Чи хотіли б ви запросити їх знову?"</string>
<string name="screen_room_invite_again_alert_title">"Ви одні в цій бесіді"</string>
<string name="screen_room_mentions_at_room_subtitle">"Сповістити всю кімнату"</string>
@@ -15,4 +15,6 @@ sealed interface DeveloperSettingsEvents {
data class ChangeBrandColor(val color: Color?) : DeveloperSettingsEvents
data object ClearCache : DeveloperSettingsEvents
data object VacuumStores : DeveloperSettingsEvents
data class MarkAllRoomsAsRead(val needsConfirmation: Boolean) : DeveloperSettingsEvents
data object DismissMarkAllRoomsAsReadConfirmation : DeveloperSettingsEvents
}
@@ -22,6 +22,7 @@ import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.preferences.impl.developer.appsettings.AppDeveloperSettingsState
import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase
import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase
import io.element.android.features.preferences.impl.tasks.MarkAllRoomsAsRead
import io.element.android.features.preferences.impl.tasks.VacuumStoresUseCase
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
import io.element.android.libraries.architecture.AsyncAction
@@ -46,6 +47,7 @@ class DeveloperSettingsPresenter(
private val vacuumStoresUseCase: VacuumStoresUseCase,
private val databaseSizesUseCase: GetDatabaseSizesUseCase,
private val fileSizeFormatter: FileSizeFormatter,
private val markAllRoomsAsRead: MarkAllRoomsAsRead,
) : Presenter<DeveloperSettingsState> {
@Composable
override fun present(): DeveloperSettingsState {
@@ -58,6 +60,9 @@ class DeveloperSettingsPresenter(
val clearCacheAction = remember {
mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized)
}
val markAllRoomsAsReadAction = remember {
mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized)
}
var showColorPicker by remember {
mutableStateOf(false)
}
@@ -88,6 +93,18 @@ class DeveloperSettingsPresenter(
DeveloperSettingsEvents.VacuumStores -> coroutineScope.launch {
vacuumStoresUseCase()
}
is DeveloperSettingsEvents.MarkAllRoomsAsRead -> {
if (event.needsConfirmation) {
markAllRoomsAsReadAction.value = AsyncAction.ConfirmingNoParams
} else {
coroutineScope.markAllRoomsAsRead(
markAllRoomsAsReadAction = markAllRoomsAsReadAction,
)
}
}
DeveloperSettingsEvents.DismissMarkAllRoomsAsReadConfirmation -> {
markAllRoomsAsReadAction.value = AsyncAction.Uninitialized
}
}
}
@@ -97,6 +114,7 @@ class DeveloperSettingsPresenter(
cacheSize = cacheSize.value,
databaseSizes = databaseSizes.value,
clearCacheAction = clearCacheAction.value,
markAllRoomsAsReadAction = markAllRoomsAsReadAction.value,
isEnterpriseBuild = enterpriseService.isEnterpriseBuild,
showColorPicker = showColorPicker,
eventSink = ::handleEvent,
@@ -131,8 +149,14 @@ class DeveloperSettingsPresenter(
}
private fun CoroutineScope.clearCache(clearCacheAction: MutableState<AsyncAction<Unit>>) = launch {
suspend { clearCacheUseCase() }.runCatchingUpdatingState(state = clearCacheAction)
}
private fun CoroutineScope.markAllRoomsAsRead(
markAllRoomsAsReadAction: MutableState<AsyncAction<Unit>>,
) = launch {
suspend {
clearCacheUseCase()
}.runCatchingUpdatingState(clearCacheAction)
markAllRoomsAsRead().getOrThrow()
}.runCatchingUpdatingState(state = markAllRoomsAsReadAction)
}
}
@@ -18,9 +18,10 @@ data class DeveloperSettingsState(
val cacheSize: AsyncData<String>,
val databaseSizes: AsyncData<ImmutableMap<String, String>>,
val clearCacheAction: AsyncAction<Unit>,
val markAllRoomsAsReadAction: AsyncAction<Unit>,
val isEnterpriseBuild: Boolean,
val showColorPicker: Boolean,
val eventSink: (DeveloperSettingsEvents) -> Unit
) {
val showLoader = clearCacheAction is AsyncAction.Loading
val showLoader = clearCacheAction is AsyncAction.Loading || markAllRoomsAsReadAction is AsyncAction.Loading
}
@@ -37,6 +37,7 @@ open class DeveloperSettingsStateProvider : PreviewParameterProvider<DeveloperSe
fun aDeveloperSettingsState(
appDeveloperSettingsState: AppDeveloperSettingsState = anAppDeveloperSettingsState(),
clearCacheAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
markAllRoomsAsReadAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
isEnterpriseBuild: Boolean = false,
showColorPicker: Boolean = false,
eventSink: (DeveloperSettingsEvents) -> Unit = {},
@@ -45,6 +46,7 @@ fun aDeveloperSettingsState(
cacheSize = AsyncData.Success("1.2 MB"),
databaseSizes = AsyncData.Success(persistentMapOf("state_store" to "1.2MB")),
clearCacheAction = clearCacheAction,
markAllRoomsAsReadAction = markAllRoomsAsReadAction,
isEnterpriseBuild = isEnterpriseBuild,
showColorPicker = showColorPicker,
eventSink = eventSink,
@@ -18,9 +18,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.preferences.impl.R
import io.element.android.features.preferences.impl.developer.appsettings.AppDeveloperSettingsView
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
@@ -45,6 +47,15 @@ fun DeveloperSettingsView(
if (state.showLoader) {
ProgressDialog()
}
if (state.markAllRoomsAsReadAction.isConfirming()) {
ConfirmationDialog(
title = "Are you sure you want to mark all the rooms as read?",
content = "",
submitText = stringResource(CommonStrings.action_yes),
onSubmitClick = { state.eventSink(DeveloperSettingsEvents.MarkAllRoomsAsRead(needsConfirmation = false)) },
onDismiss = { state.eventSink(DeveloperSettingsEvents.DismissMarkAllRoomsAsReadConfirmation) },
)
}
BackHandler(
enabled = !state.showLoader,
onBack = onBackClick,
@@ -64,6 +75,7 @@ fun DeveloperSettingsView(
onOpenShowkase = onOpenShowkase,
)
NotificationCategory(onPushHistoryClick)
MarkAllRoomsAsReadCategory(state)
if (state.isEnterpriseBuild) {
PreferenceCategory(title = "Theme") {
@@ -152,6 +164,32 @@ fun DeveloperSettingsView(
)
}
@Composable
private fun MarkAllRoomsAsReadCategory(state: DeveloperSettingsState) {
PreferenceCategory(title = "Room list") {
ListItem(
headlineContent = {
Text("Mark all rooms as read")
},
supportingContent = {
Text(
text = """
This will send a private read receipt and a read marker in every room you are part of.
It's a long running operation that might get rate limited.
It will run in the background but the app must be alive for it to finish.
""".trimIndent(),
style = ElementTheme.typography.fontBodySmRegular,
color = ElementTheme.colors.textSecondary,
)
},
enabled = !state.showLoader,
onClick = {
state.eventSink(DeveloperSettingsEvents.MarkAllRoomsAsRead(needsConfirmation = true))
},
)
}
}
@Composable
private fun NotificationCategory(onPushHistoryClick: () -> Unit) {
PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_title)) {
@@ -0,0 +1,33 @@
/*
* 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.features.preferences.impl.tasks
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.push.api.notifications.NotificationCleaner
import kotlinx.coroutines.withContext
interface MarkAllRoomsAsRead {
suspend operator fun invoke(): Result<Unit>
}
@ContributesBinding(SessionScope::class)
class DefaultMarkAllRoomsAsRead(
private val client: MatrixClient,
private val notificationCleaner: NotificationCleaner,
private val coroutineDispatchers: CoroutineDispatchers,
) : MarkAllRoomsAsRead {
override suspend fun invoke(): Result<Unit> = withContext(coroutineDispatchers.io) {
client.markAllRoomsAsRead()
.onSuccess {
notificationCleaner.clearAllMessagesEvents(client.sessionId)
}
}
}
@@ -0,0 +1,45 @@
/*
* 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.features.preferences.impl.tasks
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.push.api.notifications.NotificationCleaner
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
interface MarkRoomAsRead {
suspend operator fun invoke(roomId: RoomId): Result<Unit>
}
@ContributesBinding(SessionScope::class)
class DefaultMarkRoomAsRead(
private val client: MatrixClient,
private val notificationCleaner: NotificationCleaner,
private val sessionPreferencesStore: SessionPreferencesStore,
private val coroutineDispatchers: CoroutineDispatchers,
) : MarkRoomAsRead {
override suspend fun invoke(roomId: RoomId): Result<Unit> = withContext(coroutineDispatchers.io) {
notificationCleaner.clearMessagesForRoom(client.sessionId, roomId)
val room = client.getRoom(roomId) ?: return@withContext Result.failure(IllegalStateException("Room not found"))
room.use {
it.setUnreadFlag(isUnread = false)
val receiptType = if (sessionPreferencesStore.isSendPublicReadReceiptsEnabled().first()) {
ReceiptType.READ
} else {
ReceiptType.READ_PRIVATE
}
it.markAsRead(receiptType)
}
}
}
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="full_screen_intent_banner_message">"Щоб ніколи не пропустити важливий виклик, змініть налаштування, щоб увімкнути повноекранні сповіщення, коли телефон заблоковано."</string>
<string name="full_screen_intent_banner_title">"Покращуйте досвід дзвінків"</string>
<string name="full_screen_intent_banner_title">"Покращуйте якість викликів"</string>
<string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Виберіть спосіб отримання сповіщень"</string>
<string name="screen_advanced_settings_developer_mode">"Режим розробника"</string>
<string name="screen_advanced_settings_developer_mode_description">"Увімкніть доступ до функцій і можливостей для розробників."</string>
@@ -10,18 +10,18 @@
<string name="screen_advanced_settings_element_call_base_url_validation_error">"Неправильна URL-адреса. Переконайтеся, що ви вказали протокол (http/https) та правильну адресу."</string>
<string name="screen_advanced_settings_hide_invite_avatars_toggle_title">"Сховати аватари у запитах на запрошення до кімнат"</string>
<string name="screen_advanced_settings_hide_timeline_media_toggle_title">"Сховати попередній перегляд медіа у стрічці"</string>
<string name="screen_advanced_settings_labs">"Лабораторії"</string>
<string name="screen_advanced_settings_labs">"Лабораторія"</string>
<string name="screen_advanced_settings_live_location_section_description">"Відстань, яку потрібно пройти, щоб ініціювати оновлення."</string>
<string name="screen_advanced_settings_live_location_section_footer">"Переконайтеся, що для цього додатка увімкнено функцію «Точна геолокація». Щоб змінити дозвіл, перейдіть на сторінку %1$s."</string>
<string name="screen_advanced_settings_live_location_section_footer_link">"Налаштування додатка"</string>
<string name="screen_advanced_settings_live_location_section_footer">"Переконайтеся, що для цього застосунку ввімкнено функцію «Точна геолокація». Щоб змінити дозвіл, перейдіть на сторінку %1$s."</string>
<string name="screen_advanced_settings_live_location_section_footer_link">"Налаштування застосунку"</string>
<string name="screen_advanced_settings_live_location_section_title">"Оновлення місцезнаходження в реальному часі"</string>
<plurals name="screen_advanced_settings_live_location_update_distance">
<item quantity="one">"Кожен %1$d метр"</item>
<item quantity="few">"Кожні %1$d метри"</item>
<item quantity="many">"Кожні %1$d метрів"</item>
</plurals>
<string name="screen_advanced_settings_media_compression_description">"Швидше завантажуйте фотографії та відео та зменшуйте використання даних"</string>
<string name="screen_advanced_settings_media_compression_title">"Оптимізуйте медіаякість"</string>
<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>
@@ -34,14 +34,14 @@
<string name="screen_advanced_settings_rich_text_editor_description">"Вимкніть редактор розширеного тексту, щоб вводити Markdown вручну."</string>
<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">"Ділитися присутністю"</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_advanced_settings_view_source_description">"Увімкнення цього параметра уможливить перегляд джерела повідомлень зі стрічки."</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>
@@ -55,11 +55,12 @@
<string name="screen_edit_profile_updating_details">"Оновлення профілю…"</string>
<string name="screen_labs_enable_threads">"Увімкнути відповіді в гілках"</string>
<string name="screen_labs_enable_threads_description">"Застосунок перезапуститься, щоб застосувати цю зміну."</string>
<string name="screen_labs_header_description">"Випробуйте наші останні ідеї, що перебувають на стадії розробки. Ці функції ще не остаточні; вони можуть бути нестабільними та змінюватися."</string>
<string name="screen_labs_header_description">"Випробуйте найновіші ідеї наших розробників. Ці функції ще не завершені, вони можуть бути нестабільними та змінюватись."</string>
<string name="screen_labs_header_title">"Хочете поекспериментувати?"</string>
<string name="screen_labs_title">"Лабораторії"</string>
<string name="screen_labs_title">"Лабораторія"</string>
<string name="screen_notification_settings_additional_settings_section_title">"Додаткові налаштування"</string>
<string name="screen_notification_settings_calls_label">"Аудіо та відеодзвінки"</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">"Ми спростили налаштування сповіщень, щоб полегшити пошук параметрів. Деякі користувацькі налаштування, які ви вибрали раніше, тут не відображаються, але вони все ще активні.
@@ -77,18 +78,25 @@
<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_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_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_silent">"Без звуку"</string>
<string name="screen_notification_settings_sound_system_default">"Як у системі"</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>
@@ -17,6 +17,7 @@ import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.preferences.impl.developer.appsettings.anAppDeveloperSettingsState
import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase
import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase
import io.element.android.features.preferences.impl.tasks.FakeMarkAllRoomsAsRead
import io.element.android.features.preferences.impl.tasks.VacuumStoresUseCase
import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter
import io.element.android.libraries.architecture.AsyncAction
@@ -127,6 +128,28 @@ class DeveloperSettingsPresenterTest {
}
}
@Test
fun `present - confirm mark all rooms as read`() = runTest {
val markAllRoomsAsRead = FakeMarkAllRoomsAsRead()
val presenter = createDeveloperSettingsPresenter(markAllRoomsAsRead = markAllRoomsAsRead)
presenter.test {
skipItems(2)
val initialState = awaitItem()
initialState.eventSink(DeveloperSettingsEvents.MarkAllRoomsAsRead(needsConfirmation = true))
val stateWithConfirmation = awaitItem()
assertThat(stateWithConfirmation.markAllRoomsAsReadAction.isConfirming()).isTrue()
stateWithConfirmation.eventSink(DeveloperSettingsEvents.MarkAllRoomsAsRead(needsConfirmation = false))
awaitItem().also { state ->
assertThat(state.markAllRoomsAsReadAction.isConfirming()).isFalse()
assertThat(state.markAllRoomsAsReadAction).isInstanceOf(AsyncAction.Loading::class.java)
}
awaitItem().also { state ->
assertThat(state.markAllRoomsAsReadAction).isInstanceOf(AsyncAction.Success::class.java)
assertThat(markAllRoomsAsRead.invokeCallCount).isEqualTo(1)
}
}
}
@Test
fun `present - VacuumStores action invokes the VacuumStoresUseCase`() = runTest {
var vacuumCalled = false
@@ -151,6 +174,7 @@ class DeveloperSettingsPresenterTest {
enterpriseService: EnterpriseService = FakeEnterpriseService(),
vacuumStoresUseCase: VacuumStoresUseCase = VacuumStoresUseCase {},
databaseSizesUseCase: GetDatabaseSizesUseCase = GetDatabaseSizesUseCase { Result.success(SdkStoreSizes(null, null, null, null)) },
markAllRoomsAsRead: FakeMarkAllRoomsAsRead = FakeMarkAllRoomsAsRead(),
): DeveloperSettingsPresenter {
return DeveloperSettingsPresenter(
appDeveloperSettingsPresenter = { anAppDeveloperSettingsState() },
@@ -161,6 +185,7 @@ class DeveloperSettingsPresenterTest {
vacuumStoresUseCase = vacuumStoresUseCase,
databaseSizesUseCase = databaseSizesUseCase,
fileSizeFormatter = FakeFileSizeFormatter(),
markAllRoomsAsRead = markAllRoomsAsRead,
)
}
}
@@ -71,7 +71,7 @@ class DeveloperSettingsViewTest : RobolectricTest() {
}
}
@Config(qualifiers = "h2200dp")
@Config(qualifiers = "h2400dp")
@Test
fun `clicking on clear cache emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<DeveloperSettingsEvents>()
@@ -0,0 +1,22 @@
/*
* 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.features.preferences.impl.tasks
import io.element.android.tests.testutils.simulateLongTask
class FakeMarkAllRoomsAsRead(
private val invokeLambda: suspend () -> Result<Unit> = { Result.success(Unit) },
) : MarkAllRoomsAsRead {
var invokeCallCount = 0
private set
override suspend fun invoke(): Result<Unit> = simulateLongTask {
invokeCallCount++
invokeLambda()
}
}
@@ -0,0 +1,21 @@
/*
* 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.features.preferences.impl.tasks
import io.element.android.libraries.matrix.api.core.RoomId
class FakeMarkRoomAsRead(
private val invokeLambda: suspend (RoomId) -> Result<Unit> = { Result.success(Unit) },
) : MarkRoomAsRead {
val invokedRoomIds = mutableListOf<RoomId>()
override suspend fun invoke(roomId: RoomId): Result<Unit> {
invokedRoomIds.add(roomId)
return invokeLambda(roomId)
}
}
@@ -0,0 +1,61 @@
/*
* 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.features.preferences.impl.tasks
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.test.runTest
import org.junit.Test
class MarkAllRoomsAsReadTest {
@Test
fun `invoke - delegates to client and clears all notifications`() = runTest {
val markAllRoomsAsReadLambda = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
val clearAllMessagesEventsLambda = lambdaRecorder<SessionId, Unit> { }
val markAllRoomsAsRead = DefaultMarkAllRoomsAsRead(
client = FakeMatrixClient(
markAllRoomsAsReadResult = markAllRoomsAsReadLambda,
),
notificationCleaner = FakeNotificationCleaner(
clearAllMessagesEventsLambda = clearAllMessagesEventsLambda,
),
coroutineDispatchers = testCoroutineDispatchers(),
)
val result = markAllRoomsAsRead()
assertThat(result.isSuccess).isTrue()
markAllRoomsAsReadLambda.assertions().isCalledOnce()
clearAllMessagesEventsLambda.assertions().isCalledOnce().with(value(A_SESSION_ID))
}
@Test
fun `invoke - does not clear notifications when client fails`() = runTest {
val clearAllMessagesEventsLambda = lambdaRecorder<SessionId, Unit> { }
val markAllRoomsAsRead = DefaultMarkAllRoomsAsRead(
client = FakeMatrixClient(
markAllRoomsAsReadResult = { Result.failure(IllegalStateException("Failed")) },
),
notificationCleaner = FakeNotificationCleaner(
clearAllMessagesEventsLambda = clearAllMessagesEventsLambda,
),
coroutineDispatchers = testCoroutineDispatchers(),
)
val result = markAllRoomsAsRead()
assertThat(result.isFailure).isTrue()
clearAllMessagesEventsLambda.assertions().isNeverCalled()
}
}
@@ -0,0 +1,79 @@
/*
* 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.
*/
@file:OptIn(ExperimentalCoroutinesApi::class)
package io.element.android.features.preferences.impl.tasks
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
class MarkRoomAsReadTest {
@Test
fun `invoke - clears notifications, clears unread flag and sends public read receipt`() = runTest {
val markAsReadResult = lambdaRecorder<ReceiptType, Result<Unit>> { Result.success(Unit) }
val room = FakeBaseRoom(markAsReadResult = markAsReadResult)
val matrixClient = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room)
}
val clearMessagesForRoomLambda = lambdaRecorder { _: SessionId, _: RoomId -> }
val notificationCleaner = FakeNotificationCleaner(clearMessagesForRoomLambda = clearMessagesForRoomLambda)
val sessionPreferencesStore = InMemorySessionPreferencesStore()
val markRoomAsRead = DefaultMarkRoomAsRead(
client = matrixClient,
notificationCleaner = notificationCleaner,
sessionPreferencesStore = sessionPreferencesStore,
coroutineDispatchers = testCoroutineDispatchers(),
)
val result = markRoomAsRead(A_ROOM_ID)
runCurrent()
assertThat(result.isSuccess).isTrue()
clearMessagesForRoomLambda.assertions().isCalledOnce()
.with(value(A_SESSION_ID), value(A_ROOM_ID))
assertThat(room.setUnreadFlagCalls).isEqualTo(listOf(false))
markAsReadResult.assertions().isCalledOnce().with(value(ReceiptType.READ))
}
@Test
fun `invoke - sends private read receipt when public receipts are disabled`() = runTest {
val markAsReadResult = lambdaRecorder<ReceiptType, Result<Unit>> { Result.success(Unit) }
val room = FakeBaseRoom(markAsReadResult = markAsReadResult)
val matrixClient = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, room)
}
val sessionPreferencesStore = InMemorySessionPreferencesStore(isSendPublicReadReceiptsEnabled = false)
val markRoomAsRead = DefaultMarkRoomAsRead(
client = matrixClient,
notificationCleaner = FakeNotificationCleaner(clearMessagesForRoomLambda = { _, _ -> }),
sessionPreferencesStore = sessionPreferencesStore,
coroutineDispatchers = testCoroutineDispatchers(),
)
val result = markRoomAsRead(A_ROOM_ID)
runCurrent()
assertThat(result.isSuccess).isTrue()
markAsReadResult.assertions().isCalledOnce().with(value(ReceiptType.READ_PRIVATE))
}
}
@@ -9,6 +9,7 @@
<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 issue 编号(如果有)。"</string>
<string name="screen_bug_report_github_issue_label">"GitHub issue"</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>
@@ -48,7 +48,7 @@
<string name="screen_room_details_edit_room_title">"Edytuj szczegóły"</string>
<string name="screen_room_details_edition_error">"Wystąpił nieznany błąd i nie można było zmienić informacji."</string>
<string name="screen_room_details_edition_error_title">"Nie można zaktualizować pokoju"</string>
<string name="screen_room_details_encryption_enabled_subtitle">"Wiadomości są zabezpieczone kłódkami. Tylko Ty i odbiorcy macie unikalne klucze do ich odblokowania."</string>
<string name="screen_room_details_encryption_enabled_subtitle">"Wiadomości są zabezpieczone kłódkami. Tylko Ty i Twoi odbiorcy posiadacie klucze do ich otwarcia."</string>
<string name="screen_room_details_encryption_enabled_title">"Szyfrowanie wiadomości włączone"</string>
<string name="screen_room_details_error_loading_notification_settings">"Wystąpił błąd podczas ładowania ustawień powiadomień."</string>
<string name="screen_room_details_error_muting">"Wyciszenie tego pokoju nie powiodło się, spróbuj ponownie."</string>
@@ -25,7 +25,8 @@
<string name="screen_custom_recovery_key_confirm_subtitle">"Sisesta oma taastevõti uuesti."</string>
<string name="screen_custom_recovery_key_confirm_title">"Korda oma taastevõtit"</string>
<plurals name="screen_custom_recovery_key_input_notice">
<item quantity="other">"Vähemalt %1$s tähemärki. Ära kasuta oma konto salasõna."</item>
<item quantity="one">"Vähemalt %1$d tähemärk. Ära pruugi kasutajakonto salasõna."</item>
<item quantity="other">"Vähemalt %1$d tähemärki. Ära pruugi kasutajakonto salasõna."</item>
</plurals>
<string name="screen_custom_recovery_key_input_strength">"Tugevus"</string>
<string name="screen_custom_recovery_key_input_strength_a11y">"Salafraasi tugevus: %1$s"</string>
@@ -11,7 +11,7 @@
<string name="screen_chat_backup_recovery_action_change_description">"Pulihkan identitas kriptografi dan riwayat pesan Anda dengan kunci pemulihan jika Anda kehilangan semua perangkat yang ada."</string>
<string name="screen_chat_backup_recovery_action_confirm">"Masukkan kunci pemulihan"</string>
<string name="screen_chat_backup_recovery_action_confirm_description">"Penyimpanan kunci Anda saat ini tidak sinkron."</string>
<string name="screen_chat_backup_recovery_action_setup">"Siapkan pemulihan"</string>
<string name="screen_chat_backup_recovery_action_setup">"Siapkan Kunci pemulihan"</string>
<string name="screen_create_new_recovery_key_list_item_1">"Buka %1$s di perangkat desktop"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Masuk ke akun Anda lagi"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Saat diminta untuk memverifikasi perangkat Anda, pilih %1$s"</string>
@@ -25,8 +25,8 @@
<string name="screen_encryption_reset_bullet_3">"Anda perlu memverifikasi semua perangkat dan kontak yang ada lagi"</string>
<string name="screen_encryption_reset_footer">"Hanya atur ulang identitas Anda jika Anda tidak memiliki akses ke perangkat lain yang masuk dan Anda kehilangan kunci pemulihan."</string>
<string name="screen_encryption_reset_title">"Tidak dapat mengonfirmasi? Anda perlu mengatur ulang identitas Anda."</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Matikan"</string>
<string name="screen_key_backup_disable_confirmation_description">"Anda akan kehilangan pesan terenkripsi jika Anda keluar dari semua perangkat."</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Hapus"</string>
<string name="screen_key_backup_disable_confirmation_description">"Anda akan kehilangan pesan terenkripsi jika Anda menghapusnya dari semua perangkat."</string>
<string name="screen_key_backup_disable_confirmation_title">"Apakah Anda yakin ingin mematikan pencadangan?"</string>
<string name="screen_key_backup_disable_description">"Mematikan pencadangan akan menghapus pencadangan kunci terenkripsi saat ini dan mematikan fitur keamanan lainnya. Dalam hal ini, Anda akan:"</string>
<string name="screen_key_backup_disable_description_point_1">"Tidak memiliki riwayat pesan terenkripsi di perangkat baru"</string>
@@ -58,7 +58,7 @@
<string name="screen_recovery_key_setup_generate_key">"Buat kunci pemulihan Anda"</string>
<string name="screen_recovery_key_setup_generate_key_description">"Jangan bagikan ini kepada siapa pun!"</string>
<string name="screen_recovery_key_setup_success">"Penyiapan pemulihan berhasil"</string>
<string name="screen_recovery_key_setup_title">"Siapkan pemulihan"</string>
<string name="screen_recovery_key_setup_title">"Siapkan Kunci pemulihan"</string>
<string name="screen_reset_encryption_confirmation_alert_action">"Ya, atur ulang sekarang"</string>
<string name="screen_reset_encryption_confirmation_alert_subtitle">"Proses ini tidak dapat diurungkan."</string>
<string name="screen_reset_encryption_confirmation_alert_title">"Apakah Anda yakin ingin mengatur ulang identitas Anda?"</string>
@@ -2,7 +2,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_chat_backup_key_backup_action_disable">"Apagar o armazenamento de chaves"</string>
<string name="screen_chat_backup_key_backup_action_enable">"Ativar o backup"</string>
<string name="screen_chat_backup_key_backup_description">"Armazene sua identidade criptográfica e chaves de mensagem com segurança no servidor. Isso permitirá que você visualize seu histórico de mensagens em dispositivos futuros. %1$s."</string>
<string name="screen_chat_backup_key_backup_description">"Isso te permite ver seu histórico de conversas em dispositivos novos, e é necessário para o backup de conversas e identidade digital. %1$s"</string>
<string name="screen_chat_backup_key_backup_title">"Armazenamento de chaves"</string>
<string name="screen_chat_backup_key_storage_disabled_error">"O armazenamento de chaves deve ser ativado para configurar a recuperação."</string>
<string name="screen_chat_backup_key_storage_toggle_description">"Enviar chaves a partir deste dispositivo"</string>
@@ -11,7 +11,7 @@
<string name="screen_chat_backup_recovery_action_change_description">"Recupere sua identidade criptográfica e o histórico de mensagens com uma chave de recuperação, caso você tenha perdido todos os dispositivos existentes."</string>
<string name="screen_chat_backup_recovery_action_confirm">"Digitar chave de recuperação"</string>
<string name="screen_chat_backup_recovery_action_confirm_description">"Seu armazenamento de chaves está fora de sincronia no momento."</string>
<string name="screen_chat_backup_recovery_action_setup">"Configurar a recuperação"</string>
<string name="screen_chat_backup_recovery_action_setup">"Receber chave de recuperação"</string>
<string name="screen_create_new_recovery_key_list_item_1">"Abra o %1$s em um computador"</string>
<string name="screen_create_new_recovery_key_list_item_2">"Entre na sua conta novamente"</string>
<string name="screen_create_new_recovery_key_list_item_3">"Ao ser solicitado para verificar o seu dispositivo, selecione %1$s"</string>
@@ -25,10 +25,10 @@
<string name="screen_encryption_reset_bullet_3">"Você precisará verificar todos os seus dispositivos e contatos existentes novamente."</string>
<string name="screen_encryption_reset_footer">"Redefina sua identidade somente se você não tiver acesso a outro dispositivo conectado e se tiver perdido sua chave de recuperação."</string>
<string name="screen_encryption_reset_title">"Não consegue confirmar? Você precisará redefinir sua identidade."</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Desativar"</string>
<string name="screen_key_backup_disable_confirmation_description">"Você perderá suas mensagens criptografadas se for desconectado de todos os dispositivos."</string>
<string name="screen_key_backup_disable_confirmation_title">"Tem certeza de que deseja desativar o backup?"</string>
<string name="screen_key_backup_disable_description">"Ao apagar o armazenamento de chaves, a sua identidade criptográfica e as chaves das mensagens serão apagadas do servidor e os seguintes recursos de segurança serão desativados:"</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Apagar"</string>
<string name="screen_key_backup_disable_confirmation_description">"Você perderá o seu histórico de mensagens criptografadas e precisará reconfigurar sua identidade digital se remover todos os dispositivos."</string>
<string name="screen_key_backup_disable_confirmation_title">"Tem certeza de que deseja apagar o armazenamento de chaves?"</string>
<string name="screen_key_backup_disable_description">"Ao apagar o armazenamento de chaves, a sua identidade digital e as chaves das mensagens serão apagadas do servidor e os seguintes recursos de segurança serão desativados:"</string>
<string name="screen_key_backup_disable_description_point_1">"Você não terá o histórico de mensagens criptografadas em dispositivos novos"</string>
<string name="screen_key_backup_disable_description_point_2">"Você perderá o acesso às suas mensagens criptografadas se for desconectado de %1$s em todos os dispositivos"</string>
<string name="screen_key_backup_disable_title">"Tem certeza de que deseja desativar o armazenamento de chaves e apagá-lo?"</string>
@@ -58,7 +58,7 @@
<string name="screen_recovery_key_setup_generate_key">"Gere sua chave de recuperação"</string>
<string name="screen_recovery_key_setup_generate_key_description">"Não compartilhe isso com ninguém!"</string>
<string name="screen_recovery_key_setup_success">"Configuração de recuperação bem-sucedida"</string>
<string name="screen_recovery_key_setup_title">"Configurar a recuperação"</string>
<string name="screen_recovery_key_setup_title">"Receber chave de recuperação"</string>
<string name="screen_reset_encryption_confirmation_alert_action">"Sim, redefinir agora"</string>
<string name="screen_reset_encryption_confirmation_alert_subtitle">"Esse processo é irreversível."</string>
<string name="screen_reset_encryption_confirmation_alert_title">"Você tem certeza de que deseja redefinir sua identidade?"</string>
@@ -2,34 +2,56 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_chat_backup_key_backup_action_disable">"Вимкнути резервне копіювання"</string>
<string name="screen_chat_backup_key_backup_action_enable">"Увімкнути резервне копіювання"</string>
<string name="screen_chat_backup_key_backup_description">"Зберігайте свій криптографічний ідентифікатор і ключі повідомлень на сервері. Це дозволить вам переглядати історію повідомлень на будь-яких нових пристроях. %1$s."</string>
<string name="screen_chat_backup_key_backup_description">"Це дозволить вам переглядати історію бесід на нових пристроях і необхідно для резервного копіювання бесід і цифрової ідентичності. %1$s."</string>
<string name="screen_chat_backup_key_backup_title">"Резервне копіювання"</string>
<string name="screen_chat_backup_key_storage_disabled_error">"Щоб налаштувати відновлення, потрібно ввімкнути зберігання ключів."</string>
<string name="screen_chat_backup_key_storage_disabled_error">"Для створення резервної копії ваших бесід необхідно ввімкнути функцію зберігання ключів."</string>
<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_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_3">"Коли вас попросять підтвердити пристрій, виберіть %1$s"</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>
<string name="screen_create_new_recovery_key_list_item_5">"Збережіть новий ключ відновлення у менеджері паролів або зашифрованій нотатці"</string>
<string name="screen_create_new_recovery_key_title">"Скинути шифрування облікового запису за допомогою іншого пристрою"</string>
<string name="screen_custom_recovery_key_confirm_error_mismatch">"Введений ключ відновлення не збігається"</string>
<string name="screen_custom_recovery_key_confirm_submit">"Завершити налаштування"</string>
<string name="screen_custom_recovery_key_confirm_subtitle">"Повторно введіть свій ключ відновлення."</string>
<string name="screen_custom_recovery_key_confirm_title">"Підтвердьте свій ключ відновлення"</string>
<plurals name="screen_custom_recovery_key_input_notice">
<item quantity="one">"Щонайменше %1$d символ. Не використовуйте пароль облікового запису."</item>
<item quantity="few">"Щонайменше %1$d символи. Не використовуйте пароль облікового запису."</item>
<item quantity="many">"Щонайменше %1$d символів. Не використовуйте пароль облікового запису."</item>
</plurals>
<string name="screen_custom_recovery_key_input_strength">"Надійність"</string>
<string name="screen_custom_recovery_key_input_strength_a11y">"Надійність парольної фрази: %1$s"</string>
<string name="screen_custom_recovery_key_input_strength_moderate">"Посередня"</string>
<string name="screen_custom_recovery_key_input_strength_strong">"Надійна"</string>
<string name="screen_custom_recovery_key_input_strength_very_strong">"Дуже надійна"</string>
<string name="screen_custom_recovery_key_input_strength_very_weak">"Дуже ненадійна"</string>
<string name="screen_custom_recovery_key_input_strength_weak">"Ненадійна"</string>
<string name="screen_custom_recovery_key_input_subtitle">"Оберіть ключ відновлення, який ви зможете запам\'ятати."</string>
<string name="screen_custom_recovery_key_input_title">"Введіть ключ відновлення"</string>
<string name="screen_custom_recovery_key_loading_a11y">"Завантаження вимог до ключа відновлення"</string>
<string name="screen_custom_recovery_key_success_notice">"Щоб змінити ключ відновлення, перейдіть до Налаштування → Шифрування → Резервне копіювання"</string>
<string name="screen_custom_recovery_key_success_subtitle">"Ви можете використовувати свій ключ відновлення для підтвердження нових пристроїв або відновлення зашифрованих бесід, якщо втратите доступ до всіх своїх пристроїв."</string>
<string name="screen_custom_recovery_key_success_title">"Налаштування резервного копіювання завершено"</string>
<string name="screen_encryption_reset_action_continue_reset">"Продовжити скидання налаштувань"</string>
<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_title">"Не можете підтвердити? Вам доведеться скинути свою ідентичність."</string>
<string name="screen_key_backup_disable_confirmation_action_turn_off">"Вимкнути"</string>
<string name="screen_key_backup_disable_confirmation_description">"Ви втратите зашифровані повідомлення, якщо вийдете з усіх пристроїв."</string>
<string name="screen_key_backup_disable_confirmation_title">"Ви впевнені, що хочете вимкнути резервне копіювання?"</string>
<string name="screen_key_backup_disable_description">"Вимкнення резервного копіювання призведе до видалення поточної резервної копії ключа шифрування та вимкнення інших функцій безпеки. В такому разі ви:"</string>
<string name="screen_encryption_reset_bullet_3">"Вам потрібно буде повторно звірити всі наявні пристрої та контакти"</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>
<string name="screen_key_backup_disable_confirmation_title">"Ви впевнені, що хочете видалити сховище ключів?"</string>
<string name="screen_key_backup_disable_description">"Видалення сховища ключів призведе до видалення вашої цифрової ідентичності та ключів повідомлень із сервера, а також вимкне ці функції безпеки:"</string>
<string name="screen_key_backup_disable_description_point_1">"Не матимете історії зашифрованих повідомлень на нових пристроях"</string>
<string name="screen_key_backup_disable_description_point_2">"Втратите доступ до зашифрованих повідомлень, якщо вийдете з усіх сеансів %1$s"</string>
<string name="screen_key_backup_disable_title">"Ви впевнені, що хочете вимкнути резервне копіювання?"</string>
@@ -55,16 +77,16 @@
<string name="screen_recovery_key_save_title">"Збережіть ключ відновлення"</string>
<string name="screen_recovery_key_setup_confirmation_description">"Після цього кроку ви не зможете отримати доступ до нового ключа відновлення."</string>
<string name="screen_recovery_key_setup_confirmation_title">"Ви зберегли ключ відновлення?"</string>
<string name="screen_recovery_key_setup_description">"Ваша резервна копія чату захищена ключем відновлення. Якщо вам потрібен новий ключ відновлення після налаштування, ви можете відтворити, вибравши «Змінити ключ відновлення»."</string>
<string name="screen_recovery_key_setup_description">"Ваше сховище ключів захищено ключем відновлення. Якщо вам знадобиться новий ключ відновлення після налаштування, ви можете створити його повторно, вибравши «Змінити ключ відновлення»."</string>
<string name="screen_recovery_key_setup_generate_key">"Згенеруйте ключ відновлення"</string>
<string name="screen_recovery_key_setup_generate_key_description">"Не діліться цим ні з ким!"</string>
<string name="screen_recovery_key_setup_success">"Налаштування відновлення виконано успішно"</string>
<string name="screen_recovery_key_setup_title">"Налаштувати відновлення"</string>
<string name="screen_recovery_key_setup_title">"Отримати ключ відновлення"</string>
<string name="screen_reset_encryption_confirmation_alert_action">"Так, скинути зараз"</string>
<string name="screen_reset_encryption_confirmation_alert_subtitle">"Цей процес незворотний."</string>
<string name="screen_reset_encryption_confirmation_alert_title">"Ви впевнені, що хочете скинути шифрування?"</string>
<string name="screen_reset_encryption_confirmation_alert_title">"Ви впевнені, що хочете скинути свою цифрову ідентичність?"</string>
<string name="screen_reset_encryption_password_error">"Сталася невідома помилка. Будь ласка, перевірте правильність пароля свого облікового запису та повторіть спробу."</string>
<string name="screen_reset_encryption_password_placeholder">"Входимо…"</string>
<string name="screen_reset_encryption_password_subtitle">"Підтвердьте, що ви хочете скинути шифрування."</string>
<string name="screen_reset_encryption_password_subtitle">"Підтвердьте, що ви хочете скинути свою цифрову ідентичність."</string>
<string name="screen_reset_encryption_password_title">"Введіть пароль облікового запису, щоб продовжити"</string>
</resources>
@@ -5,7 +5,7 @@
<string name="screen_start_chat_error_starting_chat">"Παρουσιάστηκε σφάλμα κατά την προσπάθεια έναρξης μιας συνομιλίας"</string>
<string name="screen_start_chat_join_room_by_address_action">"Συμμετοχή σε αίθουσα μέσω διεύθυνσης"</string>
<string name="screen_start_chat_join_room_by_address_invalid_address">"Μη έγκυρη διεύθυνση"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Εισάγετε…"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Εισαγάγετε διεύθυνση…"</string>
<string name="screen_start_chat_join_room_by_address_room_found">"Βρέθηκε η αντίστοιχη αίθουσα"</string>
<string name="screen_start_chat_join_room_by_address_room_not_found">"Η αίθουσα δεν βρέθηκε"</string>
<string name="screen_start_chat_join_room_by_address_supporting_text">"π.χ. #όνομα-αίθουσας:matrix.org"</string>
@@ -5,7 +5,7 @@
<string name="screen_start_chat_error_starting_chat">"Vestluse alustamisel tekkis viga"</string>
<string name="screen_start_chat_join_room_by_address_action">"Liitu jututoaga aadressi alusel"</string>
<string name="screen_start_chat_join_room_by_address_invalid_address">"See pole kehtiv aadress"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Sisene…"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Sisesta aadress…"</string>
<string name="screen_start_chat_join_room_by_address_room_found">"Leidsime vastava jututoa"</string>
<string name="screen_start_chat_join_room_by_address_room_not_found">"Jututuba ei leidu"</string>
<string name="screen_start_chat_join_room_by_address_supporting_text">"nt. #jututoa-nimi:matrix.org"</string>
@@ -5,7 +5,7 @@
<string name="screen_start_chat_error_starting_chat">"Terjadi kesalahan saat mencoba memulai obrolan"</string>
<string name="screen_start_chat_join_room_by_address_action">"Bergabung dalam ruangan berdasarkan alamat"</string>
<string name="screen_start_chat_join_room_by_address_invalid_address">"Bukan alamat yang valid"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Masuk…"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Masukkan alamat…"</string>
<string name="screen_start_chat_join_room_by_address_room_found">"Ruangan yang cocok ditemukan"</string>
<string name="screen_start_chat_join_room_by_address_room_not_found">"Ruangan tidak ditemukan"</string>
<string name="screen_start_chat_join_room_by_address_supporting_text">"mis. #nama-ruangan:matrix.org"</string>
@@ -5,7 +5,7 @@
<string name="screen_start_chat_error_starting_chat">"Wystąpił błąd podczas próby rozpoczęcia czatu"</string>
<string name="screen_start_chat_join_room_by_address_action">"Dołącz do pokoju za pomocą adresu"</string>
<string name="screen_start_chat_join_room_by_address_invalid_address">"Nieprawidłowy adres"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Wprowadź…"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Wprowadź adres…"</string>
<string name="screen_start_chat_join_room_by_address_room_found">"Znaleziono pasujący pokój"</string>
<string name="screen_start_chat_join_room_by_address_room_not_found">"Nie znaleziono pokoju"</string>
<string name="screen_start_chat_join_room_by_address_supporting_text">"np. #room-name:matrix.org"</string>
@@ -5,7 +5,7 @@
<string name="screen_start_chat_error_starting_chat">"Під час спроби почати бесіду сталася помилка"</string>
<string name="screen_start_chat_join_room_by_address_action">"Приєднатися до кімнати за адресою"</string>
<string name="screen_start_chat_join_room_by_address_invalid_address">"Недійсна адреса"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Введіть…"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Увести адресу…"</string>
<string name="screen_start_chat_join_room_by_address_room_found">"Знайдено відповідну кімнату"</string>
<string name="screen_start_chat_join_room_by_address_room_not_found">"Кімната не знайдена"</string>
<string name="screen_start_chat_join_room_by_address_supporting_text">"наприклад, #room-name:matrix.org"</string>
@@ -5,7 +5,7 @@
<string name="screen_start_chat_error_starting_chat">"在开始聊天时发生了错误"</string>
<string name="screen_start_chat_join_room_by_address_action">"输入地址加入房间"</string>
<string name="screen_start_chat_join_room_by_address_invalid_address">"地址无效"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"输入…"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"输入地址…"</string>
<string name="screen_start_chat_join_room_by_address_room_found">"找到匹配的房间"</string>
<string name="screen_start_chat_join_room_by_address_room_not_found">"未找到房间"</string>
<string name="screen_start_chat_join_room_by_address_supporting_text">"例如 #room-name:matrix.org"</string>
@@ -5,7 +5,7 @@
<string name="screen_start_chat_error_starting_chat">"An error occurred when trying to start a chat"</string>
<string name="screen_start_chat_join_room_by_address_action">"Join room by address"</string>
<string name="screen_start_chat_join_room_by_address_invalid_address">"Not a valid address"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Enter…"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Enter address…"</string>
<string name="screen_start_chat_join_room_by_address_room_found">"Matching room found"</string>
<string name="screen_start_chat_join_room_by_address_room_not_found">"Room not found"</string>
<string name="screen_start_chat_join_room_by_address_supporting_text">"e.g. #room-name:matrix.org"</string>
@@ -13,7 +13,7 @@
<string name="screen_room_member_details_unblock_alert_action">"Розблокувати"</string>
<string name="screen_room_member_details_unblock_alert_description">"Ви знову зможете бачити всі повідомлення від них."</string>
<string name="screen_room_member_details_unblock_user">"Розблокувати користувача"</string>
<string name="screen_room_member_details_verify_button_subtitle">"Використовуйте веб-додаток, щоб верифікувати цього користувача."</string>
<string name="screen_room_member_details_verify_button_title">"Перевірте %1$s"</string>
<string name="screen_room_member_details_verify_button_subtitle">"Використовуйте вебзастосунок, щоб звірити цього користувача."</string>
<string name="screen_room_member_details_verify_button_title">"Звірити %1$s"</string>
<string name="screen_start_chat_error_starting_chat">"Під час спроби почати бесіду сталася помилка"</string>
</resources>
@@ -2,7 +2,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_identity_confirmation_cannot_confirm">"Tidak dapat mengonfirmasi?"</string>
<string name="screen_identity_confirmation_create_new_recovery_key">"Buat kunci pemulihan baru"</string>
<string name="screen_identity_confirmation_subtitle">"Verifikasi perangkat ini untuk menyiapkan perpesanan aman."</string>
<string name="screen_identity_confirmation_subtitle">"Pilih cara verifikasi untuk mengatur pengiriman pesan yang aman."</string>
<string name="screen_identity_confirmation_title">"Konfirmasi bahwa ini Anda"</string>
<string name="screen_identity_confirmation_use_another_device">"Gunakan perangkat lain"</string>
<string name="screen_identity_confirmation_use_recovery_key">"Gunakan kunci pemulihan"</string>
@@ -50,5 +50,5 @@
<string name="screen_session_verification_waiting_subtitle">"Depois de aceito, você poderá continuar com a verificação."</string>
<string name="screen_session_verification_waiting_to_accept_subtitle">"Aceite a solicitação para iniciar o processo de verificação na sua outra sessão para continuar."</string>
<string name="screen_session_verification_waiting_to_accept_title">"Aguardando para aceitar a solicitação"</string>
<string name="screen_signout_in_progress_dialog_content">"Saindo…"</string>
<string name="screen_signout_in_progress_dialog_content">"Removendo dispositivo…"</string>
</resources>
@@ -2,12 +2,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_identity_confirmation_cannot_confirm">"Не можете підтвердити?"</string>
<string name="screen_identity_confirmation_create_new_recovery_key">"Створити новий ключ відновлення"</string>
<string name="screen_identity_confirmation_subtitle">"Верифікуйте цей пристрій, щоб налаштувати безпечний обмін повідомленнями."</string>
<string name="screen_identity_confirmation_title">"Підтвердьте, що це ви"</string>
<string name="screen_identity_confirmation_subtitle">"Виберіть спосіб звіряння, щоб налаштувати безпечний обмін повідомленнями."</string>
<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_title">"Пристрій перевірено"</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>
<string name="screen_session_verification_cancelled_subtitle">"Щось не так. Або час очікування запиту минув, або в запиті було відмовлено."</string>
@@ -17,38 +17,38 @@
<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_user_subtitle">"Тепер ви можете довіряти особистості цього користувача під час надсилання або отримання повідомлень."</string>
<string name="screen_session_verification_device_verified">"Пристрій перевірено"</string>
<string name="screen_session_verification_complete_user_subtitle">"Відтепер ви можете довіряти цифровій ідентичності цього користувача під час надсилання або отримання повідомлень"</string>
<string name="screen_session_verification_device_verified">"Пристрій звірено"</string>
<string name="screen_session_verification_enter_recovery_key">"Введіть ключ відновлення"</string>
<string name="screen_session_verification_failed_subtitle">"Або час очікування запиту минув, або запит було відхилено, або виникла розбіжність у верифікації."</string>
<string name="screen_session_verification_failed_subtitle">"Або час очікування запиту минув, або запит було відхилено, або виникла розбіжність у звірянні."</string>
<string name="screen_session_verification_open_existing_session_subtitle">"Доведіть, що це ви, щоб отримати доступ до історії зашифрованих повідомлень."</string>
<string name="screen_session_verification_open_existing_session_title">"Відкрийте активний сеанс"</string>
<string name="screen_session_verification_positive_button_canceled">"Повторити верифікацію"</string>
<string name="screen_session_verification_positive_button_canceled">"Повторити звіряння"</string>
<string name="screen_session_verification_positive_button_initial">"У мене все готово"</string>
<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_details_timestamp">"Увійшов"</string>
<string name="screen_session_verification_request_failure_subtitle">"Або час очікування запиту минув, або запит було відхилено, або виникла розбіжність у верифікації."</string>
<string name="screen_session_verification_request_failure_title">"Перевірка не вдалася"</string>
<string name="screen_session_verification_request_footer">"Продовжуйте, лише якщо ви ініціювали цю перевірку."</string>
<string name="screen_session_verification_request_subtitle">"Перевірте інший пристрій, щоб захистити історію повідомлень."</string>
<string name="screen_session_verification_request_failure_subtitle">"Або час очікування запиту минув, або запит було відхилено, або виникла розбіжність у звірянні."</string>
<string name="screen_session_verification_request_failure_title">"Не вдалося звірити"</string>
<string name="screen_session_verification_request_footer">"Продовжуйте, лише якщо ви ініціювали це звіряння."</string>
<string name="screen_session_verification_request_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_request_title">"Запитано на верифікацію"</string>
<string name="screen_session_verification_request_success_title">"Пристрій звірено"</string>
<string name="screen_session_verification_request_title">"Запит на звіряння"</string>
<string name="screen_session_verification_they_dont_match">"Вони не збігаються"</string>
<string name="screen_session_verification_they_match">"Вони збігаються"</string>
<string name="screen_session_verification_use_another_device_subtitle">"Перш ніж починати перевірку звідси, переконайтеся, що програму відкрито на іншому пристрої."</string>
<string name="screen_session_verification_use_another_device_title">"Відкрийте додаток на іншому перевіреному пристрої"</string>
<string name="screen_session_verification_user_initiator_subtitle">"Для додаткової безпеки верифікуйте цього користувача, порівнявши набір емоджі на ваших пристроях. Зробіть це, використовуючи надійний спосіб спілкування."</string>
<string name="screen_session_verification_user_initiator_title">"Верифікувати цього користувача?"</string>
<string name="screen_session_verification_user_responder_subtitle">"Для додаткової безпеки інший користувач хоче верифікувати вашу особистість. Вам буде показано набір емоджі для порівняння."</string>
<string name="screen_session_verification_waiting_another_device_subtitle">"Ви повинні побачити спливаюче вікно на іншому пристрої. Почніть перевірку звідти."</string>
<string name="screen_session_verification_waiting_another_device_title">"Почніть перевірку на іншому пристрої"</string>
<string name="screen_session_verification_waiting_other_device_title">"Почніть перевірку на іншому пристрої"</string>
<string name="screen_session_verification_use_another_device_subtitle">"Перш ніж розпочати звіряння звідси, переконайтеся, що ви відкрили застосунок на іншому пристрої."</string>
<string name="screen_session_verification_use_another_device_title">"Відкрийте застосунок на іншому звіреному пристрої"</string>
<string name="screen_session_verification_user_initiator_subtitle">"Для додаткової безпеки звірте цього користувача, порівнявши набір емоджі на ваших пристроях. Зробіть це, використовуючи надійний спосіб спілкування."</string>
<string name="screen_session_verification_user_initiator_title">"Звірити цього користувача?"</string>
<string name="screen_session_verification_user_responder_subtitle">"Для додаткової безпеки інший користувач хоче звірити вашу цифрову ідентичність. Вам буде показано набір емодзі для порівняння."</string>
<string name="screen_session_verification_waiting_another_device_subtitle">"На іншому пристрої має з’явитися спливне вікно. Розпочніть звіряння звідти."</string>
<string name="screen_session_verification_waiting_another_device_title">"Розпочати звіряння на іншому пристрої"</string>
<string name="screen_session_verification_waiting_other_device_title">"Розпочати звіряння на іншому пристрої"</string>
<string name="screen_session_verification_waiting_other_user_title">"Очікування іншого користувача"</string>
<string name="screen_session_verification_waiting_subtitle">"Після погодження ви зможете продовжити верифікацію."</string>
<string name="screen_session_verification_waiting_to_accept_subtitle">"Щоб продовжити, прийміть запит на початок процесу верифікації в іншому сеансі."</string>
<string name="screen_session_verification_waiting_subtitle">"Після схвалення ви зможете продовжити звіряння."</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>
</resources>
+3 -2
View File
@@ -22,7 +22,7 @@ camera = "1.6.1"
work = "2.11.2"
# Compose
compose_bom = "2026.05.01"
compose_bom = "2026.06.00"
# Coroutines
coroutines = "1.11.0"
@@ -130,6 +130,7 @@ androidx_compose_material3 = { module = "androidx.compose.material3:material3",
androidx_compose_material3_windowsizeclass = { module = "androidx.compose.material3:material3-window-size-class" }
androidx_compose_material3_adaptive = "androidx.compose.material3:material3-adaptive-android:1.0.0-alpha06"
androidx_compose_ui = { module = "androidx.compose.ui:ui" }
androidx_compose_ui_text = { module = "androidx.compose.ui:ui-text" }
androidx_compose_ui_tooling = { module = "androidx.compose.ui:ui-tooling" }
androidx_compose_ui_tooling_preview = { module = "androidx.compose.ui:ui-tooling-preview" }
androidx_compose_ui_test_manifest = { module = "androidx.compose.ui:ui-test-manifest" }
@@ -240,7 +241,7 @@ sigpwned_emoji4j = "com.sigpwned:emoji4j-core:16.0.0"
metro_runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" }
# Element Call
element_call_embedded = "io.element.android:element-call-embedded:0.20.2"
element_call_embedded = "io.element.android:element-call-embedded:0.20.3"
# Auto services
google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" }
@@ -203,6 +203,12 @@ interface MatrixClient {
*/
suspend fun markRoomAsFullyRead(roomId: RoomId, eventId: EventId): Result<Unit>
/**
* Mark all joined rooms as read by sending public, private and fully-read receipts
* on each room's latest event. Per-room errors are logged and skipped by the SDK.
*/
suspend fun markAllRoomsAsRead(): Result<Unit>
/**
* Check if linking a new device using QrCode is supported by the server.
*/
@@ -800,6 +800,12 @@ class RustMatrixClient(
}
}
override suspend fun markAllRoomsAsRead(): Result<Unit> = withContext(sessionDispatcher) {
runCatchingExceptions {
innerClient.markAllRoomsAsRead()
}
}
override suspend fun performDatabaseVacuum(): Result<Unit> = withContext(sessionDispatcher) {
runCatchingExceptions {
Timber.d("Performing database vacuuming for session $sessionId...")
@@ -117,6 +117,7 @@ class FakeMatrixClient(
private val getRecentEmojisLambda: () -> Result<List<String>> = { Result.success(emptyList()) },
private val addRecentEmojiLambda: (String) -> Result<Unit> = { Result.success(Unit) },
private val markRoomAsFullyReadResult: (RoomId, EventId) -> Result<Unit> = { _, _ -> lambdaError() },
private val markAllRoomsAsReadResult: () -> Result<Unit> = { Result.success(Unit) },
private val performDatabaseVacuumLambda: () -> Result<Unit> = { lambdaError() },
private val getMapStyleUrlResult: () -> Result<String?> = { lambdaError() },
private val getDatabaseSizesLambda: () -> Result<SdkStoreSizes> = { lambdaError() },
@@ -373,6 +374,10 @@ class FakeMatrixClient(
return markRoomAsFullyReadResult(roomId, eventId)
}
override suspend fun markAllRoomsAsRead(): Result<Unit> {
return markAllRoomsAsReadResult()
}
override suspend fun performDatabaseVacuum(): Result<Unit> {
return performDatabaseVacuumLambda()
}
@@ -3,5 +3,7 @@
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Αποστολή πρόσκλησης"</string>
<string name="screen_bottom_sheet_create_dm_message">"Θα θέλατε να ξεκινήσετε μια συνομιλία με τον χρήστη %1$s;"</string>
<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>
</resources>
@@ -3,7 +3,7 @@
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"Надіслати запрошення"</string>
<string name="screen_bottom_sheet_create_dm_message">"Хочете розпочати бесіду з %1$s?"</string>
<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_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>
</resources>
@@ -48,6 +48,8 @@
<string name="push_distributor_background_sync_android">"Sinkronisasi latar belakang"</string>
<string name="push_distributor_firebase_android">"Layanan Google"</string>
<string name="push_no_valid_google_play_services_apk_android">"Tidak ditemukan Layanan Google Play yang valid. Pemberitahuan mungkin tidak berfungsi dengan baik."</string>
<string name="troubleshoot_notifications_test_blocked_users_description">"Memeriksa pengguna yang diblokir"</string>
<string name="troubleshoot_notifications_test_blocked_users_result_none">"Tidak ada pengguna yang diblokir."</string>
<string name="troubleshoot_notifications_test_blocked_users_title">"Pengguna yang diblokir"</string>
<string name="troubleshoot_notifications_test_current_push_provider_description">"Dapatkan nama penyedia saat ini."</string>
<string name="troubleshoot_notifications_test_current_push_provider_failure">"Tidak ada penyedia notifikasi dorongan yang dipilih."</string>
@@ -15,14 +15,14 @@
<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_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>
<item quantity="many">"У вас %d нових повідомлень."</item>
</plurals>
<string name="notification_incoming_audio_call">"📞 Вхідний дзвінок"</string>
<string name="notification_incoming_audio_call">"📞 Вхідний виклик"</string>
<string name="notification_incoming_call">"📹 Вхідний виклик"</string>
<string name="notification_inline_reply_failed">"** Не вдалося надіслати - відкрийте кімнату"</string>
<string name="notification_invitation_action_join">"Доєднатися"</string>
@@ -33,7 +33,7 @@
<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_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">
@@ -81,7 +81,7 @@
<string name="troubleshoot_notifications_test_blocked_users_title">"Заблоковані користувачі"</string>
<string name="troubleshoot_notifications_test_current_push_provider_description">"Отримує назву поточного постачальника."</string>
<string name="troubleshoot_notifications_test_current_push_provider_failure">"Постачальників push-сповіщень не вибрано."</string>
<string name="troubleshoot_notifications_test_current_push_provider_failure_distributor_not_found">"Поточний провайдер push-повідомлень: %1$s та поточний дистриб\'ютор: %2$s. Але дистриб\'ютор %3$s не знайдено. Можливо, додаток було видалено?"</string>
<string name="troubleshoot_notifications_test_current_push_provider_failure_distributor_not_found">"Поточний провайдер push-повідомлень: %1$s та поточний дистриб\'ютор: %2$s. Але дистриб\'ютора %3$s не знайдено. Можливо, застосунок було видалено?"</string>
<string name="troubleshoot_notifications_test_current_push_provider_failure_no_distributor">"Поточний постачальник push-сповіщень: %1$s, але дистриб\'юторів не налаштовано."</string>
<string name="troubleshoot_notifications_test_current_push_provider_success">"Поточний постачальник: %1$s."</string>
<string name="troubleshoot_notifications_test_current_push_provider_success_with_distributor">"Поточний постачальник push-сповіщень: %1$s (%2$s)"</string>
@@ -463,6 +463,13 @@
<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_live_location_sheet_nobody_sharing">"Κανείς δεν μοιράζεται την τοποθεσία του"</string>
<string name="screen_live_location_sheet_sharing_live_location">"Κοινοποιείς την τοποθεσία σε πραγματικό χρόνο"</string>
<plurals name="screen_live_location_sheet_subtitle">
<item quantity="one">"%1$d άτομο"</item>
<item quantity="other">"%1$d άτομα"</item>
</plurals>
<string name="screen_live_location_sheet_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>
@@ -487,6 +494,7 @@
<string name="screen_room_event_pill">"Μήνυμα στο %1$s"</string>
<string name="screen_room_grouped_state_events_expand">"Επέκταση"</string>
<string name="screen_room_grouped_state_events_reduce">"Μείωση"</string>
<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>
@@ -5,6 +5,8 @@
<string name="a11y_address">"Aadress"</string>
<string name="a11y_avatar">"Tunnuspilt"</string>
<string name="a11y_collapse_message_text_field">"Vähenda tekstivälja"</string>
<string name="a11y_create_poll_votes_allowed_decrease">"Vähenda kasutaja kohta lubatud hääletuskordi"</string>
<string name="a11y_create_poll_votes_allowed_increase">"Suurenda kasutaja kohta lubatud hääletuskordi"</string>
<string name="a11y_delete">"Kustuta"</string>
<plurals name="a11y_digits_entered">
<item quantity="one">"%1$d number sisestatud"</item>
@@ -30,6 +32,7 @@
<string name="a11y_page_n">"%1$d. lehekülg"</string>
<string name="a11y_pause">"Peata"</string>
<string name="a11y_paused_voice_message">"Häälsõnum, kestus:%1$s, praegune asukoht: %2$s"</string>
<string name="a11y_photo_preview">"Foto eelvaade"</string>
<string name="a11y_pin_field">"PIN-koodi väli"</string>
<string name="a11y_pinned_location">"Esiletõstetud asukoht"</string>
<string name="a11y_play">"Esita"</string>
@@ -64,6 +67,7 @@
<string name="a11y_tombstoned_room">"Lõpetatuks märgitud jututuba"</string>
<string name="a11y_user_avatar">"Kasutaja tunnuspilt"</string>
<string name="a11y_user_menu">"Kasutajamenüü"</string>
<string name="a11y_video_preview">"Video eelvaade"</string>
<string name="a11y_view_avatar">"Vaata tunnuspilti"</string>
<string name="a11y_view_details">"Vaata üksikasju"</string>
<string name="a11y_voice_message">"Häälsõnum, kestus:%1$s"</string>
@@ -192,9 +196,11 @@
<string name="action_translate">"Tõlgi"</string>
<string name="action_try_again">"Proovi uuesti"</string>
<string name="action_unpin">"Eemalda esiletõstmine"</string>
<string name="action_video_call">"Videokõne"</string>
<string name="action_view">"Vaata"</string>
<string name="action_view_in_timeline">"Vaata ajajoonel"</string>
<string name="action_view_source">"Vaata lähtekoodi"</string>
<string name="action_voice_call">"Häälkõne"</string>
<string name="action_yes">"Jah"</string>
<string name="action_yes_try_again">"Jah, proovi uuesti"</string>
<string name="banner_migrate_to_native_sliding_sync_description">"Sinu koduserver toetab uut ja kiiremat protokolli. Uuendamiseks logi korraks rakendusest välja ja siis tagasi. Mingil hetkel tulevikus vana protokoll eemaldatakse kasutusest ja tehes uuenduse nüüd, väldid hilisemat sundkorras uuendust."</string>
@@ -259,6 +265,7 @@ Põhjus: %1$s."</string>
<string name="common_file_saved_on_disk_android">"Fail on salvestatud kausta Allalaadimised"</string>
<string name="common_forward_message">"Edasta sõnum"</string>
<string name="common_frequently_used">"Sagedasti kasutatud"</string>
<string name="common_gallery">"Galerii"</string>
<string name="common_gif">"GIF"</string>
<string name="common_group_call_in_progress">"Rühmakõne on pooleli"</string>
<string name="common_image">"Pilt"</string>
@@ -486,6 +493,7 @@ Kas sa oled kindel, et soovid jätkata?"</string>
<string name="screen_create_poll_options_section_title">"Valikud"</string>
<string name="screen_create_poll_remove_accessibility_label">"Kustuta: %1$s"</string>
<string name="screen_create_poll_settings_section_title">"Seadistused"</string>
<string name="screen_create_poll_votes_allowed_per_person">"Hääletuskordi kasutaja kohta"</string>
<string name="screen_live_location_sheet_nobody_sharing">"Mitte keegi ei jaga oma asukohta"</string>
<string name="screen_live_location_sheet_sharing_live_location">"Asukoht on jagamisel reaalajas"</string>
<plurals name="screen_live_location_sheet_subtitle">
@@ -145,7 +145,7 @@
<string name="action_show">"Tampilkan"</string>
<string name="action_sign_in_again">"Masuk lagi"</string>
<string name="action_signout">"Hapus device dari akun"</string>
<string name="action_signout_anyway">"Keluar saja"</string>
<string name="action_signout_anyway">"Hapus Perangkat ini"</string>
<string name="action_skip">"Lewati"</string>
<string name="action_start">"Mulai"</string>
<string name="action_start_chat">"Mulai obrolan"</string>
@@ -184,6 +184,7 @@
<string name="common_creating_room">"Membuat ruangan…"</string>
<string name="common_current_user_canceled_knock">"Permintaan dibatalkan"</string>
<string name="common_current_user_left_room">"Keluar dari ruangan"</string>
<string name="common_current_user_left_space">"Ruang tersisa"</string>
<string name="common_current_user_rejected_invite">"Undangan ditolak"</string>
<string name="common_decryption_error">"Kesalahan dekripsi"</string>
<string name="common_description">"Deskripsi"</string>
@@ -296,8 +297,9 @@ Alasan: %1$s."</string>
<string name="common_server_unreachable">"Server tidak dapat dijangkau"</string>
<string name="common_server_url">"URL Server"</string>
<string name="common_settings">"Pengaturan"</string>
<string name="common_share_space">"Berbagi ruang"</string>
<string name="common_shared_location">"Lokasi terbagi"</string>
<string name="common_signing_out">"Mengeluarkan dari akun"</string>
<string name="common_signing_out">"Melepaskan perangkat"</string>
<string name="common_something_went_wrong">"Ada yang salah"</string>
<string name="common_something_went_wrong_message">"Kami mengalami masalah. Silakan coba lagi."</string>
<string name="common_space">"Space"</string>
@@ -5,6 +5,8 @@
<string name="a11y_address">"Adres"</string>
<string name="a11y_avatar">"Awatar"</string>
<string name="a11y_collapse_message_text_field">"Zmniejsz pole tekstowe wiadomości"</string>
<string name="a11y_create_poll_votes_allowed_decrease">"Zmniejsz liczbę głosów dozwolonych na osobę"</string>
<string name="a11y_create_poll_votes_allowed_increase">"Zwiększ liczbę głosów dozwolonych na osobę"</string>
<string name="a11y_delete">"Usuń"</string>
<plurals name="a11y_digits_entered">
<item quantity="one">"Wprowadzono %1$d cyfrę"</item>
@@ -196,9 +198,11 @@
<string name="action_translate">"Przetłumacz"</string>
<string name="action_try_again">"Spróbuj ponownie"</string>
<string name="action_unpin">"Odepnij"</string>
<string name="action_video_call">"Rozmowa wideo"</string>
<string name="action_view">"Wyświetl"</string>
<string name="action_view_in_timeline">"Wyświetl na osi czasu"</string>
<string name="action_view_source">"Wyświetl źródło"</string>
<string name="action_voice_call">"Rozmowa głosowa"</string>
<string name="action_yes">"Tak"</string>
<string name="action_yes_try_again">"Tak, spróbuj ponownie"</string>
<string name="banner_migrate_to_native_sliding_sync_description">"Twój serwer wspiera teraz nowy, szybszy protokół. Zaloguj się ponownie, aby zaktualizować. Dzięki temu unikniesz wymuszonego wylogowania, gdy stary protokół zostanie usunięty."</string>
@@ -263,6 +267,7 @@ Powód: %1$s."</string>
<string name="common_file_saved_on_disk_android">"Plik zapisany do folderu Pobrane"</string>
<string name="common_forward_message">"Przekaż wiadomość"</string>
<string name="common_frequently_used">"Często używane"</string>
<string name="common_gallery">"Galeria"</string>
<string name="common_gif">"GIF"</string>
<string name="common_group_call_in_progress">"Trwa połączenie grupowe"</string>
<string name="common_image">"Zdjęcie"</string>
@@ -312,7 +317,7 @@ Powód: %1$s."</string>
<string name="common_pinned">"Przypięte"</string>
<string name="common_please_check_internet_connection">"Sprawdź swoje połączenie internetowe"</string>
<string name="common_please_wait">"Proszę czekać…"</string>
<string name="common_poll_end_confirmation">"Jesteś pewien, że chcesz zakończyć tę ankietę?"</string>
<string name="common_poll_end_confirmation">"Czy na pewno chcesz zakończyć tę ankietę?"</string>
<string name="common_poll_summary">"Ankieta: %1$s"</string>
<string name="common_poll_summary_prefix">"Ankieta"</string>
<string name="common_poll_total_votes">"Łączna liczba głosów: %1$s"</string>
@@ -497,6 +502,7 @@ Czy na pewno chcesz kontynuować?"</string>
<string name="screen_create_poll_options_section_title">"Opcje"</string>
<string name="screen_create_poll_remove_accessibility_label">"Usuń %1$s"</string>
<string name="screen_create_poll_settings_section_title">"Ustawienia"</string>
<string name="screen_create_poll_votes_allowed_per_person">"Liczba głosów na osobę"</string>
<string name="screen_live_location_sheet_nobody_sharing">"Nikt nie udostępnia swojej lokalizacji"</string>
<string name="screen_live_location_sheet_sharing_live_location">"Udostępnianie lokalizacji na żywo"</string>
<plurals name="screen_live_location_sheet_subtitle">
@@ -506,8 +512,8 @@ Czy na pewno chcesz kontynuować?"</string>
</plurals>
<string name="screen_live_location_sheet_title">"Na mapie"</string>
<string name="screen_media_picker_error_failed_selection">"Nie udało się wybrać multimediów. Spróbuj ponownie."</string>
<string name="screen_pinned_timeline_empty_state_description">"Naciśnij wiadomość i wybierz “%1$s”, aby dołączyć tutaj."</string>
<string name="screen_pinned_timeline_empty_state_headline">"Przypinaj ważne wiadomości, aby można było je łatwo znaleźć"</string>
<string name="screen_pinned_timeline_empty_state_description">"Przytrzymaj wiadomość i wybierz “%1$s”, aby umieścić ją tutaj."</string>
<string name="screen_pinned_timeline_empty_state_headline">"Przypinaj ważne wiadomości, by łatwo je później odnaleźć"</string>
<plurals name="screen_pinned_timeline_screen_title">
<item quantity="one">"%1$d przypięta wiadomość"</item>
<item quantity="few">"%1$d przypięte wiadomości"</item>
@@ -155,8 +155,8 @@
<string name="action_share_link">"Compartilhar link"</string>
<string name="action_show">"Mostrar"</string>
<string name="action_sign_in_again">"Entrar novamente"</string>
<string name="action_signout">"Sair"</string>
<string name="action_signout_anyway">"Sair mesmo assim"</string>
<string name="action_signout">"Remover este dispositivo"</string>
<string name="action_signout_anyway">"Remover este dispositivo mesmo assim"</string>
<string name="action_skip">"Pular"</string>
<string name="action_start">"Iniciar"</string>
<string name="action_start_chat">"Iniciar conversa"</string>
@@ -473,7 +473,7 @@ Você tem certeza de que deseja continuar?"</string>
<string name="screen_share_open_apple_maps">"Abrir no Apple Maps"</string>
<string name="screen_share_open_google_maps">"Abrir no Google Maps"</string>
<string name="screen_share_open_osm_maps">"Abrir no OpenStreetMap"</string>
<string name="screen_share_this_location_action">"Compartilhar esta localização"</string>
<string name="screen_share_this_location_action">"Compartilhar localização selecionada"</string>
<string name="screen_space_list_description">"Os espaços que você criou ou entrou."</string>
<string name="screen_space_list_details">"%1$s • %2$s"</string>
<string name="screen_space_list_empty_state_title">"Crie espaços para organizar salas"</string>
@@ -1,9 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="a11y_add_avatar">"Додати аватар"</string>
<string name="a11y_add_reaction">"Додати реакцію: %1$s"</string>
<string name="a11y_address">"Адреса"</string>
<string name="a11y_avatar">"Аватар"</string>
<string name="a11y_collapse_message_text_field">"Згорнути поле тексту повідомлення"</string>
<string name="a11y_create_poll_votes_allowed_decrease">"Зменшити кількість голосів, дозволених на одну особу"</string>
<string name="a11y_create_poll_votes_allowed_increase">"Збільшити кількість голосів, дозволених на одну особу"</string>
<string name="a11y_delete">"Видалити"</string>
<plurals name="a11y_digits_entered">
<item quantity="one">"Введена %1$d цифра"</item>
@@ -30,6 +33,7 @@
<string name="a11y_page_n">"Сторінка %1$d"</string>
<string name="a11y_pause">"Пауза"</string>
<string name="a11y_paused_voice_message">"Голосове повідомлення, тривалість: %1$s, поточна позиція: %2$s"</string>
<string name="a11y_photo_preview">"Попередній перегляд світлин"</string>
<string name="a11y_pin_field">"Поле PIN-коду"</string>
<string name="a11y_pinned_location">"Закріплена локація"</string>
<string name="a11y_play">"Відтворити"</string>
@@ -54,17 +58,18 @@
<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>
<string name="a11y_start_video_call">"Розпочати відеодзвінок"</string>
<string name="a11y_start_voice_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>
<string name="a11y_video_preview">"Попередній перегляд відео"</string>
<string name="a11y_view_avatar">"Переглянути аватар"</string>
<string name="a11y_view_details">"Переглянути подробиці"</string>
<string name="a11y_voice_message">"Голосове повідомлення, тривалість: %1$s"</string>
@@ -83,7 +88,7 @@
<string name="action_choose_photo">"Вибрати фото"</string>
<string name="action_clear">"Очистити"</string>
<string name="action_close">"Закрити"</string>
<string name="action_complete_verification">"Верифікація завершена"</string>
<string name="action_complete_verification">"Завершити звіряння"</string>
<string name="action_confirm">"Підтвердити"</string>
<string name="action_confirm_password">"Підтвердіть пароль"</string>
<string name="action_continue">"Продовжити"</string>
@@ -179,13 +184,13 @@
<string name="action_share_live_location">"Ділитися місцезнаходженням у реальному часі"</string>
<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">"Вилучити цей пристрій"</string>
<string name="action_signout_anyway">"Усе одно вилучити цей пристрій"</string>
<string name="action_skip">"Пропустити"</string>
<string name="action_start">"Розпочати"</string>
<string name="action_start_chat">"Розпочати бесіду"</string>
<string name="action_start_over">"Почати спочатку"</string>
<string name="action_start_verification">"Почати верифікацію"</string>
<string name="action_start_verification">"Розпочати звіряння"</string>
<string name="action_static_map_load">"Натисніть, щоб завантажити мапу"</string>
<string name="action_stop">"Зупинити"</string>
<string name="action_take_photo">"Зробити фото"</string>
@@ -193,9 +198,11 @@
<string name="action_translate">"Перекласти"</string>
<string name="action_try_again">"Спробуйте ще раз"</string>
<string name="action_unpin">"Відкріпити"</string>
<string name="action_video_call">"Відеовиклик"</string>
<string name="action_view">"Переглянути"</string>
<string name="action_view_in_timeline">"Переглянути на шкалі часу"</string>
<string name="action_view_source">"Переглянути джерело"</string>
<string name="action_voice_call">"Голосовий виклик"</string>
<string name="action_yes">"Так"</string>
<string name="action_yes_try_again">"Так, повторити спробу"</string>
<string name="banner_migrate_to_native_sliding_sync_description">"Ваш сервер тепер підтримує новий, швидший протокол. Вийдіть із системи та увійдіть знову, щоб оновити систему зараз. Якщо ви зробите це зараз, це допоможе вам уникнути примусового виходу з системи, коли старий протокол буде видалено пізніше."</string>
@@ -210,14 +217,15 @@
<string name="common_analytics">"Аналітика"</string>
<string name="common_android_shortcuts_remove_reason_left_room">"Ви вийшли з кімнати"</string>
<string name="common_android_shortcuts_remove_reason_session_logged_out">"Ви вийшли з сеансу"</string>
<string name="common_appearance">"Тема"</string>
<string name="common_appearance">"Вигляд"</string>
<string name="common_audio">"Аудіо"</string>
<string name="common_beta">"Бета-версія"</string>
<string name="common_blocked_users">"Заблоковані користувачі"</string>
<string name="common_bubbles">"Бульбашки"</string>
<string name="common_call_declined">"Дзвінок відхилено"</string>
<string name="common_call_declined">"Виклик відхилено"</string>
<string name="common_call_in_progress">"Виклик триває"</string>
<string name="common_call_started">"Виклик розпочато"</string>
<string name="common_call_you_declined">"Ви відхилили дзвінок"</string>
<string name="common_call_you_declined">"Ви відхилили виклик"</string>
<string name="common_chat_backup">"Резервне копіювання бесіди"</string>
<string name="common_copied_to_clipboard">"Скопійовано до буферу обміну"</string>
<string name="common_copyright">"Авторське право"</string>
@@ -259,7 +267,9 @@
<string name="common_file_saved_on_disk_android">"Файл збережений у Завантаження"</string>
<string name="common_forward_message">"Переслати повідомлення"</string>
<string name="common_frequently_used">"Частовживані"</string>
<string name="common_gallery">"Галерея"</string>
<string name="common_gif">"GIF"</string>
<string name="common_group_call_in_progress">"Триває груповий виклик"</string>
<string name="common_image">"Зображення"</string>
<string name="common_in_reply_to">"У відповідь на %1$s"</string>
<string name="common_install_apk_android">"Встановити APK"</string>
@@ -351,7 +361,7 @@
</plurals>
<string name="common_saved_changes">"Збережені зміни"</string>
<string name="common_saving">"Збереження"</string>
<string name="common_screen_lock">"Блокування екрану"</string>
<string name="common_screen_lock">"Блокування екрана"</string>
<string name="common_search_for_someone">"Шукати когось"</string>
<string name="common_search_results">"Результати пошуку"</string>
<string name="common_security">"Безпека"</string>
@@ -376,7 +386,7 @@
<string name="common_shared_live_location">"Поділитися місцезнаходженням в реальному часі"</string>
<string name="common_shared_location">"Поширене розташування"</string>
<string name="common_shared_space">"Простір спільного користування"</string>
<string name="common_signing_out">"Вихід"</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_space">"Простір"</string>
@@ -402,21 +412,22 @@
<string name="common_unable_to_decrypt">"Неможливо розшифрувати"</string>
<string name="common_unable_to_decrypt_insecure_device">"Надіслано з незахищеного пристрою"</string>
<string name="common_unable_to_decrypt_no_access">"Ви не маєте доступу до цього повідомлення"</string>
<string name="common_unable_to_decrypt_verification_violation">"Ідентичність відправника скинуто"</string>
<string name="common_unable_to_decrypt_verification_violation">"Звірена цифрова ідентичність відправника була скинута"</string>
<string name="common_unable_to_invite_message">"Не вдалося надіслати запрошення одному чи кільком користувачам."</string>
<string name="common_unable_to_invite_title">"Не вдалося надіслати запрошення"</string>
<string name="common_unlock">"Розблокувати"</string>
<string name="common_unmute">"Увімкнути звук"</string>
<string name="common_unsupported_call">"Непідтриманий виклик"</string>
<string name="common_unsupported_event">"Непідтримувана подія"</string>
<string name="common_user_started_a_call">"%1$s розпочинає виклик"</string>
<string name="common_username">"Ім\'я користувача"</string>
<string name="common_verification_cancelled">"Верифікацію скасовано"</string>
<string name="common_verification_complete">"Верифікацію завершено"</string>
<string name="common_verification_failed">"Перевірка не вдалася"</string>
<string name="common_verified">"Перевірено"</string>
<string name="common_verify_device">"Верифікувати пристрій"</string>
<string name="common_verify_identity">"Підтвердити особу"</string>
<string name="common_verify_user">"Верифікувати користувача"</string>
<string name="common_verification_cancelled">"Звіряння скасовано"</string>
<string name="common_verification_complete">"Звіряння завершено"</string>
<string name="common_verification_failed">"Не вдалося звірити"</string>
<string name="common_verified">"Звірено"</string>
<string name="common_verify_device">"Звірити пристрій"</string>
<string name="common_verify_identity">"Підтвердити ідентичність"</string>
<string name="common_verify_user">"Звірити користувача"</string>
<string name="common_video">"Відео"</string>
<string name="common_video_quality_high">"Висока якість"</string>
<string name="common_video_quality_high_description">"Найкраща якість, але більший розмір файлу"</string>
@@ -433,11 +444,11 @@
<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>
<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>
<string name="crypto_identity_change_pin_violation_new_user_id">"(%1$s)"</string>
<string name="crypto_identity_change_profile_pin_violation">"Ідентичність %1$s скинуто."</string>
<string name="crypto_identity_change_verification_violation_new">"Ідентичність %1$s %2$s скинуто. %3$s"</string>
<string name="crypto_identity_change_profile_pin_violation">"Цифрову ідентичність %1$s скинуто."</string>
<string name="crypto_identity_change_verification_violation_new">"Цифрову ідентичність %1$s %2$s скинуто. %3$s"</string>
<string name="crypto_identity_change_withdraw_verification_action">"Відкликати верифікацію"</string>
<string name="dialog_allow_access">"Надати доступ"</string>
<string name="dialog_confirm_link_message">"Посилання %1$s спрямовує вас на інший сайт %2$s
@@ -485,12 +496,13 @@
<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="preference_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_create_poll_votes_allowed_per_person">"Кількість голосів, дозволених на одну особу"</string>
<string name="screen_live_location_sheet_nobody_sharing">"Ніхто не ділиться своєю геопозицією"</string>
<string name="screen_live_location_sheet_sharing_live_location">"Обмін геопозицією"</string>
<plurals name="screen_live_location_sheet_subtitle">
@@ -508,11 +520,11 @@
<item quantity="many">"%1$d Закріплених повідомлення"</item>
</plurals>
<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_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_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_title">"Ваше повідомлення не було надіслано, тому що %1$s не перевірив усі пристрої"</string>
@@ -545,15 +557,22 @@
<string name="screen_space_list_title">"Простори"</string>
<string name="screen_static_location_sheet_timestamp_description">"Надано доступ %1$s"</string>
<string name="screen_static_location_sheet_title">"На карті"</string>
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Повідомлення не надіслано, оскільки підтверджену особистість %1$s скинуто."</string>
<plurals name="screen_timeline_active_call_extra_joined_count">
<item quantity="one">"+ %1$d приєднується"</item>
<item quantity="few">"+ %1$d приєднуються"</item>
<item quantity="many">"+ %1$d приєднуються"</item>
</plurals>
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Повідомлення не надіслано, оскільки звірена цифрова ідентичність %1$s була скинута."</string>
<string name="screen_timeline_item_menu_send_failure_unsigned_device">"Повідомлення не надіслано, оскільки %1$s перевірив не всі пристрої."</string>
<string name="screen_timeline_item_menu_send_failure_you_unsigned_device">"Повідомлення не надіслано, оскільки ви не підтвердили один або кілька своїх пристроїв."</string>
<string name="screen_timeline_item_menu_send_failure_you_unsigned_device">"Повідомлення не надіслано, оскільки ви не звірили один або кілька своїх пристроїв."</string>
<string name="screen_view_location_title">"Розташування"</string>
<string name="settings_version_number">"Версія: %1$s (%2$s)"</string>
<string name="test_language_identifier">"uk"</string>
<string name="timeline_decryption_failure_historical_event_no_key_backup">"Історичні повідомлення недоступні на цьому пристрої"</string>
<string name="timeline_decryption_failure_historical_event_unverified_device">"Щоб отримати доступ до історичних повідомлень, потрібно верифікувати цей пристрій"</string>
<string name="timeline_decryption_failure_historical_event_unverified_device">"Щоб отримати доступ до історії повідомлень, потрібно звірити цей пристрій"</string>
<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="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>
@@ -5,6 +5,8 @@
<string name="a11y_address">"地址"</string>
<string name="a11y_avatar">"头像"</string>
<string name="a11y_collapse_message_text_field">"最小化消息文本框"</string>
<string name="a11y_create_poll_votes_allowed_decrease">"减少每人允许的投票数"</string>
<string name="a11y_create_poll_votes_allowed_increase">"增加每人允许的投票数"</string>
<string name="a11y_delete">"删除"</string>
<plurals name="a11y_digits_entered">
<item quantity="other">"已输入 %1$d 个数字"</item>
@@ -191,9 +193,11 @@
<string name="action_translate">"翻译"</string>
<string name="action_try_again">"重试"</string>
<string name="action_unpin">"取消置顶"</string>
<string name="action_video_call">"视频通话"</string>
<string name="action_view">"查看"</string>
<string name="action_view_in_timeline">"在时间线上查看"</string>
<string name="action_view_source">"查看源码"</string>
<string name="action_voice_call">"语音通话"</string>
<string name="action_yes">"是"</string>
<string name="action_yes_try_again">"是,重试"</string>
<string name="banner_migrate_to_native_sliding_sync_description">"你的服务器现在支持更快的新协议。现在注销并重新登录以升级。立即这样做可以避免你在以后删除旧协议时被强制注销。"</string>
@@ -258,6 +262,7 @@
<string name="common_file_saved_on_disk_android">"文件已保存到“下载”"</string>
<string name="common_forward_message">"转发消息"</string>
<string name="common_frequently_used">"常用"</string>
<string name="common_gallery">"图库"</string>
<string name="common_gif">"GIF"</string>
<string name="common_group_call_in_progress">"群组通话进行中"</string>
<string name="common_image">"图片"</string>
@@ -395,7 +400,7 @@
<string name="common_unmute">"解除静音"</string>
<string name="common_unsupported_call">"不受支持的通话"</string>
<string name="common_unsupported_event">"不支持的事件"</string>
<string name="common_user_started_a_call">"%1$s 开始通话"</string>
<string name="common_user_started_a_call">"%1$s 开始通话"</string>
<string name="common_username">"用户名"</string>
<string name="common_verification_cancelled">"验证已取消"</string>
<string name="common_verification_complete">"验证完成"</string>
@@ -478,6 +483,7 @@
<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_create_poll_votes_allowed_per_person">"每人允许的投票数"</string>
<string name="screen_live_location_sheet_nobody_sharing">"目前无人分享其位置"</string>
<string name="screen_live_location_sheet_sharing_live_location">"共享实时位置"</string>
<plurals name="screen_live_location_sheet_subtitle">
@@ -5,6 +5,8 @@
<string name="a11y_address">"Address"</string>
<string name="a11y_avatar">"Avatar"</string>
<string name="a11y_collapse_message_text_field">"Minimise message text field"</string>
<string name="a11y_create_poll_votes_allowed_decrease">"Decrease votes allowed per person"</string>
<string name="a11y_create_poll_votes_allowed_increase">"Increase votes allowed per person"</string>
<string name="a11y_delete">"Delete"</string>
<plurals name="a11y_digits_entered">
<item quantity="one">"%1$d digit entered"</item>
@@ -194,9 +196,11 @@
<string name="action_translate">"Translate"</string>
<string name="action_try_again">"Try again"</string>
<string name="action_unpin">"Unpin"</string>
<string name="action_video_call">"Video call"</string>
<string name="action_view">"View"</string>
<string name="action_view_in_timeline">"View in timeline"</string>
<string name="action_view_source">"View source"</string>
<string name="action_voice_call">"Voice call"</string>
<string name="action_yes">"Yes"</string>
<string name="action_yes_try_again">"Yes, try again"</string>
<string name="banner_migrate_to_native_sliding_sync_description">"Your server now supports a new, faster protocol. Log out and log back in to upgrade now. Doing this now will help you avoid a forced logout when the old protocol is removed later."</string>
@@ -261,6 +265,7 @@ Reason: %1$s."</string>
<string name="common_file_saved_on_disk_android">"File saved to Downloads"</string>
<string name="common_forward_message">"Forward message"</string>
<string name="common_frequently_used">"Frequently used"</string>
<string name="common_gallery">"Gallery"</string>
<string name="common_gif">"GIF"</string>
<string name="common_group_call_in_progress">"Group call in progress"</string>
<string name="common_image">"Image"</string>
@@ -488,6 +493,7 @@ Are you sure you want to continue?"</string>
<string name="screen_create_poll_options_section_title">"Options"</string>
<string name="screen_create_poll_remove_accessibility_label">"Remove %1$s"</string>
<string name="screen_create_poll_settings_section_title">"Settings"</string>
<string name="screen_create_poll_votes_allowed_per_person">"Votes allowed per person"</string>
<string name="screen_live_location_sheet_nobody_sharing">"Nobody is sharing their location"</string>
<string name="screen_live_location_sheet_sharing_live_location">"Sharing live location"</string>
<plurals name="screen_live_location_sheet_subtitle">
+1
View File
@@ -19,6 +19,7 @@ android {
dependencies {
implementation(projects.libraries.androidutils)
implementation(projects.services.toolbox.impl)
implementation(libs.androidx.compose.ui.text)
testCommonDependencies(libs)
}
@@ -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.ui.utils.text
import androidx.compose.ui.text.style.TextDirection
/**
* Detect if a text is RTL or not, based on its content.
*/
fun TextDirection.Companion.detect(text: String): TextDirection {
return if (text.any(::isRtlChar)) TextDirection.Rtl else TextDirection.Ltr
}
/**
* Detect if a character is an RTL character or not, based on its Unicode directionality.
*/
fun isRtlChar(char: Char): Boolean = when (Character.getDirectionality(char)) {
Character.DIRECTIONALITY_RIGHT_TO_LEFT,
Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC,
Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING,
Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE -> true
else -> false
}
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ad7189cbb855280fd2e12a5dc43d0062918f76ddcc4b5cc7677748cf08a4afa9
size 34478
oid sha256:25753de5e78f127478e94ca29354a0ef057a20b795b302a911748b724b206c17
size 33753
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:17acafce434f31f50b3232c2bebcd3bb828c70031cb892d7f5a78704f5d58661
size 36455
oid sha256:4cac82fe268df0631f766e69030e1dc1bdb09d869db18e62d15c563e9ff81260
size 36237
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f063ef08eaf607de6d7d7361046ac96f8932d232b1f5cc48125cc2ac04d30531
size 45836
oid sha256:88eab769270bf7492cfbafbd537392348b76780bc93c2b4b858f1fecbc8e8af2
size 45264
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5f30f5d0f8e410f3addc64cd5fa7fbed8505bcd562a75de06cbf3a1e7a40ceb0
size 46745
oid sha256:d94c8697c49e36f4275860e6a1825815738f1c8263ae7e129cd4139b2544b3e7
size 46286
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3ce82d48e8b0b0c78a978277886a9916775cc12fc28edeb2807f63afb879c6a5
size 48236
oid sha256:919e66822097fe71920c70c4923bcb61b332fb0766dd0bfd4e662081854151c3
size 47732
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f063ef08eaf607de6d7d7361046ac96f8932d232b1f5cc48125cc2ac04d30531
size 45836
oid sha256:88eab769270bf7492cfbafbd537392348b76780bc93c2b4b858f1fecbc8e8af2
size 45264
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6c844dd51824db5a123a2b7e564a001b3609245259ec25c601a181eb69362098
size 46863
oid sha256:ce9fbcbf082cf07edbb271870818e74782cbc5ed6c71624e1297b9af10a3c130
size 46273
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9b6138083429a82d1de576009df367a52361b39e5ac094d0c4abc0bb47e89550
size 41888
oid sha256:42d004e1086f2224bb9ab8b41568d4e7f60a9f4c5b7a3e75394ef0f623f8bebf
size 41247
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e3b6ee300dc038fbea819d19286d10bdf023872abda1bbe6d81b4fef97a2d934
size 41341
oid sha256:df105a3030abbf1760d5c1ee580664349f745d08023ad0f5da784ff7a6a3a1db
size 40619
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d550d37eb3a14779dd33171843339729cf3457f9a2a6e9d41d89b6c9f52f2f0f
size 35489
oid sha256:cc1d720d39e352d815fe20759dae937f87a50e89cf9de4aa998d20e2408417dc
size 34846
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:68ab96769960de6cc00a06b2ca59cbf4ad20e9788c9fcf09b4ddb126104359f7
size 37773
oid sha256:ff909aab1190f8d9eff5b3c100df6d2a5cad0513955a1ab09351b2ac6fd9a021
size 37618
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e8583c1a707b3784dc2daf279628d86d813046743f77568aa1acb0361ce7cb4e
size 47505
oid sha256:7d10e5c062dee14e0775ac72884ebbed0fe41aae4d1bb9a0892d8fa456d8c296
size 47067
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9016458c07e6c5e8fd962fe1ef08d0da82572c867304fb3ff4f694f83c40f36c
size 48497
oid sha256:44fd0dd695a9e059ab54b2e6c628a6ac24625ec7c9c92c651c6626a228bd541c
size 48101
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0ba0af2f22cd932c626df190f2139f11906934b4d5de16cc1fde9d35488552ab
size 50028
oid sha256:fe8b585c1bca2036160cac128233b0899c091bbc30fd087a72e4b1191ee5eef9
size 49666
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e8583c1a707b3784dc2daf279628d86d813046743f77568aa1acb0361ce7cb4e
size 47505
oid sha256:7d10e5c062dee14e0775ac72884ebbed0fe41aae4d1bb9a0892d8fa456d8c296
size 47067
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ab9a5bc72358625c7623b3156365c299eaec576e96d11ecc3946e92511815509
size 48578
oid sha256:0d56402d64dc752d4cccc3a1e0642a69ca244225cce346aa842fdf3cc492f8d1
size 48121
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:981e3075d5285a630932c02427cca3d46efad74a640f12d35da8e0f29fb4b779
size 43474
oid sha256:55e147811154db2ce5de206693057fedf11b38ce6fcf23ef7ef4ac9ee00d4f7d
size 42888
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d35a6a56027b453b845400ec2b5b5912eb5eff551b2c370a26110b584ed3d3a3
size 42939
oid sha256:807efc10c7d97dcfcbbaa2c696996c9ffe3c69f3f21d992fee0635f5c1dd623b
size 42289
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a2f2b452d5f951c917841eacea002106c353f1236a63450498a7cb3cab948ab1
size 43627
oid sha256:ab2ea24bf2c91529674d2d3d582137ec195c6d28f10d65d46c1851716c1d1408
size 43842
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c6708f52e140fb8a5e2697a221182d36048671063ae3fdca96a49c131a129cc0
size 63960
oid sha256:5eee6d3b3040f3144e4785aa1b14fdf2057ef9a43a758317f4d59a47c8fbc2fb
size 63817

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