Merge branch 'develop' into feature/fga/live_location_sharing_setup
This commit is contained in:
commit
9a984e1423
632 changed files with 4530 additions and 3107 deletions
|
|
@ -9,12 +9,15 @@
|
|||
package io.element.android.libraries.androidutils.ui
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.ResultReceiver
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import android.view.WindowInsets
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.core.content.getSystemService
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
fun View.hideKeyboard() {
|
||||
|
|
@ -22,6 +25,32 @@ fun View.hideKeyboard() {
|
|||
imm?.hideSoftInputFromWindow(windowToken, 0)
|
||||
}
|
||||
|
||||
suspend fun View.hideKeyboardAndAwaitAnimation() {
|
||||
val imm = context?.getSystemService<InputMethodManager>()
|
||||
|
||||
val mutex = Mutex()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
setOnApplyWindowInsetsListener { view, insets ->
|
||||
if (!insets.isVisible(WindowInsets.Type.ime())) {
|
||||
mutex.unlock()
|
||||
}
|
||||
insets
|
||||
}
|
||||
imm?.hideSoftInputFromWindow(windowToken, 0)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
imm?.hideSoftInputFromWindow(windowToken, 0, object : ResultReceiver(null) {
|
||||
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
|
||||
if (resultCode == InputMethodManager.RESULT_UNCHANGED_HIDDEN ||
|
||||
resultCode == InputMethodManager.RESULT_HIDDEN) {
|
||||
mutex.unlock()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
mutex.lock()
|
||||
}
|
||||
|
||||
fun View.showKeyboard(andRequestFocus: Boolean = false) {
|
||||
if (andRequestFocus) {
|
||||
requestFocus()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="error_no_compatible_app_found">"Не найдено совместимое приложение для обработки этого действия."</string>
|
||||
<string name="error_no_compatible_app_found">"Не найдено приложение для выполнения этого действия."</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.bumble.appyx.core.children.ChildEntry
|
||||
import com.bumble.appyx.core.composable.Children
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
import com.bumble.appyx.core.navigation.model.combined.plus
|
||||
import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
|
||||
|
|
@ -27,6 +26,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
|||
import com.bumble.appyx.navmodel.backstack.BackStack
|
||||
import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackFader
|
||||
import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackSlider
|
||||
import io.element.android.libraries.architecture.appyx.SafeChildren
|
||||
import io.element.android.libraries.architecture.overlay.Overlay
|
||||
|
||||
/**
|
||||
|
|
@ -66,9 +66,9 @@ inline fun <reified NavTarget : Any> BaseFlowNode<NavTarget>.BackstackView(
|
|||
transitionSpec = { spring(stiffness = Spring.StiffnessMediumLow) },
|
||||
),
|
||||
) {
|
||||
Children(
|
||||
modifier = modifier,
|
||||
SafeChildren(
|
||||
navModel = backstack,
|
||||
modifier = modifier,
|
||||
transitionHandler = transitionHandler,
|
||||
)
|
||||
}
|
||||
|
|
@ -78,9 +78,9 @@ inline fun <reified NavTarget : Any> BaseFlowNode<NavTarget>.OverlayView(
|
|||
modifier: Modifier = Modifier,
|
||||
transitionHandler: TransitionHandler<NavTarget, BackStack.State> = rememberBackstackFader(),
|
||||
) {
|
||||
Children(
|
||||
modifier = modifier,
|
||||
SafeChildren(
|
||||
navModel = overlay,
|
||||
modifier = modifier,
|
||||
transitionHandler = transitionHandler,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* 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.architecture.appyx
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||
import androidx.compose.animation.SharedTransitionLayout
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import com.bumble.appyx.core.composable.Child
|
||||
import com.bumble.appyx.core.composable.ChildRenderer
|
||||
import com.bumble.appyx.core.composable.ChildTransitionScope
|
||||
import com.bumble.appyx.core.navigation.NavKey
|
||||
import com.bumble.appyx.core.navigation.NavModel
|
||||
import com.bumble.appyx.core.navigation.transition.JumpToEndTransitionHandler
|
||||
import com.bumble.appyx.core.navigation.transition.TransitionBounds
|
||||
import com.bumble.appyx.core.navigation.transition.TransitionDescriptor
|
||||
import com.bumble.appyx.core.navigation.transition.TransitionHandler
|
||||
import com.bumble.appyx.core.navigation.transition.TransitionParams
|
||||
import com.bumble.appyx.core.node.LocalMovableContentMap
|
||||
import com.bumble.appyx.core.node.LocalNodeTargetVisibility
|
||||
import com.bumble.appyx.core.node.LocalSharedElementScope
|
||||
import com.bumble.appyx.core.node.ParentNode
|
||||
import io.element.android.libraries.core.coroutine.withPreviousValue
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.map
|
||||
import timber.log.Timber
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// All the components in this file come from Appyx, and they've been modified to fix an issue with
|
||||
// the saved state. The parts that are modified are marked.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Immutable
|
||||
class SafeChildrenTransitionScope<T : Any, S>(
|
||||
val transitionHandler: TransitionHandler<T, S>,
|
||||
val transitionParams: TransitionParams,
|
||||
val navModel: NavModel<T, S>
|
||||
) {
|
||||
|
||||
@Composable
|
||||
inline fun <reified V : T> ParentNode<T>.children(
|
||||
noinline block: @Composable ChildTransitionScope<S>.(
|
||||
child: ChildRenderer,
|
||||
transitionDescriptor: TransitionDescriptor<T, S>
|
||||
) -> Unit,
|
||||
) {
|
||||
safeChildren(V::class, block)
|
||||
}
|
||||
|
||||
@Composable
|
||||
inline fun <reified V : T> ParentNode<T>.children(
|
||||
noinline block: @Composable ChildTransitionScope<S>.(child: ChildRenderer) -> Unit,
|
||||
) {
|
||||
safeChildren(V::class, block)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@SuppressLint("ComposableNaming")
|
||||
fun ParentNode<T>.safeChildren(
|
||||
clazz: KClass<out T>,
|
||||
block: @Composable ChildTransitionScope<S>.(ChildRenderer) -> Unit,
|
||||
) {
|
||||
_safeChildren(clazz) { scope, child, _ ->
|
||||
scope.block(child)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@SuppressLint("ComposableNaming")
|
||||
fun ParentNode<T>.safeChildren(
|
||||
clazz: KClass<out T>,
|
||||
block: @Composable ChildTransitionScope<S>.(
|
||||
ChildRenderer,
|
||||
TransitionDescriptor<T, S>
|
||||
) -> Unit,
|
||||
) {
|
||||
_safeChildren(clazz) { scope, child, descriptor ->
|
||||
scope.block(
|
||||
child,
|
||||
descriptor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ComposableNaming")
|
||||
@Composable
|
||||
private fun ParentNode<T>._safeChildren(
|
||||
clazz: KClass<out T>,
|
||||
block: @Composable (
|
||||
transitionScope: ChildTransitionScope<S>,
|
||||
child: ChildRenderer,
|
||||
transitionDescriptor: TransitionDescriptor<T, S>
|
||||
) -> Unit
|
||||
) {
|
||||
val saveableStateHolder = rememberSaveableStateHolder()
|
||||
|
||||
val disposedNavKeys = remember { mutableSetOf<NavKey<T>>() }
|
||||
|
||||
LaunchedEffect(navModel) {
|
||||
navModel
|
||||
.removedElementKeys()
|
||||
.map { list ->
|
||||
list.filter { clazz.isInstance(it.navTarget) }
|
||||
}
|
||||
////////// MODIFIED ////////////
|
||||
.filter { it.isNotEmpty() }
|
||||
.collect { deletedKeys ->
|
||||
deletedKeys.forEach { navKey ->
|
||||
// Wait for the NavKey to be disposed before removing its key from saveableStateHolder:
|
||||
// Otherwise, the child SaveableStateRegistry will be removed but not the `SavedState`, which will accumulate
|
||||
// and may cause TransactionTooLargeExceptions
|
||||
while (!disposedNavKeys.contains(navKey)) {
|
||||
delay(10)
|
||||
}
|
||||
disposedNavKeys.remove(navKey)
|
||||
Timber.v("Removed NavKey ${navKey} from saveableStateHolder. NavTarget: ${navKey.navTarget}")
|
||||
saveableStateHolder.removeState(navKey)
|
||||
}
|
||||
}
|
||||
////////// END OF MODIFIED ////////////
|
||||
}
|
||||
|
||||
val screenStateFlow = remember {
|
||||
this@SafeChildrenTransitionScope
|
||||
.navModel
|
||||
.screenState
|
||||
}
|
||||
|
||||
val children by screenStateFlow.collectAsState()
|
||||
|
||||
children
|
||||
.onScreen
|
||||
.filter { clazz.isInstance(it.key.navTarget) }
|
||||
.forEach { navElement ->
|
||||
key(navElement.key.id) {
|
||||
CompositionLocalProvider(
|
||||
LocalNodeTargetVisibility provides
|
||||
children.onScreenWithVisibleTargetState.contains(navElement)
|
||||
) {
|
||||
Child(
|
||||
navElement,
|
||||
saveableStateHolder,
|
||||
transitionParams,
|
||||
transitionHandler,
|
||||
block
|
||||
)
|
||||
|
||||
////////// MODIFIED ////////////
|
||||
DisposableEffect(navElement.key) {
|
||||
onDispose {
|
||||
Timber.v("Disposed NavKey ${navElement.key}. NavTarget: ${navElement.key.navTarget}")
|
||||
disposedNavKeys.add(navElement.key)
|
||||
}
|
||||
}
|
||||
////////// END OF MODIFIED ////////////
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||
@Composable
|
||||
inline fun <reified NavTarget : Any, State> ParentNode<NavTarget>.SafeChildren(
|
||||
navModel: NavModel<NavTarget, State>,
|
||||
modifier: Modifier = Modifier,
|
||||
transitionHandler: TransitionHandler<NavTarget, State> = remember { JumpToEndTransitionHandler() },
|
||||
withSharedElementTransition: Boolean = false,
|
||||
withMovableContent: Boolean = false,
|
||||
noinline block: @Composable SafeChildrenTransitionScope<NavTarget, State>.() -> Unit = {
|
||||
children<NavTarget> { child ->
|
||||
child()
|
||||
}
|
||||
}
|
||||
) {
|
||||
val density = LocalDensity.current.density
|
||||
var transitionBounds by remember { mutableStateOf(IntSize(0, 0)) }
|
||||
val transitionParams by remember(transitionBounds) {
|
||||
derivedStateOf {
|
||||
TransitionParams(
|
||||
bounds = TransitionBounds(
|
||||
width = Dp(transitionBounds.width / density),
|
||||
height = Dp(transitionBounds.height / density)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (withSharedElementTransition) {
|
||||
SharedTransitionLayout(modifier = modifier
|
||||
.onSizeChanged {
|
||||
transitionBounds = it
|
||||
}
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
/** LocalSharedElementScope will be consumed by children UI to apply shareElement modifier */
|
||||
LocalSharedElementScope provides this,
|
||||
LocalMovableContentMap provides if (withMovableContent) mutableMapOf() else null
|
||||
) {
|
||||
block(
|
||||
SafeChildrenTransitionScope(
|
||||
transitionHandler = transitionHandler,
|
||||
transitionParams = transitionParams,
|
||||
navModel = navModel
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Box(modifier = modifier
|
||||
.onSizeChanged {
|
||||
transitionBounds = it
|
||||
}
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
/** If sharedElement is not supported for this Node - provide null otherwise children
|
||||
* can consume ascendant's LocalSharedElementScope */
|
||||
LocalSharedElementScope provides null,
|
||||
LocalMovableContentMap provides if (withMovableContent) mutableMapOf() else null
|
||||
) {
|
||||
block(
|
||||
SafeChildrenTransitionScope(
|
||||
transitionHandler = transitionHandler,
|
||||
transitionParams = transitionParams,
|
||||
navModel = navModel
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T: Any, S> NavModel<T, S>.removedElementKeys(): Flow<List<NavKey<T>>> {
|
||||
return this.elements.withPreviousValue()
|
||||
.map { (previous, current) ->
|
||||
val previousKeys = previous?.map { it.key }.orEmpty()
|
||||
val currentKeys = current.map { it.key }
|
||||
previousKeys.filter { element ->
|
||||
!currentKeys.contains(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,20 +6,7 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
plugins {
|
||||
id("java-library")
|
||||
id("com.android.lint")
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = Versions.javaVersion
|
||||
targetCompatibility = Versions.javaVersion
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
languageVersion = Versions.javaLanguageVersion
|
||||
}
|
||||
id("io.element.jvm-library")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
id("com.android.lint")
|
||||
id("io.element.jvm-library")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
<string name="state_event_room_avatar_changed_by_you">"Вие променихте снимката на стаята"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s премахна снимката на стаята"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Вие премахнахте снимката на стаята"</string>
|
||||
<string name="state_event_room_ban_by_you">"Вие забранихте %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s създаде стаята"</string>
|
||||
<string name="state_event_room_created_by_you">"Вие създадохте стаята"</string>
|
||||
<string name="state_event_room_invite">"%1$s покани %2$s"</string>
|
||||
|
|
@ -58,5 +59,6 @@
|
|||
<string name="state_event_room_topic_removed">"%1$s премахна темата на стаята"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Вие премахнахте темата на стаята"</string>
|
||||
<string name="state_event_room_unban">"%1$s премахна забраната на %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"Вие отблокирахте %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s направи неизвестна промяна в членството си"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,73 +1,73 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="state_event_avatar_changed_too">"(изображение тоже было изменено)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s сменил своё изображение"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Вы сменили изображение профиля"</string>
|
||||
<string name="state_event_demoted_to_member">"%1$s был понижен до участника"</string>
|
||||
<string name="state_event_demoted_to_moderator">"%1$s был понижен до модератора"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s изменил свое отображаемое имя с %2$s на %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Вы изменили свое отображаемое имя с %1$s на %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s удалил свое отображаемое имя (оно было %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Вы удалили свое отображаемое имя (оно было %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s установили свое отображаемое имя на %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Вы установили отображаемое имя на %1$s"</string>
|
||||
<string name="state_event_promoted_to_administrator">"%1$s был повышен до уровня администратора"</string>
|
||||
<string name="state_event_promoted_to_moderator">"%1$s был повышен до модератора"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s изменил изображение комнаты"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Вы изменили изображение комнаты"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s удалил изображение комнаты"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Вы удалили изображение комнаты"</string>
|
||||
<string name="state_event_room_ban">"%1$s заблокировал %2$s"</string>
|
||||
<string name="state_event_avatar_changed_too">"(аватар тоже был изменен)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s сменил(а) аватар"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Вы сменили аватар"</string>
|
||||
<string name="state_event_demoted_to_member">"%1$s был(а) понижен до участника"</string>
|
||||
<string name="state_event_demoted_to_moderator">"%1$s был(а) понижен до модератора"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s изменил(а) имя с %2$s на %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Вы изменили имя с %1$s на %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s (ранее %2$s) удалил(а) имя"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Вы удалили свое имя (ранее %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s изменил(а) имя на %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Вы изменили имя на %1$s"</string>
|
||||
<string name="state_event_promoted_to_administrator">"%1$s повышен(а) до администратора"</string>
|
||||
<string name="state_event_promoted_to_moderator">"%1$s повышен(а) до модератора"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s изменил(а) аватар комнаты"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Вы изменили аватар комнаты"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s удалил(а) аватар комнаты"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Вы удалили аватар комнаты"</string>
|
||||
<string name="state_event_room_ban">"%1$s заблокировал(а) %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"Вы заблокировали %1$s"</string>
|
||||
<string name="state_event_room_ban_by_you_with_reason">"Вы заблокировали %1$s: %2$s"</string>
|
||||
<string name="state_event_room_ban_with_reason">"%1$s заблокирован %2$s: %3$s"</string>
|
||||
<string name="state_event_room_created">"%1$s создал комнату"</string>
|
||||
<string name="state_event_room_ban_with_reason">"%1$s заблокировал(а) %2$s: %3$s"</string>
|
||||
<string name="state_event_room_created">"%1$s создал(а) комнату"</string>
|
||||
<string name="state_event_room_created_by_you">"Вы создали комнату"</string>
|
||||
<string name="state_event_room_invite">"%1$s пригласил %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s принял приглашение"</string>
|
||||
<string name="state_event_room_invite">"%1$s пригласил(а) %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s принял(а) приглашение"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Вы приняли приглашение"</string>
|
||||
<string name="state_event_room_invite_by_you">"Вы пригласили %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"Пользователь %1$s пригласил вас"</string>
|
||||
<string name="state_event_room_join">"%1$s присоединился к комнате"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s пригласил(а) вас"</string>
|
||||
<string name="state_event_room_join">"%1$s присоединился(ась)"</string>
|
||||
<string name="state_event_room_join_by_you">"Вы присоединились к комнате"</string>
|
||||
<string name="state_event_room_knock">"%1$s хочет присоединиться"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s разрешил %2$s присоединиться"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s разрешил(а) %2$s присоединиться"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"Вы разрешили %1$s присоединиться"</string>
|
||||
<string name="state_event_room_knock_by_you">"Вы запросили присоединение"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s отклонил запрос %2$s на присоединение"</string>
|
||||
<string name="state_event_room_knock_by_you">"Вы отправили запрос на присоединение"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s отклонил(а) запрос %2$s на присоединение"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"Вы отклонили запрос %1$s на присоединение"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s отклонил ваш запрос на присоединение"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s больше не заинтересован в присоединении"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Вы отменили запрос на присоединение"</string>
|
||||
<string name="state_event_room_leave">"%1$s покинул комнату"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s отклонил(а) ваш запрос на присоединение"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s отозвал(а) запрос на присоединение"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Вы отозвали запрос на присоединение"</string>
|
||||
<string name="state_event_room_leave">"%1$s покинул(а) комнату"</string>
|
||||
<string name="state_event_room_leave_by_you">"Вы покинули комнату"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s изменил название комнаты на: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Вы изменили название комнаты на: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s удалил название комнаты"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Вы удалили название комнаты"</string>
|
||||
<string name="state_event_room_none">"%1$s ничего не изменил"</string>
|
||||
<string name="state_event_room_none_by_you">"Вы не внесли никаких изменений"</string>
|
||||
<string name="state_event_room_pinned_events_changed">"%1$s изменил закрепленные сообщения"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s изменил(а) название на %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Вы изменили название на %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s удалил(а) название"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Вы удалили название"</string>
|
||||
<string name="state_event_room_none">"%1$s не внес(ла) изменений"</string>
|
||||
<string name="state_event_room_none_by_you">"Вы не внесли изменений"</string>
|
||||
<string name="state_event_room_pinned_events_changed">"%1$s изменил(а) закрепленные сообщения"</string>
|
||||
<string name="state_event_room_pinned_events_changed_by_you">"Вы изменили закрепленные сообщения"</string>
|
||||
<string name="state_event_room_pinned_events_pinned">"%1$s закрепил сообщение"</string>
|
||||
<string name="state_event_room_pinned_events_pinned">"%1$s закрепил(а) сообщение"</string>
|
||||
<string name="state_event_room_pinned_events_pinned_by_you">"Вы закрепили сообщение"</string>
|
||||
<string name="state_event_room_pinned_events_unpinned">"%1$s открепил сообщение"</string>
|
||||
<string name="state_event_room_pinned_events_unpinned">"%1$s открепил(а) сообщение"</string>
|
||||
<string name="state_event_room_pinned_events_unpinned_by_you">"Вы открепили сообщение"</string>
|
||||
<string name="state_event_room_reject">"%1$s отклонил приглашение"</string>
|
||||
<string name="state_event_room_reject">"%1$s отклонил(а) приглашение"</string>
|
||||
<string name="state_event_room_reject_by_you">"Вы отклонили приглашение"</string>
|
||||
<string name="state_event_room_remove">"%1$s удалил %2$s"</string>
|
||||
<string name="state_event_room_remove">"%1$s удалил(а) %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"Вы удалили %1$s"</string>
|
||||
<string name="state_event_room_remove_by_you_with_reason">"Вы удалили %1$s: %2$s"</string>
|
||||
<string name="state_event_room_remove_with_reason">"%1$s удален %2$s: %3$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s отправила приглашение %2$s присоединиться к комнате"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Вы отправили приглашение присоединиться к комнате %1$s"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s отозвал приглашение %2$s присоединиться к комнате"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"Вы отозвали приглашение %1$s присоединиться к комнате"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s изменил тему на: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Вы изменили тему на: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s удалил тему комнаты"</string>
|
||||
<string name="state_event_room_remove_with_reason">"%1$s удалил(а) %2$s: %3$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s отправил(а) приглашение %2$s"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Вы отправили приглашение %1$s"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s отозвал приглашение %2$s"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"Вы отозвали приглашение %1$s"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s изменил(а) тему на %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Вы изменили тему на %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s удалил(а) тему комнаты"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Вы удалили тему комнаты"</string>
|
||||
<string name="state_event_room_unban">"%1$s разблокировал %2$s"</string>
|
||||
<string name="state_event_room_unban">"%1$s разблокировал(а) %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"Вы разблокировали %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s внес неизвестное изменение для своих участников"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -20,5 +20,6 @@ sealed class QrLoginException : Exception() {
|
|||
data object OtherDeviceNotSignedIn : QrLoginException()
|
||||
data object CheckCodeAlreadySent : QrLoginException()
|
||||
data object CheckCodeCannotBeSent : QrLoginException()
|
||||
data object UnsupportedQrCodeType : QrLoginException()
|
||||
data object Unknown : QrLoginException()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ sealed class ErrorType(message: String) : Exception(message) {
|
|||
*/
|
||||
class UnsupportedProtocol(message: String) : ErrorType(message)
|
||||
|
||||
/**
|
||||
* The QR code type is not supported by the client.
|
||||
*/
|
||||
class UnsupportedQrCodeType(message: String) : ErrorType(message)
|
||||
|
||||
/**
|
||||
* Secrets backup not set up properly.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ dependencies {
|
|||
} else {
|
||||
debugImplementation(libs.matrix.sdk)
|
||||
}
|
||||
implementation(files("libs/rustls-platform-verifier-android.aar"))
|
||||
|
||||
implementation(projects.appconfig)
|
||||
implementation(projects.libraries.androidutils)
|
||||
implementation(projects.libraries.di)
|
||||
|
|
|
|||
BIN
libraries/matrix/impl/libs/rustls-platform-verifier-android.aar
Normal file
BIN
libraries/matrix/impl/libs/rustls-platform-verifier-android.aar
Normal file
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
Updated rustls-platform-verifier-android.aar using `rustls-platform-verifier-0.1.1.aar`
|
||||
|
|
@ -17,7 +17,6 @@ import io.element.android.libraries.di.annotations.AppCoroutineScope
|
|||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
||||
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
|
||||
import io.element.android.libraries.matrix.impl.paths.SessionPaths
|
||||
import io.element.android.libraries.matrix.impl.paths.getSessionPaths
|
||||
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
|
||||
|
|
@ -57,7 +56,6 @@ class RustMatrixClientFactory(
|
|||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val sessionStore: SessionStore,
|
||||
private val userAgentProvider: UserAgentProvider,
|
||||
private val userCertificatesProvider: UserCertificatesProvider,
|
||||
private val proxyProvider: ProxyProvider,
|
||||
private val clock: SystemClock,
|
||||
private val analyticsService: AnalyticsService,
|
||||
|
|
@ -143,7 +141,6 @@ class RustMatrixClientFactory(
|
|||
}
|
||||
.setSessionDelegate(sessionDelegate)
|
||||
.userAgent(userAgentProvider.provide())
|
||||
.addRootCertificates(userCertificatesProvider.provides())
|
||||
.autoEnableBackups(true)
|
||||
.autoEnableCrossSigning(true)
|
||||
.roomKeyRecipientStrategy(
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@ fun Throwable.mapAuthenticationException(): AuthenticationException {
|
|||
is ClientBuildException.Sdk -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.ServerUnreachable -> AuthenticationException.ServerUnreachable(message)
|
||||
is ClientBuildException.SlidingSync -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.WellKnownDeserializationException -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.WellKnownDeserializationException -> {
|
||||
// Can happen if the .well-known URL has a redirection to an HTML page for instance
|
||||
AuthenticationException.InvalidServerName(message)
|
||||
}
|
||||
is ClientBuildException.WellKnownLookupFailed -> AuthenticationException.Generic(message)
|
||||
is ClientBuildException.EventCache -> AuthenticationException.Generic(message)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,19 +13,16 @@ import dev.zacsweers.metro.ContributesBinding
|
|||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||
import io.element.android.libraries.matrix.api.auth.HomeServerLoginCompatibilityChecker
|
||||
import io.element.android.libraries.matrix.impl.ClientBuilderProvider
|
||||
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
|
||||
import timber.log.Timber
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class RustHomeServerLoginCompatibilityChecker(
|
||||
private val clientBuilderProvider: ClientBuilderProvider,
|
||||
private val userCertificatesProvider: UserCertificatesProvider,
|
||||
) : HomeServerLoginCompatibilityChecker {
|
||||
override suspend fun check(url: String): Result<Boolean> = runCatchingExceptions {
|
||||
clientBuilderProvider.provide()
|
||||
.inMemoryStore()
|
||||
.serverNameOrHomeserverUrl(url)
|
||||
.addRootCertificates(userCertificatesProvider.provides())
|
||||
.build()
|
||||
.use {
|
||||
it.homeserverLoginDetails()
|
||||
|
|
|
|||
|
|
@ -46,5 +46,6 @@ object QrErrorMapper {
|
|||
is RustHumanQrLoginException.SlidingSyncNotAvailable -> QrLoginException.SlidingSyncNotAvailable
|
||||
is RustHumanQrLoginException.CheckCodeAlreadySent -> QrLoginException.CheckCodeAlreadySent
|
||||
is RustHumanQrLoginException.CheckCodeCannotBeSent -> QrLoginException.CheckCodeCannotBeSent
|
||||
is RustHumanQrLoginException.UnsupportedQrCodeType -> QrLoginException.UnsupportedQrCodeType
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.certificates
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import timber.log.Timber
|
||||
import java.security.KeyStore
|
||||
import java.security.KeyStoreException
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultUserCertificatesProvider : UserCertificatesProvider {
|
||||
/**
|
||||
* Get additional user-installed certificates from the `AndroidCAStore` `Keystore`.
|
||||
*
|
||||
* The Rust HTTP client doesn't include user-installed certificates in its internal certificate
|
||||
* store. This means that whatever the user installs will be ignored.
|
||||
*
|
||||
* While most users don't need user-installed certificates some special deployments or debugging
|
||||
* setups using a proxy might want to use them.
|
||||
*
|
||||
* @return A list of byte arrays where each byte array is a single user-installed certificate
|
||||
* in encoded form.
|
||||
*/
|
||||
override fun provides(): List<ByteArray> {
|
||||
// At least for API 34 the `AndroidCAStore` `Keystore` type contained user certificates as well.
|
||||
// I have not found this to be documented anywhere.
|
||||
val keyStore: KeyStore = try {
|
||||
KeyStore.getInstance("AndroidCAStore")
|
||||
} catch (e: KeyStoreException) {
|
||||
Timber.w(e, "Failed to get AndroidCAStore keystore")
|
||||
return emptyList()
|
||||
}
|
||||
val aliases = try {
|
||||
keyStore.load(null)
|
||||
keyStore.aliases()
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "Failed to load and get aliases AndroidCAStore keystore")
|
||||
return emptyList()
|
||||
}
|
||||
return aliases.toList()
|
||||
.filter { alias ->
|
||||
// The certificate alias always contains the prefix `system` or
|
||||
// `user` and the MD5 subject hash separated by a colon.
|
||||
//
|
||||
// The subject hash can be calculated using openssl as such:
|
||||
// openssl x509 -subject_hash_old -noout -in mycert.cer
|
||||
//
|
||||
// Again, I have not found this to be documented somewhere.
|
||||
alias.startsWith("user")
|
||||
}
|
||||
.mapNotNull { alias ->
|
||||
try {
|
||||
keyStore.getEntry(alias, null)
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "Failed to get entry for alias $alias")
|
||||
null
|
||||
}
|
||||
}
|
||||
.filterIsInstance<KeyStore.TrustedCertificateEntry>()
|
||||
.map { trustedCertificateEntry ->
|
||||
trustedCertificateEntry.trustedCertificate.encoded
|
||||
}
|
||||
.also {
|
||||
// Let's at least log the number of user-installed certificates we found,
|
||||
// since the alias isn't particularly useful nor does the issuer seem to
|
||||
// be easily available.
|
||||
Timber.i("Found ${it.size} additional user-provided certificates.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.certificates
|
||||
|
||||
interface UserCertificatesProvider {
|
||||
fun provides(): List<ByteArray>
|
||||
}
|
||||
|
|
@ -22,4 +22,5 @@ internal fun HumanQrGrantLoginException.map() = when (this) {
|
|||
is HumanQrGrantLoginException.OtherDeviceAlreadySignedIn -> ErrorType.OtherDeviceAlreadySignedIn(message.orEmpty())
|
||||
is HumanQrGrantLoginException.Unknown -> ErrorType.Unknown(message.orEmpty())
|
||||
is HumanQrGrantLoginException.UnsupportedProtocol -> ErrorType.UnsupportedProtocol(message.orEmpty())
|
||||
is HumanQrGrantLoginException.UnsupportedQrCodeType -> ErrorType.UnsupportedQrCodeType(message.orEmpty())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,16 +10,19 @@ package io.element.android.libraries.matrix.impl.platform
|
|||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.matrix.api.platform.InitPlatformService
|
||||
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
|
||||
import io.element.android.libraries.matrix.impl.tracing.map
|
||||
import org.matrix.rustcomponents.sdk.initPlatform
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class RustInitPlatformService : InitPlatformService {
|
||||
class RustInitPlatformService(
|
||||
private val buildMeta: BuildMeta,
|
||||
) : InitPlatformService {
|
||||
override fun init(tracingConfiguration: TracingConfiguration) {
|
||||
initPlatform(
|
||||
config = tracingConfiguration.map(),
|
||||
config = tracingConfiguration.map(buildMeta),
|
||||
useLightweightTokioRuntime = false
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,9 +65,8 @@ internal fun RoomListInterface.entriesFlow(
|
|||
trySendBlocking(roomEntriesUpdate)
|
||||
}
|
||||
}
|
||||
val result = entriesWithDynamicAdaptersWith(
|
||||
val result = entriesWithDynamicAdapters(
|
||||
pageSize = pageSize.toUInt(),
|
||||
enableLatestEventSorter = true,
|
||||
listener = listener,
|
||||
)
|
||||
val controller = result.controller()
|
||||
|
|
|
|||
|
|
@ -107,6 +107,10 @@ class TimelineEventContentMapper(
|
|||
threadInfo = extractThreadInfo(it.content),
|
||||
)
|
||||
}
|
||||
is MsgLikeKind.LiveLocation -> {
|
||||
// Live location messages are a special kind of message that we want to treat as unknown content for now
|
||||
UnknownContent
|
||||
}
|
||||
is MsgLikeKind.Other -> UnknownContent
|
||||
}
|
||||
}
|
||||
|
|
@ -134,9 +138,6 @@ class TimelineEventContentMapper(
|
|||
}
|
||||
is TimelineItemContent.CallInvite -> LegacyCallInviteContent
|
||||
is TimelineItemContent.RtcNotification -> CallNotifyContent
|
||||
is TimelineItemContent.LiveLocation -> {
|
||||
UnknownContent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.tracing.LogLevel
|
|||
import io.element.android.libraries.matrix.api.tracing.TracingConfiguration
|
||||
import io.element.android.libraries.matrix.api.tracing.TracingService
|
||||
import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration
|
||||
import org.matrix.rustcomponents.sdk.SentryConfig
|
||||
import org.matrix.rustcomponents.sdk.TracingFileConfiguration
|
||||
import org.matrix.rustcomponents.sdk.reloadTracingFileWriter
|
||||
import timber.log.Timber
|
||||
|
|
@ -59,11 +60,17 @@ private fun WriteToFilesConfiguration.toTracingFileConfiguration(): TracingFileC
|
|||
}
|
||||
}
|
||||
|
||||
fun TracingConfiguration.map(): org.matrix.rustcomponents.sdk.TracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration(
|
||||
fun TracingConfiguration.map(buildMeta: BuildMeta): org.matrix.rustcomponents.sdk.TracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration(
|
||||
writeToStdoutOrSystem = writesToLogcat,
|
||||
logLevel = logLevel.toRustLogLevel(),
|
||||
extraTargets = extraTargets,
|
||||
traceLogPacks = traceLogPacks.map(),
|
||||
writeToFiles = writesToFilesConfiguration.toTracingFileConfiguration(),
|
||||
sentryDsn = sdkSentryDsn,
|
||||
sentryConfig = sdkSentryDsn?.let {
|
||||
SentryConfig(
|
||||
dsn = it,
|
||||
appVersion = buildMeta.versionName,
|
||||
appPlatform = "Android",
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.google.common.truth.Truth.assertThat
|
|||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.impl.auth.FakeProxyProvider
|
||||
import io.element.android.libraries.matrix.impl.auth.FakeUserCertificatesProvider
|
||||
import io.element.android.libraries.matrix.impl.room.FakeTimelineEventFilterFactory
|
||||
import io.element.android.libraries.matrix.impl.storage.FakeSqliteStoreBuilderProvider
|
||||
import io.element.android.libraries.network.useragent.SimpleUserAgentProvider
|
||||
|
|
@ -58,7 +57,6 @@ fun TestScope.createRustMatrixClientFactory(
|
|||
coroutineDispatchers = testCoroutineDispatchers(),
|
||||
sessionStore = sessionStore,
|
||||
userAgentProvider = SimpleUserAgentProvider(),
|
||||
userCertificatesProvider = FakeUserCertificatesProvider(),
|
||||
proxyProvider = FakeProxyProvider(),
|
||||
clock = FakeSystemClock(),
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,13 @@ class AuthenticationExceptionMappingTest {
|
|||
assertThat(mappedException).isException<AuthenticationException.Generic>("Generic exception")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mapping a WellKnownDeserializationException returns a InvalidServerName AuthenticationException`() {
|
||||
val exception = ClientBuildException.WellKnownDeserializationException("WellKnown Deserialization")
|
||||
val mappedException = exception.mapAuthenticationException()
|
||||
assertThat(mappedException).isException<AuthenticationException.InvalidServerName>("WellKnown Deserialization")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mapping specific exceptions map to their kotlin counterparts`() {
|
||||
assertThat(ClientBuildException.Generic("Unknown error").mapAuthenticationException())
|
||||
|
|
@ -50,8 +57,6 @@ class AuthenticationExceptionMappingTest {
|
|||
.isException<AuthenticationException.ServerUnreachable>("Server unreachable")
|
||||
assertThat(ClientBuildException.SlidingSync("Sliding Sync").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Generic>("Sliding Sync")
|
||||
assertThat(ClientBuildException.WellKnownDeserializationException("WellKnown Deserialization").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Generic>("WellKnown Deserialization")
|
||||
assertThat(ClientBuildException.WellKnownLookupFailed("WellKnown Lookup Failed").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Generic>("WellKnown Lookup Failed")
|
||||
assertThat(ClientBuildException.EventCache("EventCache error").mapAuthenticationException())
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
* Copyright 2024, 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
|
||||
|
||||
class FakeUserCertificatesProvider : UserCertificatesProvider {
|
||||
override fun provides(): List<ByteArray> {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +49,5 @@ class RustHomeserverLoginCompatibilityCheckerTest {
|
|||
FakeFfiClient(homeserverLoginDetailsResult = result)
|
||||
}
|
||||
},
|
||||
userCertificatesProvider = FakeUserCertificatesProvider(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -54,14 +55,14 @@ fun CreateDmConfirmationBottomSheet(
|
|||
ModalBottomSheet(
|
||||
modifier = modifier,
|
||||
onDismissRequest = onDismiss,
|
||||
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.padding(top = 24.dp, bottom = 16.dp, start = 16.dp, end = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Avatar(
|
||||
avatarData = matrixUser.getAvatarData(AvatarSize.DmCreationConfirmation),
|
||||
avatarType = AvatarType.User,
|
||||
|
|
@ -93,7 +94,6 @@ fun CreateDmConfirmationBottomSheet(
|
|||
onClick = onDismiss,
|
||||
text = stringResource(CommonStrings.action_cancel),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
<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_invites_invited_you">"%1$s (%2$s) пригласил вас"</string>
|
||||
<string name="screen_invites_invited_you">"%1$s (%2$s) пригласил(а) вас"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ private fun ExoPlayerMediaAudioView(
|
|||
durationInMillis = 0,
|
||||
canMute = false,
|
||||
isMuted = false,
|
||||
seekingToMillis = null,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -171,15 +172,21 @@ private fun ExoPlayerMediaAudioView(
|
|||
LaunchedEffect(exoPlayer.isPlaying) {
|
||||
if (exoPlayer.isPlaying) {
|
||||
while (true) {
|
||||
val position = exoPlayer.currentPosition
|
||||
val seekingTo = mediaPlayerControllerState.seekingToMillis
|
||||
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||
progressInMillis = exoPlayer.currentPosition,
|
||||
progressInMillis = position,
|
||||
seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo,
|
||||
)
|
||||
delay(200)
|
||||
}
|
||||
} else {
|
||||
// Ensure we render the final state
|
||||
val position = exoPlayer.currentPosition
|
||||
val seekingTo = mediaPlayerControllerState.seekingToMillis
|
||||
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||
progressInMillis = exoPlayer.currentPosition,
|
||||
progressInMillis = position,
|
||||
seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -294,6 +301,9 @@ private fun ExoPlayerMediaAudioView(
|
|||
exoPlayer.togglePlay()
|
||||
},
|
||||
onSeekChange = {
|
||||
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||
seekingToMillis = it.toLong(),
|
||||
)
|
||||
exoPlayer.seekToEnsurePlaying(it.toLong())
|
||||
},
|
||||
onToggleMute = {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,16 @@ data class MediaPlayerControllerState(
|
|||
val durationInMillis: Long,
|
||||
val canMute: Boolean,
|
||||
val isMuted: Boolean,
|
||||
val seekingToMillis: Long?,
|
||||
) {
|
||||
/**
|
||||
* The progress in milliseconds to display. When [seekingToMillis] is non-null (during a seek operation),
|
||||
* this returns the target seek position. Once the player catches up to the seek position,
|
||||
* [seekingToMillis] is cleared (set to null) and this returns [progressInMillis] again.
|
||||
*/
|
||||
val displayProgressInMillis: Long
|
||||
get() = seekingToMillis ?: progressInMillis
|
||||
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
val progressAsFloat = (progressInMillis.toFloat() / durationInMillis.toFloat()).coerceIn(0f, 1f)
|
||||
val progressAsFloat = (displayProgressInMillis.toFloat() / durationInMillis.toFloat()).coerceIn(0f, 1f)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ private fun aMediaPlayerControllerState(
|
|||
durationInMillis: Long = 83_000,
|
||||
canMute: Boolean = true,
|
||||
isMuted: Boolean = false,
|
||||
seekingToMillis: Long? = null,
|
||||
) = MediaPlayerControllerState(
|
||||
isVisible = isVisible,
|
||||
isPlaying = isPlaying,
|
||||
|
|
@ -42,4 +43,5 @@ private fun aMediaPlayerControllerState(
|
|||
durationInMillis = durationInMillis,
|
||||
canMute = canMute,
|
||||
isMuted = isMuted,
|
||||
seekingToMillis = seekingToMillis,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ fun MediaPlayerControllerView(
|
|||
modifier = Modifier
|
||||
.widthIn(min = 48.dp)
|
||||
.padding(horizontal = 8.dp),
|
||||
text = state.progressInMillis.toHumanReadableDuration(),
|
||||
text = state.displayProgressInMillis.toHumanReadableDuration(),
|
||||
textAlign = TextAlign.Center,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
style = ElementTheme.typography.fontBodyXsMedium,
|
||||
|
|
@ -135,7 +135,9 @@ fun MediaPlayerControllerView(
|
|||
Slider(
|
||||
modifier = Modifier.weight(1f),
|
||||
valueRange = 0f..state.durationInMillis.toFloat(),
|
||||
value = lastSelectedValue.takeIf { it >= 0 } ?: state.progressInMillis.toFloat(),
|
||||
value = lastSelectedValue.takeIf { it >= 0 }
|
||||
?: state.seekingToMillis?.toFloat()
|
||||
?: state.progressInMillis.toFloat(),
|
||||
onValueChange = {
|
||||
lastSelectedValue = it
|
||||
},
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ private fun ExoPlayerMediaVideoView(
|
|||
durationInMillis = 0,
|
||||
canMute = true,
|
||||
isMuted = false,
|
||||
seekingToMillis = null,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -225,6 +226,9 @@ private fun ExoPlayerMediaVideoView(
|
|||
},
|
||||
onSeekChange = {
|
||||
autoHideController++
|
||||
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||
seekingToMillis = it.toLong(),
|
||||
)
|
||||
exoPlayer.seekToEnsurePlaying(it.toLong())
|
||||
},
|
||||
onToggleMute = {
|
||||
|
|
@ -242,15 +246,21 @@ private fun ExoPlayerMediaVideoView(
|
|||
LaunchedEffect(exoPlayer.isPlaying) {
|
||||
if (exoPlayer.isPlaying) {
|
||||
while (true) {
|
||||
val position = exoPlayer.currentPosition
|
||||
val seekingTo = mediaPlayerControllerState.seekingToMillis
|
||||
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||
progressInMillis = exoPlayer.currentPosition,
|
||||
progressInMillis = position,
|
||||
seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo,
|
||||
)
|
||||
delay(200)
|
||||
}
|
||||
} else {
|
||||
// Ensure we render the final state
|
||||
val position = exoPlayer.currentPosition
|
||||
val seekingTo = mediaPlayerControllerState.seekingToMillis
|
||||
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||
progressInMillis = exoPlayer.currentPosition,
|
||||
progressInMillis = position,
|
||||
seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@
|
|||
<string name="screen_media_browser_list_loading_media">"Загрузка медиа…"</string>
|
||||
<string name="screen_media_browser_list_mode_files">"Файлы"</string>
|
||||
<string name="screen_media_browser_list_mode_media">"Медиа"</string>
|
||||
<string name="screen_media_browser_media_empty_state_subtitle">"Здесь будут показаны изображения и видео, загруженные в данную комнату."</string>
|
||||
<string name="screen_media_browser_media_empty_state_title">"Пока что нет загруженных медиафайлов"</string>
|
||||
<string name="screen_media_browser_media_empty_state_subtitle">"Здесь будут показаны изображения и видео из данной комнаты."</string>
|
||||
<string name="screen_media_browser_media_empty_state_title">"Пока что нет загруженных медиа"</string>
|
||||
<string name="screen_media_browser_title">"Медиа и файлы"</string>
|
||||
<string name="screen_media_details_file_format">"Формат файла"</string>
|
||||
<string name="screen_media_details_filename">"Имя файла"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Больше нет файлов для показа"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Больше нет медиа для показа"</string>
|
||||
<string name="screen_media_details_no_more_files_to_show">"Больше нет файлов для отображения"</string>
|
||||
<string name="screen_media_details_no_more_media_to_show">"Больше нет медиа для отображения"</string>
|
||||
<string name="screen_media_details_uploaded_by">"Загружено"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Загружено на"</string>
|
||||
<string name="screen_media_details_uploaded_on">"Загружено"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.push.api.push
|
||||
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
/**
|
||||
* Abstraction over wakelocks used for push handling to ensure the device stays awake while we handle the push and schedule and run the work.
|
||||
*/
|
||||
interface PushHandlingWakeLock {
|
||||
/**
|
||||
* Acquire a wakelock. The wakelock will be held for the given [time] or until [unlock] is called, whichever happens first.
|
||||
*/
|
||||
fun lock(time: Duration = 1.minutes)
|
||||
|
||||
/**
|
||||
* Release the wakelock. If no wakelock is associated with the key, this method does nothing.
|
||||
*/
|
||||
fun unlock()
|
||||
}
|
||||
|
|
@ -11,6 +11,11 @@
|
|||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
|
||||
<application>
|
||||
<service android:name=".push.FetchPushForegroundService"
|
||||
android:foregroundServiceType="shortService"
|
||||
android:exported="false"
|
||||
android:enabled="true" />
|
||||
|
||||
<receiver
|
||||
android:name=".notifications.TestNotificationReceiver"
|
||||
android:exported="false" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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.push.impl.di
|
||||
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesTo
|
||||
import io.element.android.libraries.push.impl.push.FetchPushForegroundService
|
||||
|
||||
@ContributesTo(AppScope::class)
|
||||
interface PushBindings {
|
||||
fun inject(fetchPushForegroundService: FetchPushForegroundService)
|
||||
}
|
||||
|
|
@ -58,6 +58,8 @@ interface NotificationChannels {
|
|||
* Get the channel for test notifications.
|
||||
*/
|
||||
fun getChannelIdForTest(): String
|
||||
|
||||
fun getSilentChannelId(): String = SILENT_NOTIFICATION_CHANNEL_ID
|
||||
}
|
||||
|
||||
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O)
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ package io.element.android.libraries.push.impl.notifications.factories
|
|||
import android.app.Notification
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationCompat.MessagingStyle
|
||||
import androidx.core.app.Person
|
||||
import androidx.core.os.bundleOf
|
||||
import coil3.ImageLoader
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
|
|
@ -145,7 +145,7 @@ class DefaultNotificationCreator(
|
|||
sessionId = roomInfo.sessionId,
|
||||
roomId = roomInfo.roomId,
|
||||
eventId = eventId,
|
||||
extras = bundleOf(ROOM_OPENED_FROM_NOTIFICATION to true),
|
||||
extras = Bundle().apply { putBoolean(ROOM_OPENED_FROM_NOTIFICATION, true) },
|
||||
)
|
||||
}
|
||||
val containsMissedCall = events.any { it.type == EventType.RTC_NOTIFICATION }
|
||||
|
|
@ -293,7 +293,7 @@ class DefaultNotificationCreator(
|
|||
sessionId = simpleNotifiableEvent.sessionId,
|
||||
roomId = simpleNotifiableEvent.roomId,
|
||||
eventId = null,
|
||||
extras = bundleOf(ROOM_OPENED_FROM_NOTIFICATION to true),
|
||||
extras = Bundle().apply { putBoolean(ROOM_OPENED_FROM_NOTIFICATION, true) },
|
||||
)
|
||||
)
|
||||
.apply {
|
||||
|
|
@ -331,9 +331,7 @@ class DefaultNotificationCreator(
|
|||
.annotateForDebug(8)
|
||||
)
|
||||
.setExtras(
|
||||
bundleOf(
|
||||
FALLBACK_COUNTER_EXTRA to counter
|
||||
)
|
||||
Bundle().apply { putInt(FALLBACK_COUNTER_EXTRA, counter) },
|
||||
)
|
||||
.setNumber(counter)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,10 @@ import io.element.android.libraries.workmanager.api.WorkManagerScheduler
|
|||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.currentCoroutineContext
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
private val loggerTag = LoggerTag("PushHandler", LoggerTag.PushLoggerTag)
|
||||
|
|
@ -61,7 +64,7 @@ class DefaultPushHandler(
|
|||
* @param pushData the data received in the push.
|
||||
* @param providerInfo the provider info.
|
||||
*/
|
||||
override suspend fun handle(pushData: PushData, providerInfo: String) {
|
||||
override suspend fun handle(pushData: PushData, providerInfo: String): Boolean {
|
||||
// Start measuring how long it takes to display a notification from when the push is received
|
||||
Timber.d("Calculating push-to-notification for event ${pushData.eventId}")
|
||||
val parent = analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.PushToNotification(pushData.eventId.value))
|
||||
|
|
@ -71,11 +74,17 @@ class DefaultPushHandler(
|
|||
if (buildMeta.lowPrivacyLoggingEnabled) {
|
||||
Timber.tag(loggerTag.value).d("## pushData: $pushData")
|
||||
}
|
||||
incrementPushDataStore.incrementPushCounter()
|
||||
|
||||
// Update the push counter without blocking the coroutine execution, as it is not critical to be updated before handling the push
|
||||
CoroutineScope(currentCoroutineContext()).launch {
|
||||
incrementPushDataStore.incrementPushCounter()
|
||||
}
|
||||
|
||||
// Diagnostic Push
|
||||
if (pushData.eventId == DefaultTestPush.TEST_EVENT_ID) {
|
||||
return if (pushData.eventId == DefaultTestPush.TEST_EVENT_ID) {
|
||||
pushHistoryService.onDiagnosticPush(providerInfo)
|
||||
diagnosticPushHandler.handlePush()
|
||||
false
|
||||
} else {
|
||||
handleInternal(pushData, providerInfo)
|
||||
}
|
||||
|
|
@ -92,7 +101,7 @@ class DefaultPushHandler(
|
|||
* @param pushData Object containing message data.
|
||||
* @param providerInfo the provider info.
|
||||
*/
|
||||
private suspend fun handleInternal(pushData: PushData, providerInfo: String) {
|
||||
private suspend fun handleInternal(pushData: PushData, providerInfo: String): Boolean {
|
||||
try {
|
||||
if (buildMeta.lowPrivacyLoggingEnabled) {
|
||||
Timber.tag(loggerTag.value).d("## handleInternal() : $pushData")
|
||||
|
|
@ -109,13 +118,13 @@ class DefaultPushHandler(
|
|||
roomId = pushData.roomId,
|
||||
reason = "Unable to get userId from client secret",
|
||||
)
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
val areNotificationsEnabled = userPushStoreFactory.getOrCreate(userId).getNotificationEnabledForDevice().first()
|
||||
if (!areNotificationsEnabled) {
|
||||
Timber.w("Push notification received when push notifications are disabled.")
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
val pushRequest = PushRequest(
|
||||
|
|
@ -130,13 +139,17 @@ class DefaultPushHandler(
|
|||
|
||||
Timber.d("Queueing notification: $pushRequest")
|
||||
pushHistoryService.insertOrUpdatePushRequest(pushRequest)
|
||||
Timber.d("Queueing notification finished")
|
||||
|
||||
if (!workManagerScheduler.hasPendingWork(userId, WorkManagerRequestType.NOTIFICATION_SYNC)) {
|
||||
Timber.d("No pending worker for push notifications found")
|
||||
workManagerScheduler.submit(syncPendingNotificationsRequestFactory.create(userId))
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
Timber.tag(loggerTag.value).e(e, "## handleInternal() failed")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.libraries.push.impl.push
|
||||
|
||||
import android.content.Context
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
import io.element.android.libraries.push.api.push.PushHandlingWakeLock
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.time.Duration
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
@SingleIn(AppScope::class)
|
||||
class DefaultPushHandlingWakeLock(
|
||||
@ApplicationContext private val context: Context,
|
||||
) : PushHandlingWakeLock {
|
||||
private val count = AtomicInteger(0)
|
||||
|
||||
override fun lock(time: Duration) {
|
||||
Timber.d("Acquiring wakelock for push handling, starting service.")
|
||||
FetchPushForegroundService.startIfNeeded(context)
|
||||
|
||||
count.incrementAndGet()
|
||||
}
|
||||
|
||||
override fun unlock() {
|
||||
Timber.d("Releasing wakelock used for push handling.")
|
||||
FetchPushForegroundService.stop(context)
|
||||
if (count.decrementAndGet() <= 0) {
|
||||
Timber.d("No more wakelock needed for push handling, stopping service.")
|
||||
count.set(0)
|
||||
} else {
|
||||
Timber.d("Wakelock still needed for push handling, restarting service | count: ${count.get()}.")
|
||||
FetchPushForegroundService.startIfNeeded(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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.push.impl.push
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.PowerManager
|
||||
import androidx.core.app.NotificationCompat
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||
import io.element.android.libraries.push.api.push.PushHandlingWakeLock
|
||||
import io.element.android.libraries.push.impl.di.PushBindings
|
||||
import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
private const val NOTIFICATION_ID = 1001
|
||||
|
||||
// This kind of foreground service can only last up to 3 minutes before onTimeout is called
|
||||
private val wakelockTimeout = 3.minutes.inWholeMilliseconds
|
||||
|
||||
/**
|
||||
* Foreground service used to ensure the device stays awake while we handle the pushes and schedule and run the work to fetch the notification content.
|
||||
*/
|
||||
class FetchPushForegroundService : Service() {
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
@Inject lateinit var notificationChannels: NotificationChannels
|
||||
@Inject lateinit var pushHandlingWakeLock: PushHandlingWakeLock
|
||||
@Inject @AppCoroutineScope lateinit var coroutineScope: CoroutineScope
|
||||
|
||||
private val wakelock: PowerManager.WakeLock by lazy {
|
||||
val powerManager = getSystemService(POWER_SERVICE) as PowerManager
|
||||
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "FetchPushService:WakeLock").apply {
|
||||
setReferenceCounted(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
bindings<PushBindings>().inject(this)
|
||||
|
||||
wakelock.acquire(wakelockTimeout)
|
||||
|
||||
val notificationCompat = NotificationCompat.Builder(this, notificationChannels.getSilentChannelId())
|
||||
.setSmallIcon(CommonDrawables.ic_notification)
|
||||
.setContentTitle(getString(CommonStrings.common_android_fetching_notifications_title))
|
||||
.setProgress(0, 0, true)
|
||||
.setVibrate(longArrayOf(0))
|
||||
.setSound(null)
|
||||
.build()
|
||||
startForeground(NOTIFICATION_ID, notificationCompat)
|
||||
|
||||
// The timeout is not automatic before Android 15, so we need to schedule it ourselves
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
coroutineScope.launch {
|
||||
delay(wakelockTimeout)
|
||||
onTimeout(startId)
|
||||
}
|
||||
}
|
||||
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
override fun stopService(intent: Intent?): Boolean {
|
||||
wakelock.release()
|
||||
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
return super.stopService(intent)
|
||||
}
|
||||
|
||||
override fun onTimeout(startId: Int) {
|
||||
super.onTimeout(startId)
|
||||
|
||||
pushHandlingWakeLock.unlock()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun startIfNeeded(context: Context) {
|
||||
// Don't start the foreground service if the device is already awake
|
||||
val powerManager = context.getSystemService(POWER_SERVICE) as PowerManager
|
||||
if (powerManager.isInteractive) return
|
||||
|
||||
start(context)
|
||||
}
|
||||
|
||||
fun start(context: Context) {
|
||||
val intent = Intent(context, FetchPushForegroundService::class.java)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent)
|
||||
} else {
|
||||
context.startService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
fun stop(context: Context) {
|
||||
val intent = Intent(context, FetchPushForegroundService::class.java)
|
||||
context.stopService(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.api.auth.SessionRestorationException
|
|||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.exception.ClientException
|
||||
import io.element.android.libraries.matrix.api.exception.isNetworkError
|
||||
import io.element.android.libraries.push.api.push.PushHandlingWakeLock
|
||||
import io.element.android.libraries.push.impl.db.PushRequest
|
||||
import io.element.android.libraries.push.impl.history.PushHistoryService
|
||||
import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver
|
||||
|
|
@ -57,6 +58,7 @@ class FetchPendingNotificationsWorker(
|
|||
private val resultProcessor: NotificationResultProcessor,
|
||||
private val analyticsService: AnalyticsService,
|
||||
private val systemClock: SystemClock,
|
||||
private val pushHandlingWakeLock: PushHandlingWakeLock,
|
||||
) : CoroutineWorker(context, params) {
|
||||
override suspend fun doWork(): Result {
|
||||
Timber.d("FetchNotificationsWorker started")
|
||||
|
|
@ -65,6 +67,8 @@ class FetchPendingNotificationsWorker(
|
|||
inputData.getString(SyncPendingNotificationsRequestBuilder.SESSION_ID)?.let(::SessionId)
|
||||
}.getOrNull() ?: return Result.failure()
|
||||
|
||||
pushHandlingWakeLock.unlock()
|
||||
|
||||
// Fetch pending requests in the last 24 hours
|
||||
val fetchSince = Instant.fromEpochMilliseconds(systemClock.epochMillis()).minus(1.days)
|
||||
val requests = pushHistoryService.getPendingPushRequests(sessionId, fetchSince).getOrNull() ?: return Result.failure()
|
||||
|
|
@ -101,9 +105,9 @@ class FetchPendingNotificationsWorker(
|
|||
|
||||
results
|
||||
},
|
||||
onFailure = {
|
||||
onFailure = { throwable ->
|
||||
// This is a failure at the fetch notification setup, not a failure for a single fetch notification operation
|
||||
return handleSetupError(sessionId, requests, pendingAnalyticTransactions, it)
|
||||
return handleSetupError(sessionId, requests, pendingAnalyticTransactions, throwable)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
<item quantity="few">"Máte %d nové zprávy."</item>
|
||||
<item quantity="other">"Máte %d nových zpráv."</item>
|
||||
</plurals>
|
||||
<string name="notification_incoming_audio_call">"📞 Příchozí hovor"</string>
|
||||
<string name="notification_incoming_call">"📹 Příchozí hovor"</string>
|
||||
<string name="notification_inline_reply_failed">"** Nepodařilo se odeslat - otevřete prosím místnost"</string>
|
||||
<string name="notification_invitation_action_join">"Vstoupit"</string>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<item quantity="one">"Du har %d ny besked."</item>
|
||||
<item quantity="other">"Du har %d nye beskeder."</item>
|
||||
</plurals>
|
||||
<string name="notification_incoming_audio_call">"📞 Indgående opkald"</string>
|
||||
<string name="notification_incoming_call">"📹 Indgående opkald"</string>
|
||||
<string name="notification_inline_reply_failed">"** Kunne ikke sende - åbn venligst rummet"</string>
|
||||
<string name="notification_invitation_action_join">"Deltag"</string>
|
||||
|
|
@ -42,8 +43,8 @@
|
|||
<string name="notification_room_invite_body_with_sender">"%1$s inviterede dig til at deltage i rummet"</string>
|
||||
<string name="notification_sender_me">"Mig"</string>
|
||||
<string name="notification_sender_mention_reply">"%1$s nævnt eller besvaret"</string>
|
||||
<string name="notification_space_invite_body">"Inviterede dig til at deltage i gruppen"</string>
|
||||
<string name="notification_space_invite_body_with_sender">"%1$s inviterede dig til at deltage i gruppen"</string>
|
||||
<string name="notification_space_invite_body">"Inviterede dig til at deltage i klyngen"</string>
|
||||
<string name="notification_space_invite_body_with_sender">"%1$s inviterede dig til at deltage i klyngen"</string>
|
||||
<string name="notification_test_push_notification_content">"Du ser notifikationen! Klik på mig!"</string>
|
||||
<string name="notification_thread_in_room">"Tråd i %1$s"</string>
|
||||
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<item quantity="one">"Έχετε %d νέο μήνυμα."</item>
|
||||
<item quantity="other">"Έχετε %d νέα μηνύματα."</item>
|
||||
</plurals>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
<item quantity="one">"Van %d új üzenete."</item>
|
||||
<item quantity="other">"Van %d új üzenete."</item>
|
||||
</plurals>
|
||||
<string name="notification_incoming_audio_call">"📞 Bejövő hívás"</string>
|
||||
<string name="notification_incoming_call">"📹 Bejövő hívás"</string>
|
||||
<string name="notification_inline_reply_failed">"** Nem sikerült elküldeni – nyissa meg a szobát"</string>
|
||||
<string name="notification_invitation_action_join">"Csatlakozás"</string>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@
|
|||
<plurals name="notification_compat_summary_title">
|
||||
<item quantity="other">"%d 알림"</item>
|
||||
</plurals>
|
||||
<string name="notification_error_unified_push_unregistered_android">"통합 푸시(UnifiedPush) 알림 배포기를 등록할 수 없어 더 이상 알림을 받을 수 없습니다. 앱의 알림 설정과 푸시 배포기의 상태를 확인해 주세요."</string>
|
||||
<string name="notification_fallback_content">"알림"</string>
|
||||
<plurals name="notification_fallback_n_content">
|
||||
<item quantity="other">"%d개의 새 메시지가 있습니다."</item>
|
||||
</plurals>
|
||||
<string name="notification_incoming_call">"📹 수신 전화"</string>
|
||||
<string name="notification_inline_reply_failed">"** 전송 실패 - 방을 열여주세요"</string>
|
||||
<string name="notification_invitation_action_join">"참가하기"</string>
|
||||
|
|
@ -33,7 +37,10 @@
|
|||
<string name="notification_room_invite_body_with_sender">"%1$s 가 당신을 이 방에 초대했습니다"</string>
|
||||
<string name="notification_sender_me">"나"</string>
|
||||
<string name="notification_sender_mention_reply">"%1$s 언급하거나 답변함"</string>
|
||||
<string name="notification_space_invite_body">"귀하를 스페이스 참여에 초대했습니다"</string>
|
||||
<string name="notification_space_invite_body_with_sender">"%1$s님이 귀하를 스페이스 참여하도록 초대했습니다"</string>
|
||||
<string name="notification_test_push_notification_content">"알림을 보고 있습니다! 클릭해주세요!"</string>
|
||||
<string name="notification_thread_in_room">"%1$s의 스레드"</string>
|
||||
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
|
||||
<string name="notification_ticker_text_group">"%1$s: %2$s %3$s"</string>
|
||||
<plurals name="notification_unread_notified_messages">
|
||||
|
|
@ -48,10 +55,19 @@
|
|||
<string name="push_distributor_background_sync_android">"백그라운드 동기화"</string>
|
||||
<string name="push_distributor_firebase_android">"Google 서비스"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"유효한 Google Play 서비스를 찾지 못했습니다. 알림이 정상적으로 동작하지 않을 수 있습니다."</string>
|
||||
<string name="troubleshoot_notifications_test_blocked_users_description">"차단된 사용자 확인"</string>
|
||||
<string name="troubleshoot_notifications_test_blocked_users_quick_fix">"차단된 사용자 보기"</string>
|
||||
<string name="troubleshoot_notifications_test_blocked_users_result_none">"차단된 사용자는 없습니다."</string>
|
||||
<plurals name="troubleshoot_notifications_test_blocked_users_result_some">
|
||||
<item quantity="other">"%1$d명의 사용자를 차단했습니다. 이제 해당 사용자들의 알림을 받지 않습니다."</item>
|
||||
</plurals>
|
||||
<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">"푸시 제공자가 선택되지 않았습니다."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure_distributor_not_found">"현재 푸시 제공업체: %1$s, 현재 배포처: %2$s. 하지만 배포처 %3$s을(를) 찾을 수 없습니다. 애플리케이션이 삭제된 것일 수 있습니다."</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_failure_no_distributor">"현재 푸시 제공업체는 %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">"현재 푸시 제공업체: %1$s(%2$s)"</string>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"현재 푸시 제공자"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"애플리케이션이 적어도 하나의 푸시 제공자를 지원하는지 확인하십시오."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"푸시 제공자 지원이 발견되지 않았습니다."</string>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
<item quantity="few">"%d pranešimai"</item>
|
||||
<item quantity="other">"%d pranešimų"</item>
|
||||
</plurals>
|
||||
<string name="notification_fallback_content">"Turite naujų žinučių."</string>
|
||||
<string name="notification_inline_reply_failed">"** Nepavyko išsiųsti - prašome atidaryti kambarį"</string>
|
||||
<plurals name="notification_invitations">
|
||||
<item quantity="one">"%d kvietimas"</item>
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
<item quantity="few">"%d naujos žinutės"</item>
|
||||
<item quantity="other">"%d naujų žinučių"</item>
|
||||
</plurals>
|
||||
<string name="notification_reaction_body">"Reaguota su %1$s"</string>
|
||||
<string name="notification_room_action_quick_reply">"Sparčiai atsakyti"</string>
|
||||
<string name="notification_room_invite_body">"Pakvietė jus jungtis prie kambario"</string>
|
||||
<string name="notification_sender_me">"Aš"</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="notification_channel_call">"Позвонить"</string>
|
||||
<string name="notification_channel_listening_for_events">"Прослушивание событий"</string>
|
||||
<string name="notification_channel_noisy">"Шумные уведомления"</string>
|
||||
<string name="notification_channel_ringing_calls">"Звонки"</string>
|
||||
<string name="notification_channel_silent">"Бесшумные уведомления"</string>
|
||||
<string name="notification_channel_call">"Звонки"</string>
|
||||
<string name="notification_channel_listening_for_events">"События"</string>
|
||||
<string name="notification_channel_noisy">"Уведомления со звуком"</string>
|
||||
<string name="notification_channel_ringing_calls">"Звонки со звуком"</string>
|
||||
<string name="notification_channel_silent">"Уведомления без звука"</string>
|
||||
<plurals name="notification_compat_summary_line_for_room">
|
||||
<item quantity="one">"%1$s: %2$d сообщение"</item>
|
||||
<item quantity="few">"%1$s: %2$d сообщения"</item>
|
||||
|
|
@ -17,6 +17,12 @@
|
|||
</plurals>
|
||||
<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_call">"📹 Входящий вызов"</string>
|
||||
<string name="notification_inline_reply_failed">"** Не удалось отправить - пожалуйста, откройте комнату"</string>
|
||||
<string name="notification_invitation_action_join">"Присоединиться"</string>
|
||||
|
|
@ -26,26 +32,26 @@
|
|||
<item quantity="few">"%d приглашения"</item>
|
||||
<item quantity="many">"%d приглашений"</item>
|
||||
</plurals>
|
||||
<string name="notification_invite_body">"Пригласил вас в чат"</string>
|
||||
<string name="notification_invite_body_with_sender">"%1$s пригласил вас в чат"</string>
|
||||
<string name="notification_mentioned_you_body">"Упомянул вас: %1$s"</string>
|
||||
<string name="notification_invite_body">"Пригласил(а) вас в чат"</string>
|
||||
<string name="notification_invite_body_with_sender">"%1$s пригласил(а) вас в чат"</string>
|
||||
<string name="notification_mentioned_you_body">"Упомянул(а) вас: %1$s"</string>
|
||||
<string name="notification_new_messages">"Новые сообщения"</string>
|
||||
<plurals name="notification_new_messages_for_room">
|
||||
<item quantity="one">"%d новое сообщение"</item>
|
||||
<item quantity="few">"%d новых сообщения"</item>
|
||||
<item quantity="many">"%d новых сообщений"</item>
|
||||
</plurals>
|
||||
<string name="notification_reaction_body">"Отреагировал на %1$s"</string>
|
||||
<string name="notification_reaction_body">"Отреагировал(а) на %1$s"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Пометить как прочитанное"</string>
|
||||
<string name="notification_room_action_quick_reply">"Быстрый ответ"</string>
|
||||
<string name="notification_room_invite_body">"Пригласил вас в комнату"</string>
|
||||
<string name="notification_room_invite_body_with_sender">"%1$s пригласил вас присоединиться к комнате"</string>
|
||||
<string name="notification_room_invite_body">"Пригласил(а) вас в комнату"</string>
|
||||
<string name="notification_room_invite_body_with_sender">"%1$s пригласил(а) вас в комнату"</string>
|
||||
<string name="notification_sender_me">"Я"</string>
|
||||
<string name="notification_sender_mention_reply">"%1$s упомянул или ответил"</string>
|
||||
<string name="notification_space_invite_body">"Пригласил вас присоединиться к пространству"</string>
|
||||
<string name="notification_space_invite_body_with_sender">"%1$s пригласил вас присоединиться к пространству"</string>
|
||||
<string name="notification_sender_mention_reply">"%1$s упомянул(а) или ответил(а)"</string>
|
||||
<string name="notification_space_invite_body">"Пригласил(а) вас в пространство"</string>
|
||||
<string name="notification_space_invite_body_with_sender">"%1$s пригласил(а) вас в пространство"</string>
|
||||
<string name="notification_test_push_notification_content">"Вы просматриваете уведомление! Нажмите на меня!"</string>
|
||||
<string name="notification_thread_in_room">"Ветка обсуждения в %1$s"</string>
|
||||
<string name="notification_thread_in_room">"Ветка в %1$s"</string>
|
||||
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
|
||||
<string name="notification_ticker_text_group">"%1$s: %2$s %3$s"</string>
|
||||
<plurals name="notification_unread_notified_messages">
|
||||
|
|
@ -61,7 +67,7 @@
|
|||
<item quantity="few">"%d комнаты"</item>
|
||||
<item quantity="many">"%d комнат"</item>
|
||||
</plurals>
|
||||
<string name="push_distributor_background_sync_android">"Фоновая синхронизация"</string>
|
||||
<string name="push_distributor_background_sync_android">"Синхронизация в фоновом режиме"</string>
|
||||
<string name="push_distributor_firebase_android">"Сервисы Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Не найдены действующие службы Google Play. Уведомления могут работать некорректно."</string>
|
||||
<string name="troubleshoot_notifications_test_blocked_users_description">"Проверка заблокированных пользователей"</string>
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@
|
|||
<string name="notification_room_invite_body_with_sender">"%1$s запросив вас приєднатися до кімнати"</string>
|
||||
<string name="notification_sender_me">"Я"</string>
|
||||
<string name="notification_sender_mention_reply">"%1$s згадували або відповідали"</string>
|
||||
<string name="notification_space_invite_body_with_sender">"%1$s запрошує вас приєднатися до простору"</string>
|
||||
<string name="notification_test_push_notification_content">"Ви переглядаєте сповіщення! Натисніть тут!"</string>
|
||||
<string name="notification_thread_in_room">"Гілка в %1$s"</string>
|
||||
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
|
||||
<string name="notification_ticker_text_group">"%1$s: %2$s %3$s"</string>
|
||||
<plurals name="notification_unread_notified_messages">
|
||||
|
|
@ -60,10 +62,14 @@
|
|||
<string name="push_distributor_background_sync_android">"Фонова синхронізація"</string>
|
||||
<string name="push_distributor_firebase_android">"Сервіси Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Не знайдено дійсних сервісів Google Play. Сповіщення можуть не працювати належним чином."</string>
|
||||
<string name="troubleshoot_notifications_test_blocked_users_quick_fix">"Переглянути заблокованих користувачів"</string>
|
||||
<string name="troubleshoot_notifications_test_blocked_users_result_none">"Немає заблокованих користувачів"</string>
|
||||
<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_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>
|
||||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Поточний постачальник push-сповіщень"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Переконайтеся, що застосунок має принаймні одного постачальника push-сповіщень."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"Не знайдено постачальників push-сповіщень."</string>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder
|
|||
import io.element.android.tests.testutils.lambda.value
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.advanceTimeBy
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
|
@ -173,6 +174,10 @@ class DefaultPushHandlerTest {
|
|||
workManagerScheduler = workManagerScheduler,
|
||||
)
|
||||
defaultPushHandler.handle(aPushData, A_PUSHER_INFO)
|
||||
|
||||
// Give it enough time to increment the push counter
|
||||
runCurrent()
|
||||
|
||||
submitWorkLambda.assertions()
|
||||
.isNeverCalled()
|
||||
incrementPushCounterResult.assertions()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvid
|
|||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@Config(sdk = [33])
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DefaultSyncPendingNotificationsRequestBuilderTest {
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import io.element.android.libraries.push.impl.notifications.FakeNotificationResu
|
|||
import io.element.android.libraries.push.impl.notifications.fixtures.aPushRequest
|
||||
import io.element.android.libraries.push.impl.notifications.model.ResolvedPushEvent
|
||||
import io.element.android.libraries.push.impl.push.SyncOnNotifiableEvent
|
||||
import io.element.android.libraries.push.test.push.FakePushHandlingWakeLock
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerRequestBuilder
|
||||
import io.element.android.libraries.workmanager.api.di.MetroWorkerFactory
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
|
|
@ -238,6 +239,7 @@ class FetchPendingNotificationWorkerTest {
|
|||
pushHistoryService: FakePushHistoryService = FakePushHistoryService(),
|
||||
resultProcessor: FakeNotificationResultProcessor = FakeNotificationResultProcessor(),
|
||||
systemClock: FakeSystemClock = FakeSystemClock(),
|
||||
pushHandlingWakeLock: FakePushHandlingWakeLock = FakePushHandlingWakeLock(),
|
||||
) = FetchPendingNotificationsWorker(
|
||||
params = createWorkerParams(workDataOf("session_id" to input)),
|
||||
context = InstrumentationRegistry.getInstrumentation().context,
|
||||
|
|
@ -248,6 +250,7 @@ class FetchPendingNotificationWorkerTest {
|
|||
pushHistoryService = pushHistoryService,
|
||||
resultProcessor = resultProcessor,
|
||||
systemClock = systemClock,
|
||||
pushHandlingWakeLock = pushHandlingWakeLock,
|
||||
)
|
||||
|
||||
private fun TestScope.createWorkerParams(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.push.test.push
|
||||
|
||||
import io.element.android.libraries.push.api.push.PushHandlingWakeLock
|
||||
import kotlin.time.Duration
|
||||
|
||||
class FakePushHandlingWakeLock(
|
||||
private val lock: (time: Duration) -> Unit = {},
|
||||
private val unlock: () -> Unit = {},
|
||||
) : PushHandlingWakeLock {
|
||||
override fun lock(time: Duration) {
|
||||
lock.invoke(time)
|
||||
}
|
||||
|
||||
override fun unlock() {
|
||||
unlock.invoke()
|
||||
}
|
||||
}
|
||||
|
|
@ -13,11 +13,11 @@ import io.element.android.libraries.pushproviders.api.PushHandler
|
|||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakePushHandler(
|
||||
private val handleResult: (PushData, String) -> Unit = { _, _ -> lambdaError() },
|
||||
private val handleResult: (PushData, String) -> Boolean = { _, _ -> lambdaError() },
|
||||
private val handleInvalidResult: (String, String) -> Unit = { _, _ -> lambdaError() },
|
||||
) : PushHandler {
|
||||
override suspend fun handle(pushData: PushData, providerInfo: String) {
|
||||
handleResult(pushData, providerInfo)
|
||||
override suspend fun handle(pushData: PushData, providerInfo: String): Boolean {
|
||||
return handleResult(pushData, providerInfo)
|
||||
}
|
||||
|
||||
override suspend fun handleInvalid(providerInfo: String, data: String) {
|
||||
|
|
|
|||
|
|
@ -9,11 +9,21 @@
|
|||
package io.element.android.libraries.pushproviders.api
|
||||
|
||||
interface PushHandler {
|
||||
/**
|
||||
* Handle a push received from the provider.
|
||||
*
|
||||
* @param pushData the data of the push, containing the client secret and the push content.
|
||||
* @param providerInfo an identifier of the provider that sent the push, for logging and debugging purposes.
|
||||
* @return `true` if the push was handled successfully and is now enqueued for processing, false otherwise.
|
||||
*/
|
||||
suspend fun handle(
|
||||
pushData: PushData,
|
||||
providerInfo: String,
|
||||
)
|
||||
): Boolean
|
||||
|
||||
/**
|
||||
* Handle an invalid push received from the provider.
|
||||
*/
|
||||
suspend fun handleInvalid(
|
||||
providerInfo: String,
|
||||
data: String,
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ dependencies {
|
|||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.di)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.push.api)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.troubleshoot.api)
|
||||
implementation(projects.services.toolbox.api)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import dev.zacsweers.metro.Inject
|
|||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||
import io.element.android.libraries.push.api.push.PushHandlingWakeLock
|
||||
import io.element.android.libraries.pushproviders.api.PushHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -25,6 +26,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
@Inject lateinit var firebaseNewTokenHandler: FirebaseNewTokenHandler
|
||||
@Inject lateinit var pushParser: FirebasePushParser
|
||||
@Inject lateinit var pushHandler: PushHandler
|
||||
@Inject lateinit var pushHandlingWakeLock: PushHandlingWakeLock
|
||||
@AppCoroutineScope
|
||||
@Inject lateinit var coroutineScope: CoroutineScope
|
||||
|
||||
|
|
@ -42,6 +44,10 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
|
||||
override fun onMessageReceived(message: RemoteMessage) {
|
||||
Timber.tag(loggerTag.value).w("New Firebase message. Priority: ${message.priority}/${message.originalPriority}")
|
||||
|
||||
// Acquire wakelock to ensure the device stays awake while we handle the push and schedule and run the work
|
||||
pushHandlingWakeLock.lock()
|
||||
|
||||
coroutineScope.launch {
|
||||
val pushData = pushParser.parse(message.data)
|
||||
if (pushData == null) {
|
||||
|
|
@ -52,11 +58,17 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||
"$it: ${message.data[it]}"
|
||||
},
|
||||
)
|
||||
pushHandlingWakeLock.unlock()
|
||||
} else {
|
||||
pushHandler.handle(
|
||||
val handled = pushHandler.handle(
|
||||
pushData = pushData,
|
||||
providerInfo = FirebaseConfig.NAME,
|
||||
)
|
||||
|
||||
// If we failed to handle the push, we should release the wakelock early to avoid keeping the device awake for too long.
|
||||
if (!handled) {
|
||||
pushHandlingWakeLock.unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.google.firebase.messaging.RemoteMessage
|
|||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SECRET
|
||||
import io.element.android.libraries.push.test.push.FakePushHandlingWakeLock
|
||||
import io.element.android.libraries.push.test.test.FakePushHandler
|
||||
import io.element.android.libraries.pushproviders.api.PushData
|
||||
import io.element.android.libraries.pushproviders.api.PushHandler
|
||||
|
|
@ -28,6 +29,7 @@ import kotlinx.coroutines.test.runTest
|
|||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import kotlin.time.Duration
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class VectorFirebaseMessagingServiceTest {
|
||||
|
|
@ -55,7 +57,7 @@ class VectorFirebaseMessagingServiceTest {
|
|||
|
||||
@Test
|
||||
fun `test receiving valid data`() = runTest {
|
||||
val lambda = lambdaRecorder<PushData, String, Unit> { _, _ -> }
|
||||
val lambda = lambdaRecorder<PushData, String, Boolean> { _, _ -> true }
|
||||
val vectorFirebaseMessagingService = createVectorFirebaseMessagingService(
|
||||
pushHandler = FakePushHandler(handleResult = lambda)
|
||||
)
|
||||
|
|
@ -77,6 +79,68 @@ class VectorFirebaseMessagingServiceTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test pushHandler returning true locks and does not unlock the wakelock so it continues running`() = runTest {
|
||||
val lockLambda = lambdaRecorder<Duration, Unit> { _ -> }
|
||||
val unlockLambda = lambdaRecorder<Unit> { }
|
||||
val vectorFirebaseMessagingService = createVectorFirebaseMessagingService(
|
||||
pushHandler = FakePushHandler(handleResult = { _, _ -> true }),
|
||||
pushHandlingWakeLock = FakePushHandlingWakeLock(
|
||||
lock = lockLambda,
|
||||
unlock = unlockLambda
|
||||
)
|
||||
)
|
||||
vectorFirebaseMessagingService.onMessageReceived(
|
||||
message = RemoteMessage(
|
||||
Bundle().apply {
|
||||
putString("event_id", AN_EVENT_ID.value)
|
||||
putString("room_id", A_ROOM_ID.value)
|
||||
putString("cs", A_SECRET)
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
// The wakelock should be locked but not unlocked
|
||||
lockLambda.assertions().isCalledOnce()
|
||||
unlockLambda.assertions().isNeverCalled()
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
// After handling the push, the wakelock should still not be unlocked
|
||||
unlockLambda.assertions().isNeverCalled()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test pushHandler returning false locks and unlocks the wakelock early`() = runTest {
|
||||
val lockLambda = lambdaRecorder<Duration, Unit> { _ -> }
|
||||
val unlockLambda = lambdaRecorder<Unit> { }
|
||||
val vectorFirebaseMessagingService = createVectorFirebaseMessagingService(
|
||||
pushHandler = FakePushHandler(handleResult = { _, _ -> false }),
|
||||
pushHandlingWakeLock = FakePushHandlingWakeLock(
|
||||
lock = lockLambda,
|
||||
unlock = unlockLambda
|
||||
)
|
||||
)
|
||||
vectorFirebaseMessagingService.onMessageReceived(
|
||||
message = RemoteMessage(
|
||||
Bundle().apply {
|
||||
putString("event_id", AN_EVENT_ID.value)
|
||||
putString("room_id", A_ROOM_ID.value)
|
||||
putString("cs", A_SECRET)
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
// The wakelock should be locked but not unlocked
|
||||
lockLambda.assertions().isCalledOnce()
|
||||
unlockLambda.assertions().isNeverCalled()
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
// After handling the push, the wakelock should be unlocked
|
||||
unlockLambda.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test new token is forwarded to the handler`() = runTest {
|
||||
val lambda = lambdaRecorder<String, Unit> { }
|
||||
|
|
@ -93,12 +157,14 @@ class VectorFirebaseMessagingServiceTest {
|
|||
private fun TestScope.createVectorFirebaseMessagingService(
|
||||
firebaseNewTokenHandler: FirebaseNewTokenHandler = FakeFirebaseNewTokenHandler(),
|
||||
pushHandler: PushHandler = FakePushHandler(),
|
||||
pushHandlingWakeLock: FakePushHandlingWakeLock = FakePushHandlingWakeLock(),
|
||||
): VectorFirebaseMessagingService {
|
||||
return VectorFirebaseMessagingService().apply {
|
||||
this.firebaseNewTokenHandler = firebaseNewTokenHandler
|
||||
this.pushParser = FirebasePushParser()
|
||||
this.pushHandler = pushHandler
|
||||
this.coroutineScope = this@createVectorFirebaseMessagingService
|
||||
this.pushHandlingWakeLock = pushHandlingWakeLock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import dev.zacsweers.metro.Inject
|
|||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||
import io.element.android.libraries.push.api.push.PushHandlingWakeLock
|
||||
import io.element.android.libraries.pushproviders.api.PushHandler
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler
|
||||
import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult
|
||||
|
|
@ -37,12 +38,16 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
|
|||
@Inject lateinit var newGatewayHandler: UnifiedPushNewGatewayHandler
|
||||
@Inject lateinit var removedGatewayHandler: UnifiedPushRemovedGatewayHandler
|
||||
@Inject lateinit var endpointRegistrationHandler: EndpointRegistrationHandler
|
||||
@Inject lateinit var pushHandlingWakeLock: PushHandlingWakeLock
|
||||
|
||||
@AppCoroutineScope
|
||||
@Inject lateinit var coroutineScope: CoroutineScope
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
context.bindings<VectorUnifiedPushMessagingReceiverBindings>().inject(this)
|
||||
// We only need to inject this object once
|
||||
if (!this::pushParser.isInitialized) {
|
||||
context.bindings<VectorUnifiedPushMessagingReceiverBindings>().inject(this)
|
||||
}
|
||||
super.onReceive(context, intent)
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +59,9 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
|
|||
* @param instance connection, for multi-account
|
||||
*/
|
||||
override fun onMessage(context: Context, message: PushMessage, instance: String) {
|
||||
// Acquire wakelock to ensure the device stays awake while we handle the push and schedule and run the work
|
||||
pushHandlingWakeLock.lock()
|
||||
|
||||
Timber.tag(loggerTag.value).d("New message, decrypted: ${message.decrypted}")
|
||||
coroutineScope.launch {
|
||||
val pushData = pushParser.parse(message.content, instance)
|
||||
|
|
@ -63,11 +71,17 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
|
|||
providerInfo = "${UnifiedPushConfig.NAME} - $instance",
|
||||
data = String(message.content),
|
||||
)
|
||||
pushHandlingWakeLock.unlock()
|
||||
} else {
|
||||
pushHandler.handle(
|
||||
val handled = pushHandler.handle(
|
||||
pushData = pushData,
|
||||
providerInfo = "${UnifiedPushConfig.NAME} - $instance",
|
||||
)
|
||||
|
||||
// If we failed to handle the push, we should release the wakelock early to avoid keeping the device awake for too long.
|
||||
if (!handled) {
|
||||
pushHandlingWakeLock.unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
|||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SECRET
|
||||
import io.element.android.libraries.push.test.push.FakePushHandlingWakeLock
|
||||
import io.element.android.libraries.push.test.test.FakePushHandler
|
||||
import io.element.android.libraries.pushproviders.api.PushData
|
||||
import io.element.android.libraries.pushproviders.api.PushHandler
|
||||
|
|
@ -38,13 +39,14 @@ import org.unifiedpush.android.connector.FailedReason
|
|||
import org.unifiedpush.android.connector.data.PublicKeySet
|
||||
import org.unifiedpush.android.connector.data.PushEndpoint
|
||||
import org.unifiedpush.android.connector.data.PushMessage
|
||||
import kotlin.time.Duration
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class VectorUnifiedPushMessagingReceiverTest {
|
||||
@Test
|
||||
fun `onReceive does the binding`() = runTest {
|
||||
val context = InstrumentationRegistry.getInstrumentation().context
|
||||
val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver()
|
||||
val vectorUnifiedPushMessagingReceiver = VectorUnifiedPushMessagingReceiver()
|
||||
// The binding is not found in the test env.
|
||||
assertThrows(IllegalStateException::class.java) {
|
||||
vectorUnifiedPushMessagingReceiver.onReceive(context, Intent())
|
||||
|
|
@ -75,7 +77,7 @@ class VectorUnifiedPushMessagingReceiverTest {
|
|||
@Test
|
||||
fun `onMessage valid invokes the push handler`() = runTest {
|
||||
val context = InstrumentationRegistry.getInstrumentation().context
|
||||
val pushHandlerResult = lambdaRecorder<PushData, String, Unit> { _, _ -> }
|
||||
val pushHandlerResult = lambdaRecorder<PushData, String, Boolean> { _, _ -> true }
|
||||
val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver(
|
||||
pushHandler = FakePushHandler(
|
||||
handleResult = pushHandlerResult
|
||||
|
|
@ -100,6 +102,60 @@ class VectorUnifiedPushMessagingReceiverTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pushHandler returning true locks the wake lock but does not unlock it so it continues to run`() = runTest {
|
||||
val context = InstrumentationRegistry.getInstrumentation().context
|
||||
val pushHandlerResult = lambdaRecorder<PushData, String, Boolean> { _, _ -> true }
|
||||
val lockLambda = lambdaRecorder<Duration, Unit> { _ -> }
|
||||
val unlockLambda = lambdaRecorder<Unit> { }
|
||||
val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver(
|
||||
pushHandler = FakePushHandler(
|
||||
handleResult = pushHandlerResult
|
||||
),
|
||||
pushHandlingWakeLock = FakePushHandlingWakeLock(
|
||||
lock = lockLambda,
|
||||
unlock = unlockLambda,
|
||||
),
|
||||
)
|
||||
vectorUnifiedPushMessagingReceiver.onMessage(context, aPushMessage(), A_SECRET)
|
||||
|
||||
// The wakelock should be locked but not unlocked, so it should continue to run
|
||||
lockLambda.assertions().isCalledOnce()
|
||||
unlockLambda.assertions().isNeverCalled()
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
// After waiting for any possible timeout, the lock is still locked
|
||||
unlockLambda.assertions().isNeverCalled()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pushHandler returning false locks and unlocks the wakelock early`() = runTest {
|
||||
val context = InstrumentationRegistry.getInstrumentation().context
|
||||
val pushHandlerResult = lambdaRecorder<PushData, String, Boolean> { _, _ -> false }
|
||||
val lockLambda = lambdaRecorder<Duration, Unit> { _ -> }
|
||||
val unlockLambda = lambdaRecorder<Unit> { }
|
||||
val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver(
|
||||
pushHandler = FakePushHandler(
|
||||
handleResult = pushHandlerResult
|
||||
),
|
||||
pushHandlingWakeLock = FakePushHandlingWakeLock(
|
||||
lock = lockLambda,
|
||||
unlock = unlockLambda,
|
||||
),
|
||||
)
|
||||
vectorUnifiedPushMessagingReceiver.onMessage(context, aPushMessage(), A_SECRET)
|
||||
|
||||
// The wakelock should be locked but not unlocked, so it should continue to run
|
||||
lockLambda.assertions().isCalledOnce()
|
||||
unlockLambda.assertions().isNeverCalled()
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
// After waiting for a bit, the lock should have been unlocked since the handler returned false
|
||||
unlockLambda.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onMessage invalid invokes the push handler invalid method`() = runTest {
|
||||
val context = InstrumentationRegistry.getInstrumentation().context
|
||||
|
|
@ -208,6 +264,7 @@ class VectorUnifiedPushMessagingReceiverTest {
|
|||
unifiedPushNewGatewayHandler: UnifiedPushNewGatewayHandler = FakeUnifiedPushNewGatewayHandler(),
|
||||
endpointRegistrationHandler: EndpointRegistrationHandler = EndpointRegistrationHandler(),
|
||||
removedGatewayHandler: UnifiedPushRemovedGatewayHandler = UnifiedPushRemovedGatewayHandler { lambdaError() },
|
||||
pushHandlingWakeLock: FakePushHandlingWakeLock = FakePushHandlingWakeLock(),
|
||||
): VectorUnifiedPushMessagingReceiver {
|
||||
return VectorUnifiedPushMessagingReceiver().apply {
|
||||
this.pushParser = unifiedPushParser
|
||||
|
|
@ -220,6 +277,7 @@ class VectorUnifiedPushMessagingReceiverTest {
|
|||
this.removedGatewayHandler = removedGatewayHandler
|
||||
this.endpointRegistrationHandler = endpointRegistrationHandler
|
||||
this.coroutineScope = this@createVectorUnifiedPushMessagingReceiver
|
||||
this.pushHandlingWakeLock = pushHandlingWakeLock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
-- This file is not strictly necessary, since the first
|
||||
-- version of the DB is 1, so we will never migrate from 0
|
||||
|
||||
CREATE TABLE SessionData (
|
||||
userId TEXT NOT NULL PRIMARY KEY,
|
||||
deviceId TEXT NOT NULL,
|
||||
accessToken TEXT NOT NULL,
|
||||
refreshToken TEXT,
|
||||
homeserverUrl TEXT NOT NULL,
|
||||
slidingSyncProxy TEXT
|
||||
);
|
||||
|
|
@ -15,7 +15,6 @@ import androidx.compose.runtime.rememberUpdatedState
|
|||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import io.element.android.libraries.androidutils.ui.awaitWindowFocus
|
||||
import io.element.android.libraries.androidutils.ui.isKeyboardVisible
|
||||
import io.element.android.libraries.androidutils.ui.showKeyboard
|
||||
|
||||
/**
|
||||
|
|
@ -42,12 +41,15 @@ internal fun <T> SoftKeyboardEffect(
|
|||
// Await window focus in case returning from a dialog
|
||||
view.awaitWindowFocus()
|
||||
|
||||
if (!view.isKeyboardVisible()) {
|
||||
// Show the keyboard, temporarily using the root view for focus
|
||||
view.showKeyboard(andRequestFocus = true)
|
||||
// First, focus the correct editor view
|
||||
latestOnRequestFocus()
|
||||
|
||||
// Refocus to the correct view
|
||||
latestOnRequestFocus()
|
||||
// Show keyboard on the focused editor view rather than the root view,
|
||||
// as some devices require showSoftInput on the actual input view.
|
||||
// Using post to run after the current focus pass completes.
|
||||
view.post {
|
||||
val focusedView = view.findFocus() ?: view
|
||||
focusedView.showKeyboard()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="rich_text_editor_a11y_add_attachment">"Pridėti priedą"</string>
|
||||
<string name="rich_text_editor_bullet_list">"Perjungti punktų sąrašą"</string>
|
||||
<string name="rich_text_editor_code_block">"Kodo blokas"</string>
|
||||
<string name="rich_text_editor_composer_placeholder">"Žinutė…"</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="rich_text_editor_a11y_add_attachment">"Прикрепить файл"</string>
|
||||
<string name="rich_text_editor_bullet_list">"Переключить список маркеров"</string>
|
||||
<string name="rich_text_editor_bullet_list">"Переключить маркированный список"</string>
|
||||
<string name="rich_text_editor_close_formatting_options">"Отменить и закрыть параметры форматирования"</string>
|
||||
<string name="rich_text_editor_code_block">"Переключить блок кода"</string>
|
||||
<string name="rich_text_editor_composer_caption_placeholder">"Необязательный заголовок…"</string>
|
||||
<string name="rich_text_editor_composer_caption_placeholder">"Добавить подпись (опционально)"</string>
|
||||
<string name="rich_text_editor_composer_encrypted_placeholder">"Зашифрованное сообщение…"</string>
|
||||
<string name="rich_text_editor_composer_placeholder">"Сообщение…"</string>
|
||||
<string name="rich_text_editor_composer_unencrypted_placeholder">"Незашифрованное сообщение…"</string>
|
||||
|
|
@ -14,16 +14,16 @@
|
|||
<string name="rich_text_editor_format_bold">"Применить жирный шрифт"</string>
|
||||
<string name="rich_text_editor_format_italic">"Применить курсивный формат"</string>
|
||||
<string name="rich_text_editor_format_state_disabled">"отключено"</string>
|
||||
<string name="rich_text_editor_format_state_off">"ОТКЛ."</string>
|
||||
<string name="rich_text_editor_format_state_off">"ОТКЛ"</string>
|
||||
<string name="rich_text_editor_format_state_on">"ВКЛ"</string>
|
||||
<string name="rich_text_editor_format_strikethrough">"Применить формат зачеркивания"</string>
|
||||
<string name="rich_text_editor_format_underline">"Применить формат подчеркивания"</string>
|
||||
<string name="rich_text_editor_format_strikethrough">"Применить зачеркивание"</string>
|
||||
<string name="rich_text_editor_format_underline">"Применить подчеркивание"</string>
|
||||
<string name="rich_text_editor_full_screen_toggle">"Переключение полноэкранного режима"</string>
|
||||
<string name="rich_text_editor_indent">"Отступ"</string>
|
||||
<string name="rich_text_editor_inline_code">"Применить встроенный формат кода"</string>
|
||||
<string name="rich_text_editor_link">"Установить ссылку"</string>
|
||||
<string name="rich_text_editor_link">"Задать ссылку"</string>
|
||||
<string name="rich_text_editor_numbered_list">"Переключить нумерованный список"</string>
|
||||
<string name="rich_text_editor_open_compose_options">"Открыть параметры компоновки"</string>
|
||||
<string name="rich_text_editor_open_compose_options">"Открыть параметры составления"</string>
|
||||
<string name="rich_text_editor_quote">"Переключить цитату"</string>
|
||||
<string name="rich_text_editor_remove_link">"Удалить ссылку"</string>
|
||||
<string name="rich_text_editor_unindent">"Без отступа"</string>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_push_history_title">"История уведомлений"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Выполнение тестов"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Повторное выполнение тестов"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Некоторые тесты провалились. Пожалуйста, проверьте детали."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Выполните тесты, чтобы обнаружить любую проблему в конфигурации, из-за которой уведомления могут работать не так, как ожидалось."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Попытка исправить"</string>
|
||||
<string name="troubleshoot_notifications_screen_action">"Выполнить тесты"</string>
|
||||
<string name="troubleshoot_notifications_screen_action_again">"Выполнить тесты повторно"</string>
|
||||
<string name="troubleshoot_notifications_screen_failure">"Некоторые тесты не завершились успешно. Пожалуйста, проверьте подробности."</string>
|
||||
<string name="troubleshoot_notifications_screen_notice">"Выполните тесты, чтобы выявить проблемы в конфигурации, из-за которой уведомления могут работать не так, как ожидалось."</string>
|
||||
<string name="troubleshoot_notifications_screen_quick_fix_action">"Попытаться исправить"</string>
|
||||
<string name="troubleshoot_notifications_screen_success">"Все тесты прошли успешно."</string>
|
||||
<string name="troubleshoot_notifications_screen_title">"Уведомления об устранении неполадок"</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Некоторые тесты требуют вашего внимания. Пожалуйста, проверьте детали."</string>
|
||||
<string name="troubleshoot_notifications_screen_waiting">"Некоторые тесты требуют вашего внимания. Пожалуйста, проверьте подробности."</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@
|
|||
<string name="common_third_party_notices">"Уведомления от трети страни"</string>
|
||||
<string name="common_thread">"Нишка"</string>
|
||||
<string name="common_topic">"Тема"</string>
|
||||
<string name="common_topic_placeholder">"За какво се отнася тази стая?"</string>
|
||||
<string name="common_topic_placeholder">"За какво е тази стая?"</string>
|
||||
<string name="common_unable_to_decrypt">"Не може да се разшифрова"</string>
|
||||
<string name="common_unable_to_decrypt_no_access">"Нямате достъп до това съобщение"</string>
|
||||
<string name="common_unable_to_invite_message">"Поканите не можаха да бъдат изпратени до един или повече потребители."</string>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
<string name="a11y_pause">"Pozastavit"</string>
|
||||
<string name="a11y_paused_voice_message">"Hlasová zpráva, délka: %1$s, aktuální pozice: %2$s"</string>
|
||||
<string name="a11y_pin_field">"Pole pro PIN"</string>
|
||||
<string name="a11y_pinned_location">"Připnutá poloha"</string>
|
||||
<string name="a11y_play">"Přehrát"</string>
|
||||
<string name="a11y_playback_speed">"Rychlost přehrávání"</string>
|
||||
<string name="a11y_poll">"Hlasování"</string>
|
||||
|
|
@ -47,9 +48,11 @@
|
|||
<string name="a11y_remove_reaction_with">"Odstranit reakci pomocí %1$s"</string>
|
||||
<string name="a11y_room_avatar">"Avatar místnosti"</string>
|
||||
<string name="a11y_send_files">"Odeslat soubory"</string>
|
||||
<string name="a11y_sender_location">"Poloha odesílatele"</string>
|
||||
<string name="a11y_session_verification_time_limited_action_required">"Vyžaduje se časově omezená akce, na ověření máte jednu minutu"</string>
|
||||
<string name="a11y_show_password">"Zobrazit heslo"</string>
|
||||
<string name="a11y_start_call">"Zahájit hovor"</string>
|
||||
<string name="a11y_start_voice_call">"Zahájit hlasový hovor"</string>
|
||||
<string name="a11y_tombstoned_room">"Místnost s náhrobkem"</string>
|
||||
<string name="a11y_user_avatar">"Avatar uživatele"</string>
|
||||
<string name="a11y_user_menu">"Uživatelské menu"</string>
|
||||
|
|
@ -276,6 +279,7 @@ Důvod: %1$s."</string>
|
|||
<string name="common_offline">"Offline"</string>
|
||||
<string name="common_open_source_licenses">"Licence s otevřeným zdrojovým kódem"</string>
|
||||
<string name="common_or">"nebo"</string>
|
||||
<string name="common_other_options">"Další možnosti"</string>
|
||||
<string name="common_password">"Heslo"</string>
|
||||
<string name="common_people">"Lidé"</string>
|
||||
<string name="common_permalink">"Trvalý odkaz"</string>
|
||||
|
|
@ -460,6 +464,7 @@ Opravdu chcete pokračovat?"</string>
|
|||
<string name="screen_create_poll_remove_accessibility_label">"Odstranit %1$s"</string>
|
||||
<string name="screen_create_poll_settings_section_title">"Nastavení"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Výběr média se nezdařil, zkuste to prosím znovu."</string>
|
||||
<string name="screen_onboarding_welcome_back">"Vítejte zpět"</string>
|
||||
<string name="screen_pinned_timeline_empty_state_description">"Přidržte zprávu a vyberte „%1$s“, kterou chcete zahrnout sem."</string>
|
||||
<string name="screen_pinned_timeline_empty_state_headline">"Připněte důležité zprávy, aby je bylo možné snadno najít"</string>
|
||||
<plurals name="screen_pinned_timeline_screen_title">
|
||||
|
|
@ -495,12 +500,15 @@ Opravdu chcete pokračovat?"</string>
|
|||
<string name="screen_share_open_apple_maps">"Otevřít v Mapách Apple"</string>
|
||||
<string name="screen_share_open_google_maps">"Otevřít v Mapách Google"</string>
|
||||
<string name="screen_share_open_osm_maps">"Otevřít v OpenStreetMap"</string>
|
||||
<string name="screen_share_this_location_action">"Sdílet tuto polohu"</string>
|
||||
<string name="screen_share_this_location_action">"Sdílejte vybranou polohu"</string>
|
||||
<string name="screen_sharing_location_option_sheet_title">"Možnosti sdílení"</string>
|
||||
<string name="screen_space_list_description">"Prostory, které jste vytvořili nebo se k nim připojili."</string>
|
||||
<string name="screen_space_list_details">"%1$s • %2$s"</string>
|
||||
<string name="screen_space_list_empty_state_title">"Vytvořte prostory pro uspořádání místností"</string>
|
||||
<string name="screen_space_list_parent_space">"%1$s prostor"</string>
|
||||
<string name="screen_space_list_title">"Prostory"</string>
|
||||
<string name="screen_static_location_sheet_timestamp_description">"Sdíleno %1$s"</string>
|
||||
<string name="screen_static_location_sheet_title">"Na mapě"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Zpráva nebyla odeslána, protože ověřená identita uživatele %1$s se změnila."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_unsigned_device">"Zpráva nebyla odeslána, protože%1$s neověřil(a) všechna zařízení."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_you_unsigned_device">"Zpráva nebyla odeslána, protože jste neověřili jedno nebo více zařízení."</string>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<string name="a11y_pause">"Pausér"</string>
|
||||
<string name="a11y_paused_voice_message">"Talebesked, varighed: %1$s, aktuel position: %2$s"</string>
|
||||
<string name="a11y_pin_field">"PIN-felt"</string>
|
||||
<string name="a11y_pinned_location">"Fastgjort placering"</string>
|
||||
<string name="a11y_play">"Afspil"</string>
|
||||
<string name="a11y_playback_speed">"Afspilningshastighed"</string>
|
||||
<string name="a11y_poll">"Afstemning"</string>
|
||||
|
|
@ -45,9 +46,11 @@
|
|||
<string name="a11y_remove_reaction_with">"Fjern reaktion med %1$s"</string>
|
||||
<string name="a11y_room_avatar">"Avatar for rummet"</string>
|
||||
<string name="a11y_send_files">"Send filer"</string>
|
||||
<string name="a11y_sender_location">"Afsenderens placering"</string>
|
||||
<string name="a11y_session_verification_time_limited_action_required">"Tidsbegrænset handling påkrævet, du har et minut til at bekræfte"</string>
|
||||
<string name="a11y_show_password">"Vis adgangskode"</string>
|
||||
<string name="a11y_start_call">"Start et opkald"</string>
|
||||
<string name="a11y_start_voice_call">"Start et taleopkald"</string>
|
||||
<string name="a11y_tombstoned_room">"Deaktiveret rum"</string>
|
||||
<string name="a11y_user_avatar">"Avatar for bruger"</string>
|
||||
<string name="a11y_user_menu">"Brugermenu"</string>
|
||||
|
|
@ -79,7 +82,7 @@
|
|||
<string name="action_copy_text">"Kopiér tekst"</string>
|
||||
<string name="action_create">"Opret"</string>
|
||||
<string name="action_create_room">"Opret et rum"</string>
|
||||
<string name="action_create_space">"Opret en gruppe"</string>
|
||||
<string name="action_create_space">"Opret en klynge"</string>
|
||||
<string name="action_deactivate">"Deaktiver"</string>
|
||||
<string name="action_deactivate_account">"Deaktiver konto"</string>
|
||||
<string name="action_decline">"Afvis"</string>
|
||||
|
|
@ -96,7 +99,7 @@
|
|||
<string name="action_enable">"Slå til"</string>
|
||||
<string name="action_end_poll">"Afslut afstemning"</string>
|
||||
<string name="action_enter_pin">"Indtast PIN-kode"</string>
|
||||
<string name="action_explore_public_spaces">"Udforsk offentlige grupper"</string>
|
||||
<string name="action_explore_public_spaces">"Udforsk offentlige klynger"</string>
|
||||
<string name="action_finish">"Afslut"</string>
|
||||
<string name="action_forgot_password">"Har du glemt din adgangskode?"</string>
|
||||
<string name="action_forward">"Videresend"</string>
|
||||
|
|
@ -114,7 +117,7 @@
|
|||
<string name="action_leave">"Forlad"</string>
|
||||
<string name="action_leave_conversation">"Forlad samtalen"</string>
|
||||
<string name="action_leave_room">"Forlad rum"</string>
|
||||
<string name="action_leave_space">"Forlad gruppe"</string>
|
||||
<string name="action_leave_space">"Forlad klynge"</string>
|
||||
<string name="action_load_more">"Indlæs mere"</string>
|
||||
<string name="action_manage_account">"Administrer konto"</string>
|
||||
<string name="action_manage_devices">"Administrer enheder"</string>
|
||||
|
|
@ -156,6 +159,7 @@
|
|||
<string name="action_send_voice_message">"Send talebesked"</string>
|
||||
<string name="action_share">"Del"</string>
|
||||
<string name="action_share_link">"Del link"</string>
|
||||
<string name="action_share_live_location">"Del liveplacering"</string>
|
||||
<string name="action_show">"Vis"</string>
|
||||
<string name="action_sign_in_again">"Log ind igen"</string>
|
||||
<string name="action_signout">"Log ud"</string>
|
||||
|
|
@ -198,10 +202,10 @@
|
|||
<string name="common_copied_to_clipboard">"Kopieret til udklipsholder"</string>
|
||||
<string name="common_copyright">"Ophavsret"</string>
|
||||
<string name="common_creating_room">"Opretter rum…"</string>
|
||||
<string name="common_creating_space">"Opretter gruppe…"</string>
|
||||
<string name="common_creating_space">"Opretter klynge…"</string>
|
||||
<string name="common_current_user_canceled_knock">"Anmodning annulleret"</string>
|
||||
<string name="common_current_user_left_room">"Forlod rummet"</string>
|
||||
<string name="common_current_user_left_space">"Forlod gruppe"</string>
|
||||
<string name="common_current_user_left_space">"Forlod klynge"</string>
|
||||
<string name="common_current_user_rejected_invite">"Invitationen blev afvist"</string>
|
||||
<string name="common_dark">"Mørkt tema"</string>
|
||||
<string name="common_decryption_error">"Fejl under dekryptering"</string>
|
||||
|
|
@ -240,7 +244,7 @@
|
|||
<string name="common_install_apk_android">"Installer APK"</string>
|
||||
<string name="common_invite_unknown_profile">"Dette Matrix-ID kan ikke findes, så invitationen modtages muligvis ikke."</string>
|
||||
<string name="common_leaving_room">"Forlader rummet"</string>
|
||||
<string name="common_leaving_space">"Forlader gruppe"</string>
|
||||
<string name="common_leaving_space">"Forlader klynge"</string>
|
||||
<string name="common_light">"Lyst tema"</string>
|
||||
<string name="common_line_copied_to_clipboard">"Linje kopieret til udklipsholder"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Linket er kopieret til udklipsholderen"</string>
|
||||
|
|
@ -266,11 +270,12 @@
|
|||
<string name="common_name_and_id">"%1$s (%2$s)"</string>
|
||||
<string name="common_no_results">"Ingen resultater"</string>
|
||||
<string name="common_no_room_name">"Intet rumnavn"</string>
|
||||
<string name="common_no_space_name">"Intet gruppenavn"</string>
|
||||
<string name="common_no_space_name">"Intet klyngenavn"</string>
|
||||
<string name="common_not_encrypted">"Ikke krypteret"</string>
|
||||
<string name="common_offline">"Offline"</string>
|
||||
<string name="common_open_source_licenses">"Open Source-licenser"</string>
|
||||
<string name="common_or">"eller"</string>
|
||||
<string name="common_other_options">"Andre indstillinger"</string>
|
||||
<string name="common_password">"Adgangskode"</string>
|
||||
<string name="common_people">"Brugere"</string>
|
||||
<string name="common_permalink">"Permalink"</string>
|
||||
|
|
@ -290,10 +295,10 @@
|
|||
<string name="common_privacy_policy">"Privatlivspolitik"</string>
|
||||
<string name="common_private">"Privat"</string>
|
||||
<string name="common_private_room">"Privat rum"</string>
|
||||
<string name="common_private_space">"Privat gruppe"</string>
|
||||
<string name="common_private_space">"Privat klynge"</string>
|
||||
<string name="common_public">"Offentlig"</string>
|
||||
<string name="common_public_room">"Offentligt rum"</string>
|
||||
<string name="common_public_space">"Offentlig gruppe"</string>
|
||||
<string name="common_public_space">"Offentlig klynge"</string>
|
||||
<string name="common_reaction">"Reaktion"</string>
|
||||
<string name="common_reactions">"Reaktioner"</string>
|
||||
<string name="common_reason">"Årsag"</string>
|
||||
|
|
@ -337,19 +342,19 @@
|
|||
<string name="common_server_unreachable">"Serveren er ikke tilgængelig"</string>
|
||||
<string name="common_server_url">"Server URL"</string>
|
||||
<string name="common_settings">"Indstillinger"</string>
|
||||
<string name="common_share_space">"Del gruppe"</string>
|
||||
<string name="common_share_space">"Del klynge"</string>
|
||||
<string name="common_shared_history">"Nye medlemmer ser historik"</string>
|
||||
<string name="common_shared_location">"Delt placering"</string>
|
||||
<string name="common_shared_space">"Delt gruppe"</string>
|
||||
<string name="common_shared_space">"Delt klynge"</string>
|
||||
<string name="common_signing_out">"Logger ud"</string>
|
||||
<string name="common_something_went_wrong">"Noget gik galt"</string>
|
||||
<string name="common_something_went_wrong_message">"Vi stødte på et problem. Prøv venligst igen."</string>
|
||||
<string name="common_space">"Gruppe"</string>
|
||||
<string name="common_space">"Klynge"</string>
|
||||
<string name="common_space_members">"Medlemmer af rummet"</string>
|
||||
<string name="common_space_topic_placeholder">"Hvad handler denne gruppe om?"</string>
|
||||
<string name="common_space_topic_placeholder">"Hvad handler denne klynge om?"</string>
|
||||
<plurals name="common_spaces">
|
||||
<item quantity="one">"%1$d Gruppe"</item>
|
||||
<item quantity="other">"%1$d Grupper"</item>
|
||||
<item quantity="one">"%1$d Klynge"</item>
|
||||
<item quantity="other">"%1$d Klynger"</item>
|
||||
</plurals>
|
||||
<string name="common_starting_chat">"Starter samtale…"</string>
|
||||
<string name="common_sticker">"Klistermærke"</string>
|
||||
|
|
@ -402,6 +407,7 @@
|
|||
<string name="crypto_identity_change_profile_pin_violation">"%1$ss identitet blev nulstillet."</string>
|
||||
<string name="crypto_identity_change_verification_violation_new">"%1$ss %2$s identitet blev nulstillet. %3$s"</string>
|
||||
<string name="crypto_identity_change_withdraw_verification_action">"Tilbagetræk verifikation"</string>
|
||||
<string name="dialog_allow_access">"Tillad adgang"</string>
|
||||
<string name="dialog_confirm_link_message">"Linket %1$s fører dig til et andet websted %2$s
|
||||
|
||||
Er du sikker på, at du vil fortsætte?"</string>
|
||||
|
|
@ -450,6 +456,7 @@ Er du sikker på, at du vil fortsætte?"</string>
|
|||
<string name="screen_create_poll_remove_accessibility_label">"Fjern %1$s"</string>
|
||||
<string name="screen_create_poll_settings_section_title">"Indstillinger"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Det lykkedes ikke at vælge medie. Prøv igen."</string>
|
||||
<string name="screen_onboarding_welcome_back">"Velkommen tilbage"</string>
|
||||
<string name="screen_pinned_timeline_empty_state_description">"Tryk på en besked og vælg \"%1$s\" for at inkludere den her."</string>
|
||||
<string name="screen_pinned_timeline_empty_state_headline">"Fastgør vigtige beskeder, så de let kan opdages"</string>
|
||||
<plurals name="screen_pinned_timeline_screen_title">
|
||||
|
|
@ -484,12 +491,15 @@ Er du sikker på, at du vil fortsætte?"</string>
|
|||
<string name="screen_share_open_apple_maps">"Åbn i Apple Maps"</string>
|
||||
<string name="screen_share_open_google_maps">"Åbn i Google Maps"</string>
|
||||
<string name="screen_share_open_osm_maps">"Åbn i OpenStreetMap"</string>
|
||||
<string name="screen_share_this_location_action">"Del denne lokation"</string>
|
||||
<string name="screen_space_list_description">"Grupper, du har oprettet eller deltager i"</string>
|
||||
<string name="screen_share_this_location_action">"Del den valgte placering"</string>
|
||||
<string name="screen_sharing_location_option_sheet_title">"Indstillinger for deling"</string>
|
||||
<string name="screen_space_list_description">"Klynger, du har oprettet eller deltager i"</string>
|
||||
<string name="screen_space_list_details">"%1$s•%2$s"</string>
|
||||
<string name="screen_space_list_empty_state_title">"Opret grupper til at organisere rum"</string>
|
||||
<string name="screen_space_list_parent_space">"%1$s gruppe"</string>
|
||||
<string name="screen_space_list_title">"Grupper"</string>
|
||||
<string name="screen_space_list_empty_state_title">"Opret klynger til at organisere rum i"</string>
|
||||
<string name="screen_space_list_parent_space">"%1$s klynge"</string>
|
||||
<string name="screen_space_list_title">"Klynger"</string>
|
||||
<string name="screen_static_location_sheet_timestamp_description">"Delt %1$s"</string>
|
||||
<string name="screen_static_location_sheet_title">"På kortet"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Beskeden blev ikke sendt fordi %1$s s bekræftede identitet blev nulstillet."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_unsigned_device">"Meddelelsen er ikke sendt, fordi %1$s ikke har bekræftet alle enheder."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_you_unsigned_device">"Beskeden er ikke sendt, fordi du ikke har verificeret en eller flere af dine enheder."</string>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<string name="a11y_pause">"Παύση"</string>
|
||||
<string name="a11y_paused_voice_message">"Φωνητικό μήνυμα, διάρκεια:%1$s, τρέχουσα θέση: %2$s"</string>
|
||||
<string name="a11y_pin_field">"Πεδίο PIN"</string>
|
||||
<string name="a11y_pinned_location">"Καρφιτσωμένη τοποθεσία"</string>
|
||||
<string name="a11y_play">"Αναπαραγωγή"</string>
|
||||
<string name="a11y_playback_speed">"Ταχύτητα αναπαραγωγής"</string>
|
||||
<string name="a11y_poll">"Δημοσκόπηση"</string>
|
||||
|
|
@ -45,9 +46,11 @@
|
|||
<string name="a11y_remove_reaction_with">"Αφαιρέστε την αντίδραση με %1$s"</string>
|
||||
<string name="a11y_room_avatar">"Άβαταρ αίθουσας"</string>
|
||||
<string name="a11y_send_files">"Αποστολή αρχείων"</string>
|
||||
<string name="a11y_sender_location">"Τοποθεσία αποστολέα"</string>
|
||||
<string name="a11y_session_verification_time_limited_action_required">"Απαιτείται ενέργεια περιορισμένης χρονικής διάρκειας, έχετε ένα λεπτό για επαλήθευση."</string>
|
||||
<string name="a11y_show_password">"Εμφάνιση κωδικού πρόσβασης"</string>
|
||||
<string name="a11y_start_call">"Ξεκίνησε μια κλήση"</string>
|
||||
<string name="a11y_start_voice_call">"Έναρξη φωνητικής κλήσης"</string>
|
||||
<string name="a11y_tombstoned_room">"Θαμένη αίθουσα"</string>
|
||||
<string name="a11y_user_avatar">"Άβαταρ χρήστη"</string>
|
||||
<string name="a11y_user_menu">"Μενού χρήστη"</string>
|
||||
|
|
@ -156,10 +159,11 @@
|
|||
<string name="action_send_voice_message">"Αποστολή φωνητικού μηνύματος"</string>
|
||||
<string name="action_share">"Κοινή χρήση"</string>
|
||||
<string name="action_share_link">"Κοινή χρήση συνδέσμου"</string>
|
||||
<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>
|
||||
|
|
@ -186,6 +190,7 @@
|
|||
<string name="common_advanced_settings">"Ρυθμίσεις για προχωρημένους"</string>
|
||||
<string name="common_an_image">"μια εικόνα"</string>
|
||||
<string name="common_analytics">"Στατιστικά στοιχεία"</string>
|
||||
<string name="common_android_fetching_notifications_title">"Συγχρονισμός ειδοποιήσεων…"</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>
|
||||
|
|
@ -271,6 +276,7 @@
|
|||
<string name="common_offline">"Εκτός σύνδεσης"</string>
|
||||
<string name="common_open_source_licenses">"Άδειες ανοιχτού κώδικα"</string>
|
||||
<string name="common_or">"ή"</string>
|
||||
<string name="common_other_options">"Άλλες επιλογές"</string>
|
||||
<string name="common_password">"Κωδικός πρόσβασης"</string>
|
||||
<string name="common_people">"Άτομα"</string>
|
||||
<string name="common_permalink">"Μόνιμος σύνδεσμος"</string>
|
||||
|
|
@ -342,7 +348,7 @@
|
|||
<string name="common_shared_history">"Τα νέα μέλη βλέπουν το ιστορικό"</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>
|
||||
|
|
@ -367,7 +373,7 @@
|
|||
<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>
|
||||
|
|
@ -397,11 +403,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
|
||||
|
|
@ -433,6 +439,7 @@
|
|||
<string name="error_failed_locating_user">"Το %1$s δεν μπόρεσε να αποκτήσει πρόσβαση στην τοποθεσία σου. Προσπάθησε ξανά αργότερα."</string>
|
||||
<string name="error_failed_uploading_voice_message">"Αποτυχία μεταφόρτωσης του φωνητικού σου μηνύματος."</string>
|
||||
<string name="error_invalid_invite">"Η αίθουσα δεν υπάρχει πλέον ή η πρόσκληση δεν ισχύει πλέον."</string>
|
||||
<string name="error_location_service_disabled_android">"Ενεργοποιήστε το GPS σας για να έχετε πρόσβαση σε λειτουργίες που βασίζονται στην τοποθεσία."</string>
|
||||
<string name="error_message_not_found">"Το μήνυμα δεν βρέθηκε"</string>
|
||||
<string name="error_missing_location_auth_android">"Το %1$s δεν έχει άδεια πρόσβασης στην τοποθεσία σου. Μπορείς να ενεργοποιήσεις την πρόσβαση στις Ρυθμίσεις."</string>
|
||||
<string name="error_missing_location_rationale_android">"Ο χρήστης %1$s δεν έχει άδεια πρόσβασης στην τοποθεσία σου. Ενεργοποίησε την πρόσβαση παρακάτω."</string>
|
||||
|
|
@ -452,6 +459,7 @@
|
|||
<string name="screen_create_poll_remove_accessibility_label">"Αφαίρεση %1$s"</string>
|
||||
<string name="screen_create_poll_settings_section_title">"Ρυθμίσεις"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Αποτυχία επιλογής πολυμέσου, δοκίμασε ξανά."</string>
|
||||
<string name="screen_onboarding_welcome_back">"Καλώς ήρθατε ξανά"</string>
|
||||
<string name="screen_pinned_timeline_empty_state_description">"Πάτα σε ένα μήνυμα και επέλεξε «%1$s» για να συμπεριληφθεί εδώ."</string>
|
||||
<string name="screen_pinned_timeline_empty_state_headline">"Καρφίτσωσε σημαντικά μηνύματα, ώστε να μπορούν να εντοπιστούν εύκολα"</string>
|
||||
<plurals name="screen_pinned_timeline_screen_title">
|
||||
|
|
@ -460,10 +468,10 @@
|
|||
</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_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>
|
||||
|
|
@ -486,13 +494,16 @@
|
|||
<string name="screen_share_open_apple_maps">"Άνοιγμα στο Apple Maps"</string>
|
||||
<string name="screen_share_open_google_maps">"Άνοιγμα στο Google Maps"</string>
|
||||
<string name="screen_share_open_osm_maps">"Άνοιγμα στο OpenStreetMap"</string>
|
||||
<string name="screen_share_this_location_action">"Κοινή χρήση αυτής της τοποθεσίας"</string>
|
||||
<string name="screen_share_this_location_action">"Κοινοποίηση επιλεγμένης τοποθεσίας"</string>
|
||||
<string name="screen_sharing_location_option_sheet_title">"Επιλογές κοινοποίησης"</string>
|
||||
<string name="screen_space_list_description">"Χώροι που έχετε δημιουργήσει ή στους οποίους έχετε συμμετάσχει."</string>
|
||||
<string name="screen_space_list_details">"%1$s • %2$s"</string>
|
||||
<string name="screen_space_list_empty_state_title">"Δημιουργήστε χώρους για να οργανώσετε αίθουσες"</string>
|
||||
<string name="screen_space_list_parent_space">"%1$s χώρος"</string>
|
||||
<string name="screen_space_list_title">"Χώροι"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Το μήνυμα δεν στάλθηκε γιατί έγινε επαναφορά της επαληθευμένης ταυτότητας του χρήστη %1$s."</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>
|
||||
<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_view_location_title">"Τοποθεσία"</string>
|
||||
|
|
@ -502,5 +513,5 @@
|
|||
<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>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<string name="a11y_pause">"Keskeytä"</string>
|
||||
<string name="a11y_paused_voice_message">"Ääniviesti, kesto: %1$s, nykyinen sijainti: %2$s"</string>
|
||||
<string name="a11y_pin_field">"PIN-kenttä"</string>
|
||||
<string name="a11y_pinned_location">"Kiinnitetty sijainti"</string>
|
||||
<string name="a11y_play">"Toista"</string>
|
||||
<string name="a11y_playback_speed">"Toistonopeus"</string>
|
||||
<string name="a11y_poll">"Kysely"</string>
|
||||
|
|
@ -45,9 +46,11 @@
|
|||
<string name="a11y_remove_reaction_with">"Poista reaktio: %1$s"</string>
|
||||
<string name="a11y_room_avatar">"Huoneen avatar"</string>
|
||||
<string name="a11y_send_files">"Lähetä tiedostoja"</string>
|
||||
<string name="a11y_sender_location">"Lähettäjän sijainti"</string>
|
||||
<string name="a11y_session_verification_time_limited_action_required">"Aikarajoitettu toimenpide vaaditaan, sinulla on yksi minuutti aikaa vahvistaa"</string>
|
||||
<string name="a11y_show_password">"Näytä salasana"</string>
|
||||
<string name="a11y_start_call">"Aloita puhelu"</string>
|
||||
<string name="a11y_start_voice_call">"Aloita äänipuhelu"</string>
|
||||
<string name="a11y_tombstoned_room">"Haudattu huone"</string>
|
||||
<string name="a11y_user_avatar">"Käyttäjän avatar"</string>
|
||||
<string name="a11y_user_menu">"Käyttäjävalikko"</string>
|
||||
|
|
@ -117,6 +120,7 @@
|
|||
<string name="action_leave_space">"Poistu tilasta"</string>
|
||||
<string name="action_load_more">"Lataa lisää"</string>
|
||||
<string name="action_manage_account">"Hallitse tiliä"</string>
|
||||
<string name="action_manage_account_and_devices">"Hallitse tiliä ja laitteita"</string>
|
||||
<string name="action_manage_devices">"Hallitse laitteita"</string>
|
||||
<string name="action_manage_rooms">"Huoneiden hallitseminen"</string>
|
||||
<string name="action_message">"Lähetä viesti"</string>
|
||||
|
|
@ -159,14 +163,15 @@
|
|||
<string name="action_share_live_location">"Jaa reaaliaikainen sijainti"</string>
|
||||
<string name="action_show">"Näytä"</string>
|
||||
<string name="action_sign_in_again">"Kirjaudu uudelleen"</string>
|
||||
<string name="action_signout">"Kirjaudu ulos"</string>
|
||||
<string name="action_signout_anyway">"Kirjaudu ulos silti"</string>
|
||||
<string name="action_signout">"Poista tämä laite"</string>
|
||||
<string name="action_signout_anyway">"Poista tämä laite silti"</string>
|
||||
<string name="action_skip">"Ohita"</string>
|
||||
<string name="action_start">"Aloita"</string>
|
||||
<string name="action_start_chat">"Aloita keskustelu"</string>
|
||||
<string name="action_start_over">"Aloita alusta"</string>
|
||||
<string name="action_start_verification">"Aloita vahvistus"</string>
|
||||
<string name="action_static_map_load">"Lataa kartta napauttamalla"</string>
|
||||
<string name="action_stop">"Lopeta"</string>
|
||||
<string name="action_take_photo">"Ota kuva"</string>
|
||||
<string name="action_tap_for_options">"Näytä vaihtoehdot napauttamalla"</string>
|
||||
<string name="action_translate">"Käännä"</string>
|
||||
|
|
@ -187,6 +192,7 @@
|
|||
<string name="common_advanced_settings">"Edistyneet asetukset"</string>
|
||||
<string name="common_an_image">"kuva"</string>
|
||||
<string name="common_analytics">"Analytiikka"</string>
|
||||
<string name="common_android_fetching_notifications_title">"Synkronoidaan ilmoituksia…"</string>
|
||||
<string name="common_android_shortcuts_remove_reason_left_room">"Poistuit huoneesta"</string>
|
||||
<string name="common_android_shortcuts_remove_reason_session_logged_out">"Sinut kirjattiin ulos istunnosta"</string>
|
||||
<string name="common_appearance">"Ulkoasu"</string>
|
||||
|
|
@ -220,6 +226,7 @@
|
|||
<string name="common_empty_file">"Tyhjä tiedosto"</string>
|
||||
<string name="common_encryption">"Salaus"</string>
|
||||
<string name="common_encryption_enabled">"Salaus käytössä"</string>
|
||||
<string name="common_ends_at">"Päättyy klo %1$s"</string>
|
||||
<string name="common_enter_your_pin">"Anna PIN-koodisi"</string>
|
||||
<string name="common_error">"Virhe"</string>
|
||||
<string name="common_error_registering_pusher_android">"Tapahtui virhe. Et välttämättä saa ilmoituksia uusista viesteistä. Tee ilmoitusten vianmääritys asetuksista.
|
||||
|
|
@ -246,6 +253,8 @@ Syy: %1$s."</string>
|
|||
<string name="common_line_copied_to_clipboard">"Rivi kopioitu leikepöydälle"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Linkki kopioitu leikepöydälle"</string>
|
||||
<string name="common_link_new_device">"Yhdistä uusi laite"</string>
|
||||
<string name="common_live_location">"Reaaliaikainen sijainti"</string>
|
||||
<string name="common_live_location_ended">"Reaaliaikainen sijainti päättyi"</string>
|
||||
<string name="common_loading">"Ladataan…"</string>
|
||||
<string name="common_loading_more">"Ladataan lisää…"</string>
|
||||
<plurals name="common_many_members">
|
||||
|
|
@ -342,9 +351,10 @@ Syy: %1$s."</string>
|
|||
<string name="common_settings">"Asetukset"</string>
|
||||
<string name="common_share_space">"Jaa tila"</string>
|
||||
<string name="common_shared_history">"Uudet jäsenet näkevät historian"</string>
|
||||
<string name="common_shared_live_location">"Jaettu reaaliaikainen sijainti"</string>
|
||||
<string name="common_shared_location">"Jaettu sijainti"</string>
|
||||
<string name="common_shared_space">"Jaettu tila"</string>
|
||||
<string name="common_signing_out">"Kirjaudutaan ulos"</string>
|
||||
<string name="common_signing_out">"Poistetaan laitetta"</string>
|
||||
<string name="common_something_went_wrong">"Jokin meni pieleen"</string>
|
||||
<string name="common_something_went_wrong_message">"Kohtasimme ongelman. Yritä uudelleen."</string>
|
||||
<string name="common_space">"Tila"</string>
|
||||
|
|
@ -369,7 +379,7 @@ Syy: %1$s."</string>
|
|||
<string name="common_unable_to_decrypt">"Salauksen purkaminen ei onnistunut"</string>
|
||||
<string name="common_unable_to_decrypt_insecure_device">"Lähetetty suojaamattomasta laitteesta"</string>
|
||||
<string name="common_unable_to_decrypt_no_access">"Sinulla ei ole oikeutta lukea tätä viestiä"</string>
|
||||
<string name="common_unable_to_decrypt_verification_violation">"Lähettäjän vahvistettu identiteetti nollattiin"</string>
|
||||
<string name="common_unable_to_decrypt_verification_violation">"Lähettäjän vahvistettu digitaalinen identiteetti nollattiin"</string>
|
||||
<string name="common_unable_to_invite_message">"Kutsujen ei voitu lähettää yhdelle tai useammalle käyttäjälle."</string>
|
||||
<string name="common_unable_to_invite_title">"Kutsujen lähettäminen ei onnistunut"</string>
|
||||
<string name="common_unlock">"Avaa"</string>
|
||||
|
|
@ -394,16 +404,17 @@ Syy: %1$s."</string>
|
|||
<string name="common_voice_message">"Ääniviesti"</string>
|
||||
<string name="common_waiting">"Odotetaan…"</string>
|
||||
<string name="common_waiting_for_decryption_key">"Odotetaan viestiä"</string>
|
||||
<string name="common_waiting_live_location">"Odotetaan reaaliaikaista sijaintia…"</string>
|
||||
<string name="common_world_readable_history">"Kuka tahansa voi nähdä historian"</string>
|
||||
<string name="common_you">"Sinä"</string>
|
||||
<string name="crypto_event_key_forwarded_known_profile_dialog_content">"%1$s (%2$s) jakoi tämän viestin, koska et ollut huoneessa, kun se lähetettiin."</string>
|
||||
<string name="crypto_event_key_forwarded_unknown_profile_dialog_content">"%1$s jakoi tämän viestin, koska et ollut huoneessa, kun se lähetettiin."</string>
|
||||
<string name="crypto_history_visible">"Tämä huone on määritetty niin, että uudet jäsenet voivat lukea historiaa. %1$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation">"Käyttäjän %1$s identiteetti nollattiin. %2$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation_new">"Käyttäjän %1$s %2$s identiteetti nollattiin. %3$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation">"Käyttäjän %1$s digitaalinen identiteetti nollattiin. %2$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation_new">"Käyttäjän %1$s %2$s digitaalinen identiteetti nollattiin. %3$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation_new_user_id">"(%1$s)"</string>
|
||||
<string name="crypto_identity_change_profile_pin_violation">"Käyttäjän %1$s identiteetti nollattiin."</string>
|
||||
<string name="crypto_identity_change_verification_violation_new">"Käyttäjän %1$s %2$s identiteetti nollattiin. %3$s"</string>
|
||||
<string name="crypto_identity_change_profile_pin_violation">"Käyttäjän %1$s digitaalinen identiteetti nollattiin."</string>
|
||||
<string name="crypto_identity_change_verification_violation_new">"Käyttäjän %1$s %2$s digitaalinen identiteetti nollattiin. %3$s"</string>
|
||||
<string name="crypto_identity_change_withdraw_verification_action">"Peruuta vahvistus"</string>
|
||||
<string name="dialog_allow_access">"Anna käyttölupa"</string>
|
||||
<string name="dialog_confirm_link_message">"Linkki %1$s on viemässä sinua toiselle sivustolle %2$s
|
||||
|
|
@ -435,6 +446,7 @@ Haluatko varmasti jatkaa?"</string>
|
|||
<string name="error_failed_locating_user">"%1$s ei päässyt käsiksi sijaintiisi. Yritä myöhemmin uudelleen."</string>
|
||||
<string name="error_failed_uploading_voice_message">"Ääniviestin lähettäminen epäonnistui."</string>
|
||||
<string name="error_invalid_invite">"Huone ei ole enää olemassa tai kutsu ei ole enää voimassa."</string>
|
||||
<string name="error_location_service_disabled_android">"Ota GPS käyttöön, jotta voit käyttää sijaintiin perustuvia ominaisuuksia."</string>
|
||||
<string name="error_message_not_found">"Viestiä ei löytynyt"</string>
|
||||
<string name="error_missing_location_auth_android">"%1$s -sovelluksella ei ole lupaa sijaintiisi. Voit sallia sen asetuksista."</string>
|
||||
<string name="error_missing_location_rationale_android">"%1$s -sovelluksella ei ole lupaa sijaintiisi. Voit sallia sen painamalla alla olevaa nappia."</string>
|
||||
|
|
@ -462,11 +474,11 @@ Haluatko varmasti jatkaa?"</string>
|
|||
<item quantity="other">"%1$d kiinnitettyä viestiä"</item>
|
||||
</plurals>
|
||||
<string name="screen_pinned_timeline_screen_title_empty">"Kiinnitetyt viestit"</string>
|
||||
<string name="screen_reset_identity_confirmation_subtitle">"Olet siirtymässä %1$s -tilillesi nollaamaan identiteettisi. Tämän jälkeen sinut ohjataan takaisin sovellukseen."</string>
|
||||
<string name="screen_reset_identity_confirmation_title">"Etkö voi vahvistaa? Siirry tilillesi ja nollaa identiteettisi."</string>
|
||||
<string name="screen_reset_identity_confirmation_subtitle">"Olet siirtymässä %1$s -tilillesi nollaamaan digitaalisen identiteettisi. Tämän jälkeen sinut ohjataan takaisin sovellukseen."</string>
|
||||
<string name="screen_reset_identity_confirmation_title">"Etkö voi vahvistaa? Siirry tilillesi ja nollaa digitaalinen identiteettisi."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_primary_button_title">"Peruuta vahvistus ja lähetä"</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_subtitle">"Voit peruuttaa vahvistuksen ja lähettää tämän viestin silti, tai voit peruuttaa viestin lähettämisen toistaiseksi ja yrittää uudelleen myöhemmin, kun olet vahvistanut käyttäjän %1$s uudelleen."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_title">"Viestiäsi ei lähetetty, koska käyttäjän %1$s vahvistettu identiteetti nollattiin"</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_title">"Viestiäsi ei lähetetty, koska käyttäjän %1$s vahvistettu digitaalinen identiteetti nollattiin"</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_primary_button_title">"Lähetä viesti silti"</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_subtitle">"%1$s käyttää yhtä tai useampaa vahvistamatonta laitetta. Voit lähettää viestin silti tai voit peruuttaa sen toistaiseksi ja yrittää myöhemmin uudelleen, kun %2$s on vahvistanut kaikki laitteensa."</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_title">"Viestiäsi ei lähetetty, koska %1$s ei ole vahvistanut kaikkia laitteitaan."</string>
|
||||
|
|
@ -484,18 +496,22 @@ Haluatko varmasti jatkaa?"</string>
|
|||
<string name="screen_room_pinned_banner_loading_description">"Viestiä ladataan…"</string>
|
||||
<string name="screen_room_pinned_banner_view_all_button_title">"Näytä kaikki"</string>
|
||||
<string name="screen_room_title">"Keskustelu"</string>
|
||||
<string name="screen_share_location_live_location_duration_picker_title">"Valitse, kuinka kauan haluat jakaa reaaliaikaisen sijaintisi."</string>
|
||||
<string name="screen_share_location_title">"Jaa sijainti"</string>
|
||||
<string name="screen_share_my_location_action">"Jaa sijaintini"</string>
|
||||
<string name="screen_share_open_apple_maps">"Avaa Apple Mapsissa"</string>
|
||||
<string name="screen_share_open_google_maps">"Avaa Google Mapsissa"</string>
|
||||
<string name="screen_share_open_osm_maps">"Avaa OpenStreetMapissa"</string>
|
||||
<string name="screen_share_this_location_action">"Jaa tämä sijainti"</string>
|
||||
<string name="screen_share_this_location_action">"Jaa valittu sijainti"</string>
|
||||
<string name="screen_sharing_location_option_sheet_title">"Jakamisasetukset"</string>
|
||||
<string name="screen_space_list_description">"Luomasi tai liittymäsi tilat."</string>
|
||||
<string name="screen_space_list_details">"%1$s • %2$s"</string>
|
||||
<string name="screen_space_list_empty_state_title">"Luo tiloja huoneiden järjestämiseksi"</string>
|
||||
<string name="screen_space_list_parent_space">"%1$s tila"</string>
|
||||
<string name="screen_space_list_title">"Tilat"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Viestiä ei lähetetty, koska käyttäjän %1$s vahvistettu identiteetti nollattiin."</string>
|
||||
<string name="screen_static_location_sheet_timestamp_description">"Jaettu %1$s"</string>
|
||||
<string name="screen_static_location_sheet_title">"Kartalla"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Viestiä ei lähetetty, koska käyttäjän %1$s vahvistettu digitaalinen identiteetti nollattiin."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_unsigned_device">"Viestiä ei lähetetty, koska %1$s ei ole vahvistanut kaikkia laitteitaan."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_you_unsigned_device">"Viestiä ei lähetetty, koska et ole vahvistanut yhtä tai useampaa laitettasi."</string>
|
||||
<string name="screen_view_location_title">"Sijainti"</string>
|
||||
|
|
@ -505,5 +521,5 @@ Haluatko varmasti jatkaa?"</string>
|
|||
<string name="timeline_decryption_failure_historical_event_unverified_device">"Sinun on vahvistettava tämä laite, jotta pääset käsiksi viestihistoriaan."</string>
|
||||
<string name="timeline_decryption_failure_historical_event_user_not_joined">"Sinulla ei ole oikeutta lukea tätä viestiä"</string>
|
||||
<string name="timeline_decryption_failure_unable_to_decrypt">"Viestin salauksen purkaminen ei onnistu"</string>
|
||||
<string name="timeline_decryption_failure_withheld_unverified">"Tämä viesti estettiin, koska laitettasi ei ole vahvistettu tai koska lähettäjän on vahvistettava identiteettisi."</string>
|
||||
<string name="timeline_decryption_failure_withheld_unverified">"Tämä viesti estettiin, koska et ole vahvistanut laitettasi tai koska lähettäjän on vahvistettava diigitaalinen identiteettisi."</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<string name="a11y_pause">"Pause"</string>
|
||||
<string name="a11y_paused_voice_message">"Message vocal, durée: %1$s, position actuelle: %2$s"</string>
|
||||
<string name="a11y_pin_field">"Code PIN"</string>
|
||||
<string name="a11y_pinned_location">"Position épinglée"</string>
|
||||
<string name="a11y_play">"Lecture"</string>
|
||||
<string name="a11y_playback_speed">"Vitesse de lecture"</string>
|
||||
<string name="a11y_poll">"Sondage"</string>
|
||||
|
|
@ -45,9 +46,11 @@
|
|||
<string name="a11y_remove_reaction_with">"Supprimer la réaction avec %1$s"</string>
|
||||
<string name="a11y_room_avatar">"Avatar du salon"</string>
|
||||
<string name="a11y_send_files">"Envoyer des fichiers"</string>
|
||||
<string name="a11y_sender_location">"Position de l’expéditeur"</string>
|
||||
<string name="a11y_session_verification_time_limited_action_required">"Action limitée dans le temps requise, vous avez une minute pour effectuer la vérification"</string>
|
||||
<string name="a11y_show_password">"Afficher le mot de passe"</string>
|
||||
<string name="a11y_start_call">"Démarrer un appel"</string>
|
||||
<string name="a11y_start_voice_call">"Lancer un appel vocal"</string>
|
||||
<string name="a11y_tombstoned_room">"Salon clôturé"</string>
|
||||
<string name="a11y_user_avatar">"Avatar de l’utilisateur"</string>
|
||||
<string name="a11y_user_menu">"Menu utilisateur"</string>
|
||||
|
|
@ -117,6 +120,7 @@
|
|||
<string name="action_leave_space">"Quitter l’espace"</string>
|
||||
<string name="action_load_more">"Voir plus"</string>
|
||||
<string name="action_manage_account">"Gérer le compte"</string>
|
||||
<string name="action_manage_account_and_devices">"Gérer le compte et les appareils"</string>
|
||||
<string name="action_manage_devices">"Gérez les sessions"</string>
|
||||
<string name="action_manage_rooms">"Gérer les salons"</string>
|
||||
<string name="action_message">"Message"</string>
|
||||
|
|
@ -159,14 +163,15 @@
|
|||
<string name="action_share_live_location">"Partager la position en continu"</string>
|
||||
<string name="action_show">"Afficher"</string>
|
||||
<string name="action_sign_in_again">"Se connecter à nouveau"</string>
|
||||
<string name="action_signout">"Se déconnecter"</string>
|
||||
<string name="action_signout_anyway">"Se déconnecter quand même"</string>
|
||||
<string name="action_signout">"Supprimer cet appareil"</string>
|
||||
<string name="action_signout_anyway">"Supprimer cet appareil quand même"</string>
|
||||
<string name="action_skip">"Passer"</string>
|
||||
<string name="action_start">"Démarrer"</string>
|
||||
<string name="action_start_chat">"Démarrer une discussion"</string>
|
||||
<string name="action_start_over">"Recommencer"</string>
|
||||
<string name="action_start_verification">"Commencer la vérification"</string>
|
||||
<string name="action_static_map_load">"Cliquez pour charger la carte"</string>
|
||||
<string name="action_stop">"Arrêter"</string>
|
||||
<string name="action_take_photo">"Prendre une photo"</string>
|
||||
<string name="action_tap_for_options">"Appuyez pour afficher les options"</string>
|
||||
<string name="action_translate">"Traduire"</string>
|
||||
|
|
@ -187,6 +192,7 @@
|
|||
<string name="common_advanced_settings">"Paramètres avancés"</string>
|
||||
<string name="common_an_image">"une image"</string>
|
||||
<string name="common_analytics">"Statistiques d’utilisation"</string>
|
||||
<string name="common_android_fetching_notifications_title">"Synchronisation des notifications…"</string>
|
||||
<string name="common_android_shortcuts_remove_reason_left_room">"Vous avez quitté le salon"</string>
|
||||
<string name="common_android_shortcuts_remove_reason_session_logged_out">"Vous avez été déconnecté de la session"</string>
|
||||
<string name="common_appearance">"Apparence"</string>
|
||||
|
|
@ -220,6 +226,7 @@
|
|||
<string name="common_empty_file">"Fichier vide"</string>
|
||||
<string name="common_encryption">"Chiffrement"</string>
|
||||
<string name="common_encryption_enabled">"Chiffrement activé"</string>
|
||||
<string name="common_ends_at">"Se termine à %1$s"</string>
|
||||
<string name="common_enter_your_pin">"Saisissez votre code PIN"</string>
|
||||
<string name="common_error">"Erreur"</string>
|
||||
<string name="common_error_registering_pusher_android">"Une erreur s’est produite, il est possible que vous ne receviez pas de notifications pour les nouveaux messages. Veuillez résoudre les problèmes liés aux notifications depuis les paramètres.
|
||||
|
|
@ -246,6 +253,8 @@ Raison : %1$s."</string>
|
|||
<string name="common_line_copied_to_clipboard">"Ligne copiée dans le presse-papiers"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Lien copié dans le presse-papiers"</string>
|
||||
<string name="common_link_new_device">"Associer un nouvel appareil"</string>
|
||||
<string name="common_live_location">"Position en direct"</string>
|
||||
<string name="common_live_location_ended">"Fin de localisation en direct"</string>
|
||||
<string name="common_loading">"Chargement…"</string>
|
||||
<string name="common_loading_more">"Chargement…"</string>
|
||||
<plurals name="common_many_members">
|
||||
|
|
@ -342,9 +351,10 @@ Raison : %1$s."</string>
|
|||
<string name="common_settings">"Paramètres"</string>
|
||||
<string name="common_share_space">"Partager l’espace"</string>
|
||||
<string name="common_shared_history">"Les nouveaux membres voient les anciens messages"</string>
|
||||
<string name="common_shared_live_location">"Position en direct partagée"</string>
|
||||
<string name="common_shared_location">"Position partagée"</string>
|
||||
<string name="common_shared_space">"Espace partagé"</string>
|
||||
<string name="common_signing_out">"Déconnexion"</string>
|
||||
<string name="common_signing_out">"Suppression de l’appareil"</string>
|
||||
<string name="common_something_went_wrong">"Une erreur s’est produite"</string>
|
||||
<string name="common_something_went_wrong_message">"Nous avons rencontré un problème. Veuillez réessayer."</string>
|
||||
<string name="common_space">"Espace"</string>
|
||||
|
|
@ -369,7 +379,7 @@ Raison : %1$s."</string>
|
|||
<string name="common_unable_to_decrypt">"Échec de déchiffrement"</string>
|
||||
<string name="common_unable_to_decrypt_insecure_device">"Envoyé depuis un appareil non sécurisé"</string>
|
||||
<string name="common_unable_to_decrypt_no_access">"Vous ne pouvez pas voir ce message"</string>
|
||||
<string name="common_unable_to_decrypt_verification_violation">"L’identité vérifiée de l’expéditeur a été réinitialisée"</string>
|
||||
<string name="common_unable_to_decrypt_verification_violation">"L’identité numérique vérifiée de l’expéditeur a été réinitialisée"</string>
|
||||
<string name="common_unable_to_invite_message">"Les invitations n’ont pas pu être envoyées à un ou plusieurs utilisateurs."</string>
|
||||
<string name="common_unable_to_invite_title">"Impossible d’envoyer une ou plusieurs invitations"</string>
|
||||
<string name="common_unlock">"Déverrouillage"</string>
|
||||
|
|
@ -394,16 +404,17 @@ Raison : %1$s."</string>
|
|||
<string name="common_voice_message">"Message vocal"</string>
|
||||
<string name="common_waiting">"En attente…"</string>
|
||||
<string name="common_waiting_for_decryption_key">"En attente de la clé de déchiffrement"</string>
|
||||
<string name="common_waiting_live_location">"En attente de la position en temps réel…"</string>
|
||||
<string name="common_world_readable_history">"Tout le monde peut voir les anciens messages"</string>
|
||||
<string name="common_you">"Vous"</string>
|
||||
<string name="crypto_event_key_forwarded_known_profile_dialog_content">"%1$s (%2$s) a partagé ce message avec vous car vous n’étiez pas dans le salon lors de son envoi."</string>
|
||||
<string name="crypto_event_key_forwarded_unknown_profile_dialog_content">"%1$s a partagé ce message avec vous car vous n’étiez pas dans le salon lors de son envoi."</string>
|
||||
<string name="crypto_history_visible">"Ce salon a été configuré pour que les nouveaux membres puissent lire l’historique. %1$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation">"L’identité de %1$s a été réinitialisée. %2$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation_new">"L’identité de %1$s %2$s a été réinitialisée. %3$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation">"L’identité numérique de %1$s a été réinitialisée. %2$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation_new">"L’identité numérique de %1$s %2$s a été réinitialisée. %3$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation_new_user_id">"(%1$s)"</string>
|
||||
<string name="crypto_identity_change_profile_pin_violation">"L’identité de %1$s a été réinitialisée."</string>
|
||||
<string name="crypto_identity_change_verification_violation_new">"L’identité de %1$s %2$s a été réinitialisée. %3$s"</string>
|
||||
<string name="crypto_identity_change_profile_pin_violation">"L’identité numérique de %1$s a été réinitialisée."</string>
|
||||
<string name="crypto_identity_change_verification_violation_new">"L’identité numérique de %1$s %2$s a été réinitialisée. %3$s"</string>
|
||||
<string name="crypto_identity_change_withdraw_verification_action">"Révoquer la vérification"</string>
|
||||
<string name="dialog_allow_access">"Autoriser l’accès"</string>
|
||||
<string name="dialog_confirm_link_message">"Le lien \"%1$s\" vous redirige vers un autre site \"%2$s\".
|
||||
|
|
@ -435,6 +446,7 @@ Raison : %1$s."</string>
|
|||
<string name="error_failed_locating_user">"%1$s n’a pas pu accéder à votre position. Veuillez réessayer ultérieurement."</string>
|
||||
<string name="error_failed_uploading_voice_message">"Échec lors de l’envoi du message vocal."</string>
|
||||
<string name="error_invalid_invite">"Ce salon n’existe plus ou l’invitation n’est plus valable."</string>
|
||||
<string name="error_location_service_disabled_android">"Veuillez activer votre GPS pour accéder aux fonctionnalités basées sur la localisation."</string>
|
||||
<string name="error_message_not_found">"Message introuvable"</string>
|
||||
<string name="error_missing_location_auth_android">"%1$s n’est pas autorisé à accéder à votre position. Vous pouvez activer l’accès dans les Paramètres."</string>
|
||||
<string name="error_missing_location_rationale_android">"%1$s n’est pas autorisé à accéder à votre position. Activez l’accès ci-dessous."</string>
|
||||
|
|
@ -462,11 +474,11 @@ Raison : %1$s."</string>
|
|||
<item quantity="other">"%1$d messages épinglés"</item>
|
||||
</plurals>
|
||||
<string name="screen_pinned_timeline_screen_title_empty">"Messages épinglés"</string>
|
||||
<string name="screen_reset_identity_confirmation_subtitle">"Vous êtes sur le point d’accéder à votre compte %1$s pour réinitialiser votre identité. Vous serez ensuite redirigé vers l’application."</string>
|
||||
<string name="screen_reset_identity_confirmation_title">"Vous ne pouvez pas confirmer ? Accédez à votre compte pour réinitialiser votre identité."</string>
|
||||
<string name="screen_reset_identity_confirmation_subtitle">"Vous êtes sur le point d’accéder à votre compte %1$s pour réinitialiser votre identité numérique. Vous serez ensuite redirigé vers l’application."</string>
|
||||
<string name="screen_reset_identity_confirmation_title">"Vous ne pouvez pas confirmer ? Accédez à votre compte pour réinitialiser votre identité numérique."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_primary_button_title">"Révoquer la verification et envoyer"</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_subtitle">"Vous pouvez révoquer la verification et envoyer ce message, ou vous pouvez annuler pour l’instant et réessayer plus tard après avoir vérifié à nouveau %1$s."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_title">"Votre message n’a pas été envoyé car l’identité vérifiée de %1$s a été réinitialisée"</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_title">"Votre message n’a pas été envoyé car l’identité numérique vérifiée de %1$s a été réinitialisée"</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_primary_button_title">"Envoyer le message quand même"</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_subtitle">"%1$s utilise un ou plusieurs appareils non vérifiés. Vous pouvez quand même envoyer le message, ou vous pouvez annuler pour l’instant et réessayer plus tard après que %2$s vérifie tous ses appareils."</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_title">"Votre message n’a pas été envoyé car %1$s n’a pas vérifié tous ses appareils"</string>
|
||||
|
|
@ -484,18 +496,22 @@ Raison : %1$s."</string>
|
|||
<string name="screen_room_pinned_banner_loading_description">"Chargement du message…"</string>
|
||||
<string name="screen_room_pinned_banner_view_all_button_title">"Voir tout"</string>
|
||||
<string name="screen_room_title">"Discussion"</string>
|
||||
<string name="screen_share_location_live_location_duration_picker_title">"Choisissez la durée pendant laquelle vous partagerez votre position en direct."</string>
|
||||
<string name="screen_share_location_title">"Partage de position"</string>
|
||||
<string name="screen_share_my_location_action">"Partager ma position"</string>
|
||||
<string name="screen_share_open_apple_maps">"Ouvrir dans Apple Maps"</string>
|
||||
<string name="screen_share_open_google_maps">"Ouvrir dans Google Maps"</string>
|
||||
<string name="screen_share_open_osm_maps">"Ouvrir dans OpenStreetMap"</string>
|
||||
<string name="screen_share_this_location_action">"Partager cette position"</string>
|
||||
<string name="screen_share_this_location_action">"Partager la position sélectionnée"</string>
|
||||
<string name="screen_sharing_location_option_sheet_title">"Options de partage"</string>
|
||||
<string name="screen_space_list_description">"Espaces que vous avez créés ou rejoints."</string>
|
||||
<string name="screen_space_list_details">"%1$s • %2$s"</string>
|
||||
<string name="screen_space_list_empty_state_title">"Créer des espaces pour organiser les salons"</string>
|
||||
<string name="screen_space_list_parent_space">"Espace %1$s"</string>
|
||||
<string name="screen_space_list_title">"Espaces"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Le message n’a pas été envoyé car l’identité vérifiée de %1$s a été réinitialisée."</string>
|
||||
<string name="screen_static_location_sheet_timestamp_description">"Partagé %1$s"</string>
|
||||
<string name="screen_static_location_sheet_title">"Sur la carte"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Le message n’a pas été envoyé car l’identité numérique vérifiée de %1$s a été réinitialisée."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_unsigned_device">"Le message n’a pas été envoyé car %1$s n’a pas vérifié tous ses appareils."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_you_unsigned_device">"Message non envoyé car vous n’avez pas vérifié tous vos appareils."</string>
|
||||
<string name="screen_view_location_title">"Position"</string>
|
||||
|
|
@ -505,5 +521,5 @@ Raison : %1$s."</string>
|
|||
<string name="timeline_decryption_failure_historical_event_unverified_device">"Vous devez vérifier cet appareil pour accéder à l’historique des messages"</string>
|
||||
<string name="timeline_decryption_failure_historical_event_user_not_joined">"Vous ne pouvez pas voir ce message"</string>
|
||||
<string name="timeline_decryption_failure_unable_to_decrypt">"Impossible de déchiffrer le message"</string>
|
||||
<string name="timeline_decryption_failure_withheld_unverified">"Ce message a été bloqué soit parce que vous n’avez pas vérifié votre session, soit parce que l’expéditeur doit vérifier votre identité."</string>
|
||||
<string name="timeline_decryption_failure_withheld_unverified">"Ce message a été bloqué soit parce que vous n’avez pas vérifié votre session, soit parce que l’expéditeur doit vérifier votre identité numérique."</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<string name="a11y_pause">"Szüneteltetés"</string>
|
||||
<string name="a11y_paused_voice_message">"Hangüzenet, időtartam:%1$s, jelenlegi pozíció:%2$s"</string>
|
||||
<string name="a11y_pin_field">"PIN-mező"</string>
|
||||
<string name="a11y_pinned_location">"Rögzített hely"</string>
|
||||
<string name="a11y_play">"Lejátszás"</string>
|
||||
<string name="a11y_playback_speed">"Lejátszási sebesség"</string>
|
||||
<string name="a11y_poll">"Szavazás"</string>
|
||||
|
|
@ -45,9 +46,11 @@
|
|||
<string name="a11y_remove_reaction_with">"Reakció eltávolítása: %1$s"</string>
|
||||
<string name="a11y_room_avatar">"Szoba profilképe"</string>
|
||||
<string name="a11y_send_files">"Fájlküldés"</string>
|
||||
<string name="a11y_sender_location">"Felhasználó tartózkodási helye"</string>
|
||||
<string name="a11y_session_verification_time_limited_action_required">"Időkorlátos művelet szükséges, egy perce van az ellenőrzésre"</string>
|
||||
<string name="a11y_show_password">"Jelszó megjelenítése"</string>
|
||||
<string name="a11y_start_call">"Hanghívás indítása"</string>
|
||||
<string name="a11y_start_voice_call">"Hanghívás indítása"</string>
|
||||
<string name="a11y_tombstoned_room">"Elévült szoba"</string>
|
||||
<string name="a11y_user_avatar">"Felhasználói profilkép"</string>
|
||||
<string name="a11y_user_menu">"Felhasználói menü"</string>
|
||||
|
|
@ -272,6 +275,7 @@ Ok: %1$s."</string>
|
|||
<string name="common_offline">"Kapcsolat nélkül"</string>
|
||||
<string name="common_open_source_licenses">"Nyílt forráskódú licencek"</string>
|
||||
<string name="common_or">"vagy"</string>
|
||||
<string name="common_other_options">"További lehetőségek"</string>
|
||||
<string name="common_password">"Jelszó"</string>
|
||||
<string name="common_people">"Emberek"</string>
|
||||
<string name="common_permalink">"Állandó hivatkozás"</string>
|
||||
|
|
@ -367,7 +371,7 @@ Ok: %1$s."</string>
|
|||
<string name="common_unable_to_decrypt">"Nem lehet visszafejteni"</string>
|
||||
<string name="common_unable_to_decrypt_insecure_device">"Nem biztonságos eszközről küldve"</string>
|
||||
<string name="common_unable_to_decrypt_no_access">"Nincs hozzáférése ehhez az üzenethez"</string>
|
||||
<string name="common_unable_to_decrypt_verification_violation">"A feladó ellenőrzött személyazonossága megváltozott"</string>
|
||||
<string name="common_unable_to_decrypt_verification_violation">"A feladó ellenőrzött digitális személyazonossága alaphelyzetbe lett állítva"</string>
|
||||
<string name="common_unable_to_invite_message">"Nem sikerült meghívót küldeni egy vagy több felhasználónak."</string>
|
||||
<string name="common_unable_to_invite_title">"Nem sikerült elküldeni a meghívót (meghívókat)"</string>
|
||||
<string name="common_unlock">"Feloldás"</string>
|
||||
|
|
@ -397,11 +401,11 @@ Ok: %1$s."</string>
|
|||
<string name="crypto_event_key_forwarded_known_profile_dialog_content">"%1$s (%2$s) megosztotta ezt az üzenetet, mivel Ön nem volt a szobában, amikor elküldték."</string>
|
||||
<string name="crypto_event_key_forwarded_unknown_profile_dialog_content">"%1$s megosztotta ezt az üzenetet, mivel Ön nem volt a szobában, amikor elküldték."</string>
|
||||
<string name="crypto_history_visible">"Az elküldött üzenetek megosztásra kerülnek a szobába meghívott új tagokkal.%1$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation">"%1$s személyazonossága megváltozott. %2$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation_new">"%1$s (%2$s) személyazonossága megváltozott. %3$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation">"%1$s digitális személyazonossága alaphelyzetbe lett állítva. %2$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation_new">"%1$s (%2$s) digitális személyazonossága alaphelyzetbe lett állítva. %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 személyazonossága megváltozott."</string>
|
||||
<string name="crypto_identity_change_verification_violation_new">"%1$s (%2$s) ellenőrzött személyazonossága megváltozott. %3$s"</string>
|
||||
<string name="crypto_identity_change_profile_pin_violation">"%1$s digitális személyazonossága alaphelyzetbe lett állítva."</string>
|
||||
<string name="crypto_identity_change_verification_violation_new">"%1$s (%2$s) digitális személyazonossága alaphelyzetbe lett állítva. %3$s"</string>
|
||||
<string name="crypto_identity_change_withdraw_verification_action">"Ellenőrzés visszavonása"</string>
|
||||
<string name="dialog_allow_access">"Hozzáférés engedélyezése"</string>
|
||||
<string name="dialog_confirm_link_message">"A(z) %1$s hivatkozás átviszi egy másik webhelyre: %2$s
|
||||
|
|
@ -433,6 +437,7 @@ Biztos, hogy folytatja?"</string>
|
|||
<string name="error_failed_locating_user">"Az %1$s nem tudta elérni a tartózkodási helyét. Próbálja újra később."</string>
|
||||
<string name="error_failed_uploading_voice_message">"Nem sikerült feltölteni a hangüzenetét."</string>
|
||||
<string name="error_invalid_invite">"A szoba már nem létezik, vagy a meghívó már nem érvényes."</string>
|
||||
<string name="error_location_service_disabled_android">"Engedélyezze a GPS-t a helyalapú funkciók eléréséhez."</string>
|
||||
<string name="error_message_not_found">"Az üzenet nem található"</string>
|
||||
<string name="error_missing_location_auth_android">"Az %1$snek nincs engedélye, hogy hozzáférjen a tartózkodási helyéhez. Ezt a beállításokban engedélyezheti."</string>
|
||||
<string name="error_missing_location_rationale_android">"Az %1$snek nincs engedélye, hogy hozzáférjen a tartózkodási helyéhez. Engedélyezze alább az elérését."</string>
|
||||
|
|
@ -452,6 +457,7 @@ Biztos, hogy folytatja?"</string>
|
|||
<string name="screen_create_poll_remove_accessibility_label">"Eltávolítás: %1$s"</string>
|
||||
<string name="screen_create_poll_settings_section_title">"Beállítások"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Nem sikerült kiválasztani a médiát, próbálja újra."</string>
|
||||
<string name="screen_onboarding_welcome_back">"Üdvözöljük újra!"</string>
|
||||
<string name="screen_pinned_timeline_empty_state_description">"Nyomjon hosszan az üzenetre, és válassza a „%1$s” lehetőséget, hogy itt szerepeljen."</string>
|
||||
<string name="screen_pinned_timeline_empty_state_headline">"Tűzze ki a fontos üzeneteket, hogy könnyen felfedezhetők legyenek"</string>
|
||||
<plurals name="screen_pinned_timeline_screen_title">
|
||||
|
|
@ -459,11 +465,11 @@ Biztos, hogy folytatja?"</string>
|
|||
<item quantity="other">"%1$d kitűzött üzenet"</item>
|
||||
</plurals>
|
||||
<string name="screen_pinned_timeline_screen_title_empty">"Kitűzött üzenetek"</string>
|
||||
<string name="screen_reset_identity_confirmation_subtitle">"Arra készül, hogy belépjen a(z) %1$s fiókjába, hogy visszaállítsa a személyazonosságát. Ezután vissza fog térni az alkalmazásba."</string>
|
||||
<string name="screen_reset_identity_confirmation_title">"Nem tudja megerősíteni? Ugorjon a fiókjához, és állítsa vissza a személyazonosságát."</string>
|
||||
<string name="screen_reset_identity_confirmation_subtitle">"Arra készül, hogy belépjen a(z) %1$s fiókjába, hogy alaphelyzetbe állítsa a digitális személyazonosságát. Ezután vissza fog térni az alkalmazásba."</string>
|
||||
<string name="screen_reset_identity_confirmation_title">"Nem tudja megerősíteni? Ugorjon a fiókjához, és állítsa alaphelyzetbe a digitális személyazonosságát."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_primary_button_title">"Ellenőrzés visszavonása és elküldés"</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_subtitle">"Visszavonhatja az ellenőrzést, és ennek ellenére elküldheti ezt az üzenetet, vagy egyelőre törölheti, és %1$s újbóli ellenőrzése után újra megpróbálhatja."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_title">"Az üzenete nem lett elküldve, mert %1$s személyazonossága megváltozott."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_title">"Az üzenete nem lett elküldve, mert %1$s digitális személyazonossága alaphelyzetbe lett állítva."</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_primary_button_title">"Üzenet elküldése mindenképp"</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_subtitle">"%1$s egy vagy több ellenőrizetlen eszközt használ. Így is elküldheti az üzenetet, vagy megszakíthatja most, és megpróbálhatja újra, miután %2$s ellenőrizte az összes eszközét."</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_title">"Az üzenet nem lett elküldve, mert %1$s nem ellenőrizte az összes eszközét"</string>
|
||||
|
|
@ -486,13 +492,16 @@ Biztos, hogy folytatja?"</string>
|
|||
<string name="screen_share_open_apple_maps">"Megnyitás az Apple Mapsben"</string>
|
||||
<string name="screen_share_open_google_maps">"Megnyitás a Google Mapsben"</string>
|
||||
<string name="screen_share_open_osm_maps">"Megnyitás az OpenStreetMapen"</string>
|
||||
<string name="screen_share_this_location_action">"E hely megosztása"</string>
|
||||
<string name="screen_share_this_location_action">"Kiválasztott hely megosztása"</string>
|
||||
<string name="screen_sharing_location_option_sheet_title">"Megosztási beállítások"</string>
|
||||
<string name="screen_space_list_description">"Létrehozott vagy olyan terek, melyekhez csatlakozott."</string>
|
||||
<string name="screen_space_list_details">"%1$s • %2$s"</string>
|
||||
<string name="screen_space_list_empty_state_title">"Terek létrehozása a szobák rendszerezéséhez"</string>
|
||||
<string name="screen_space_list_parent_space">"%1$s tér"</string>
|
||||
<string name="screen_space_list_title">"Terek"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Az üzenet nem lett elküldve, mert %1$s ellenőrzött személyazonossága megváltozott."</string>
|
||||
<string name="screen_static_location_sheet_timestamp_description">"Megosztva %1$s"</string>
|
||||
<string name="screen_static_location_sheet_title">"A térképen"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Az üzenet nem lett elküldve, mert %1$s ellenőrzött digitális személyazonossága alaphelyzetbe lett állítva."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_unsigned_device">"Az üzenet nem lett elküldve, mert %1$s nem ellenőrizte az összes eszközét."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_you_unsigned_device">"Az üzenet nem lett elküldve, mert egy vagy több eszközét nem ellenőrizte."</string>
|
||||
<string name="screen_view_location_title">"Hely"</string>
|
||||
|
|
@ -502,5 +511,5 @@ Biztos, hogy folytatja?"</string>
|
|||
<string name="timeline_decryption_failure_historical_event_unverified_device">"Ellenőriznie kell ezt az eszközt a korábbi üzenetekhez való hozzáféréshez"</string>
|
||||
<string name="timeline_decryption_failure_historical_event_user_not_joined">"Nincs hozzáférése ehhez az üzenethez"</string>
|
||||
<string name="timeline_decryption_failure_unable_to_decrypt">"Nem sikerült visszafejteni az üzenetet"</string>
|
||||
<string name="timeline_decryption_failure_withheld_unverified">"Ez az üzenet azért lett blokkolva, mert vagy nem ellenőrizte az eszközt, vagy a feladónak ellenőriznie kell az Ön személyazonosságát."</string>
|
||||
<string name="timeline_decryption_failure_withheld_unverified">"Ez az üzenet azért lett blokkolva, mert vagy nem ellenőrizte az eszközt, vagy a feladónak ellenőriznie kell az Ön digitális személyazonosságát."</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -2,13 +2,19 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="a11y_add_reaction">"Tambahkan reaksi: %1$s"</string>
|
||||
<string name="a11y_avatar">"Avatar"</string>
|
||||
<string name="a11y_collapse_message_text_field">"Minimalkan bidang teks message"</string>
|
||||
<string name="a11y_delete">"Hapus"</string>
|
||||
<plurals name="a11y_digits_entered">
|
||||
<item quantity="other">"%1$d digit dimasukkan"</item>
|
||||
</plurals>
|
||||
<string name="a11y_edit_avatar">"Sunting Avatar"</string>
|
||||
<string name="a11y_edit_room_address_hint">"Alamat lengkapnya adalah%1$s"</string>
|
||||
<string name="a11y_encryption_details">"Detail enkripsi"</string>
|
||||
<string name="a11y_expand_message_text_field">"Perluas kolom teks pesan"</string>
|
||||
<string name="a11y_hide_password">"Sembunyikan kata sandi"</string>
|
||||
<string name="a11y_join_call">"Bergabung dalam panggilan"</string>
|
||||
<string name="a11y_jump_to_bottom">"Lompat ke bawah"</string>
|
||||
<string name="a11y_move_the_map_to_my_location">"Geser peta ke lokasi saya"</string>
|
||||
<string name="a11y_notifications_mentions_only">"Hanya sebutan"</string>
|
||||
<string name="a11y_notifications_muted">"Dibisukan"</string>
|
||||
<string name="a11y_notifications_new_mentions">"Sebutan baru"</string>
|
||||
|
|
@ -37,6 +43,7 @@
|
|||
<string name="a11y_session_verification_time_limited_action_required">"Tindakan terbatas waktu diperlukan, Anda memiliki satu menit untuk memverifikasi"</string>
|
||||
<string name="a11y_show_password">"Tampilkan kata sandi"</string>
|
||||
<string name="a11y_start_call">"Mulai panggilan"</string>
|
||||
<string name="a11y_tombstoned_room">"Ruangan yang ditandai"</string>
|
||||
<string name="a11y_user_avatar">"Avatar pengguna"</string>
|
||||
<string name="a11y_user_menu">"Menu pengguna"</string>
|
||||
<string name="a11y_view_avatar">"Lihat avatar"</string>
|
||||
|
|
@ -65,7 +72,7 @@
|
|||
<string name="action_copy_link_to_message">"Salin tautan ke pesan"</string>
|
||||
<string name="action_copy_text">"Salin teks"</string>
|
||||
<string name="action_create">"Buat"</string>
|
||||
<string name="action_create_room">"Buat ruangan"</string>
|
||||
<string name="action_create_room">"Buat room"</string>
|
||||
<string name="action_deactivate">"Nonaktifkan"</string>
|
||||
<string name="action_deactivate_account">"Nonaktifkan akun"</string>
|
||||
<string name="action_decline">"Tolak"</string>
|
||||
|
|
@ -81,6 +88,7 @@
|
|||
<string name="action_enable">"Aktifkan"</string>
|
||||
<string name="action_end_poll">"Akhiri jajak pendapat"</string>
|
||||
<string name="action_enter_pin">"Masukkan PIN"</string>
|
||||
<string name="action_finish">"Selesai"</string>
|
||||
<string name="action_forgot_password">"Lupa kata sandi?"</string>
|
||||
<string name="action_forward">"Teruskan"</string>
|
||||
<string name="action_go_back">"Kembali"</string>
|
||||
|
|
@ -95,6 +103,7 @@
|
|||
<string name="action_leave">"Tinggalkan"</string>
|
||||
<string name="action_leave_conversation">"Tinggalkan percakapan"</string>
|
||||
<string name="action_leave_room">"Tinggalkan ruangan"</string>
|
||||
<string name="action_leave_space">"Tinggalkan space"</string>
|
||||
<string name="action_load_more">"Muat lainnya"</string>
|
||||
<string name="action_manage_account">"Kelola akun"</string>
|
||||
<string name="action_manage_devices">"Kelola perangkat"</string>
|
||||
|
|
@ -156,11 +165,14 @@
|
|||
<string name="banner_migrate_to_native_sliding_sync_title">"Peningkatan tersedia"</string>
|
||||
<string name="common_about">"Tentang"</string>
|
||||
<string name="common_acceptable_use_policy">"Kebijakan penggunaan wajar"</string>
|
||||
<string name="common_add_account">"Tambahkan akun"</string>
|
||||
<string name="common_add_another_account">"Tambahkan akun lainnya"</string>
|
||||
<string name="common_adding_caption">"Menambahkan keterangan"</string>
|
||||
<string name="common_advanced_settings">"Pengaturan tingkat lanjut"</string>
|
||||
<string name="common_an_image">"sebuah gambar"</string>
|
||||
<string name="common_analytics">"Analitik"</string>
|
||||
<string name="common_android_shortcuts_remove_reason_left_room">"Anda keluar dari ruangan"</string>
|
||||
<string name="common_android_shortcuts_remove_reason_session_logged_out">"Anda telah keluar dari sesi."</string>
|
||||
<string name="common_appearance">"Penampilan"</string>
|
||||
<string name="common_audio">"Audio"</string>
|
||||
<string name="common_blocked_users">"Pengguna yang diblokir"</string>
|
||||
|
|
@ -175,6 +187,7 @@
|
|||
<string name="common_current_user_rejected_invite">"Undangan ditolak"</string>
|
||||
<string name="common_dark">"Gelap"</string>
|
||||
<string name="common_decryption_error">"Kesalahan dekripsi"</string>
|
||||
<string name="common_description">"Deskripsi"</string>
|
||||
<string name="common_developer_options">"Opsi pengembang"</string>
|
||||
<string name="common_device_id">"ID Perangkat"</string>
|
||||
<string name="common_direct_chat">"Obrolan langsung"</string>
|
||||
|
|
@ -247,9 +260,12 @@ Alasan: %1$s."</string>
|
|||
<plurals name="common_poll_votes_count">
|
||||
<item quantity="other">"%d suara"</item>
|
||||
</plurals>
|
||||
<string name="common_preparing">"Menyiapkan…"</string>
|
||||
<string name="common_privacy_policy">"Kebijakan privasi"</string>
|
||||
<string name="common_private_room">"Ruangan pribadi"</string>
|
||||
<string name="common_private_space">"Space pribadi"</string>
|
||||
<string name="common_public_room">"Ruangan publik"</string>
|
||||
<string name="common_public_space">"Space publik"</string>
|
||||
<string name="common_reaction">"Reaksi"</string>
|
||||
<string name="common_reactions">"Reaksi"</string>
|
||||
<string name="common_reason">"Alasan"</string>
|
||||
|
|
@ -273,18 +289,21 @@ Alasan: %1$s."</string>
|
|||
<string name="common_search_results">"Hasil pencarian"</string>
|
||||
<string name="common_security">"Keamanan"</string>
|
||||
<string name="common_seen_by">"Dilihat oleh"</string>
|
||||
<string name="common_select_account">"Pilih akun"</string>
|
||||
<string name="common_send_to">"Kirim ke"</string>
|
||||
<string name="common_sending">"Mengirim…"</string>
|
||||
<string name="common_sending_failed">"Pengiriman gagal"</string>
|
||||
<string name="common_sent">"Terkirim"</string>
|
||||
<string name="common_sentence_delimiter">". "</string>
|
||||
<string name="common_server_not_supported">"Server tidak didukung"</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_shared_location">"Lokasi terbagi"</string>
|
||||
<string name="common_signing_out">"Mengeluarkan dari akun"</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>
|
||||
<string name="common_starting_chat">"Memulai obrolan…"</string>
|
||||
<string name="common_sticker">"Stiker"</string>
|
||||
<string name="common_success">"Berhasil"</string>
|
||||
|
|
@ -295,7 +314,7 @@ Alasan: %1$s."</string>
|
|||
<string name="common_third_party_notices">"Pemberitahuan pihak ketiga"</string>
|
||||
<string name="common_thread">"Utas"</string>
|
||||
<string name="common_topic">"Topik"</string>
|
||||
<string name="common_topic_placeholder">"Tentang apa ruangan ini?"</string>
|
||||
<string name="common_topic_placeholder">"Apa yang dibahas di room ini?"</string>
|
||||
<string name="common_unable_to_decrypt">"Tidak dapat mendekripsi"</string>
|
||||
<string name="common_unable_to_decrypt_insecure_device">"Dikirim dari perangkat yang tidak aman"</string>
|
||||
<string name="common_unable_to_decrypt_no_access">"Anda tidak memiliki akses ke pesan ini"</string>
|
||||
|
|
@ -315,6 +334,12 @@ Alasan: %1$s."</string>
|
|||
<string name="common_verify_identity">"Verifikasi identitas"</string>
|
||||
<string name="common_verify_user">"Verifikasi pengguna"</string>
|
||||
<string name="common_video">"Video"</string>
|
||||
<string name="common_video_quality_high">"Kualitas Tinggi"</string>
|
||||
<string name="common_video_quality_high_description">"Kualitas terbaik tetapi ukuran file lebih besar"</string>
|
||||
<string name="common_video_quality_low">"Kualitas Rendah"</string>
|
||||
<string name="common_video_quality_low_description">"Kecepatan unggah tercepat dan ukuran file terkecil"</string>
|
||||
<string name="common_video_quality_standard">"Kualitas standar"</string>
|
||||
<string name="common_video_quality_standard_description">"Keseimbangan antara kualitas dan kecepatan unggah"</string>
|
||||
<string name="common_voice_message">"Pesan suara"</string>
|
||||
<string name="common_waiting">"Menunggu…"</string>
|
||||
<string name="common_waiting_for_decryption_key">"Menunggu pesan ini"</string>
|
||||
|
|
@ -329,6 +354,10 @@ Alasan: %1$s."</string>
|
|||
|
||||
Apakah Anda yakin ingin melanjutkan?"</string>
|
||||
<string name="dialog_confirm_link_title">"Periksa kembali tautan ini"</string>
|
||||
<string name="dialog_default_video_quality_selector_subtitle">"Pilih kualitas default untuk video yang Anda unggah."</string>
|
||||
<string name="dialog_default_video_quality_selector_title">"Kualitas unggahan video"</string>
|
||||
<string name="dialog_file_too_large_to_upload_subtitle">"Ukuran file maksimum yang diizinkan adalah: %1$s"</string>
|
||||
<string name="dialog_file_too_large_to_upload_title">"Ukuran file terlalu besar untuk diunggah"</string>
|
||||
<string name="dialog_room_reported">"Ruangan dilaporkan"</string>
|
||||
<string name="dialog_room_reported_and_left">"Dilaporkan dan ruangan ditinggalkan"</string>
|
||||
<string name="dialog_title_confirmation">"Konfirmasi"</string>
|
||||
|
|
@ -338,6 +367,11 @@ Apakah Anda yakin ingin melanjutkan?"</string>
|
|||
<string name="dialog_unsaved_changes_description">"Anda memiliki perubahan yang belum disimpan."</string>
|
||||
<string name="dialog_unsaved_changes_description_android">"Perubahan Anda belum disimpan. Apakah Anda yakin ingin kembali?"</string>
|
||||
<string name="dialog_unsaved_changes_title">"Simpan perubahan?"</string>
|
||||
<string name="dialog_video_quality_selector_subtitle_file_size">"Ukuran file maksimum yang diizinkan adalah: %1$s"</string>
|
||||
<string name="dialog_video_quality_selector_subtitle_no_file_size">"Pilih kualitas video yang ingin Anda unggah."</string>
|
||||
<string name="dialog_video_quality_selector_title">"Pilih kualitas unggahan video"</string>
|
||||
<string name="emoji_picker_search_placeholder">"Cari emoji"</string>
|
||||
<string name="error_account_already_logged_in">"Anda sudah masuk ke akun di perangkat ini sebagai%1$s ."</string>
|
||||
<string name="error_account_creation_not_possible">"Homeserver Anda perlu ditingkatkan untuk mendukung Matrix Authentication Service dan pembuatan akun."</string>
|
||||
<string name="error_failed_creating_the_permalink">"Gagal membuat tautan permanen"</string>
|
||||
<string name="error_failed_loading_map">"%1$s tidak dapat memuat peta. Silakan coba lagi nanti."</string>
|
||||
|
|
@ -358,6 +392,7 @@ Apakah Anda yakin ingin melanjutkan?"</string>
|
|||
<string name="invite_friends_text">"Hai, bicaralah dengan saya di %1$s: %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<string name="preference_rageshake">"Rageshake untuk melaporkan kutu"</string>
|
||||
<string name="screen_bug_report_a11y_screenshot">"Tangkapan layar"</string>
|
||||
<string name="screen_create_poll_option_accessibility_label">"%1$s: %2$s"</string>
|
||||
<string name="screen_create_poll_options_section_title">"Opsi"</string>
|
||||
<string name="screen_create_poll_remove_accessibility_label">"Hapus %1$s"</string>
|
||||
|
|
@ -379,6 +414,7 @@ Apakah Anda yakin ingin melanjutkan?"</string>
|
|||
<string name="screen_resolve_send_failure_unsigned_device_title">"Pesan Anda tidak terkirim karena %1$s belum memverifikasi semua perangkat"</string>
|
||||
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"Satu atau beberapa perangkat Anda tidak terverifikasi. Anda tetap dapat mengirim pesan, atau Anda dapat membatalkannya dan mencoba lagi nanti setelah Anda memverifikasi semua perangkat."</string>
|
||||
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Pesan Anda tidak terkirim karena Anda belum memverifikasi satu atau beberapa perangkat Anda"</string>
|
||||
<string name="screen_room_change_role_administrators_or_owners_title">"Edit Admin atau Pemilik"</string>
|
||||
<string name="screen_room_error_failed_processing_media">"Gagal memproses media untuk diunggah, silakan coba lagi."</string>
|
||||
<string name="screen_room_error_failed_retrieving_user_details">"Tidak dapat mengambil detail pengguna"</string>
|
||||
<string name="screen_room_event_pill">"Pesan dalam %1$s"</string>
|
||||
|
|
@ -396,6 +432,9 @@ Apakah Anda yakin ingin melanjutkan?"</string>
|
|||
<string name="screen_share_open_google_maps">"Buka di Google Maps"</string>
|
||||
<string name="screen_share_open_osm_maps">"Buka di OpenStreetMap"</string>
|
||||
<string name="screen_share_this_location_action">"Bagikan lokasi ini"</string>
|
||||
<string name="screen_space_list_description">"Space yang Anda buat atau ikuti."</string>
|
||||
<string name="screen_space_list_details">"%1$s • %2$s"</string>
|
||||
<string name="screen_space_list_title">"Space"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Pesan tidak terkirim karena identitas terverifikasi %1$s telah diatur ulang."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_unsigned_device">"Pesan tidak terkirim karena %1$s belum memverifikasi semua perangkat."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_you_unsigned_device">"Pesan tidak terkirim karena Anda belum memverifikasi satu atau beberapa perangkat Anda."</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<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_delete">"삭제"</string>
|
||||
|
|
@ -26,8 +27,10 @@
|
|||
<string name="a11y_paused_voice_message">"음성 메시지, 지속 시간: %1$s, 현재 위치: %2$s"</string>
|
||||
<string name="a11y_pin_field">"PIN 필드"</string>
|
||||
<string name="a11y_play">"재생"</string>
|
||||
<string name="a11y_playback_speed">"재생 속도"</string>
|
||||
<string name="a11y_poll">"투표"</string>
|
||||
<string name="a11y_poll_end">"종료된 투표"</string>
|
||||
<string name="a11y_qr_code">"QR 코드"</string>
|
||||
<string name="a11y_react_with">"%1$s에 반응하세요"</string>
|
||||
<string name="a11y_react_with_other_emojis">"다른 이모지로 반응하세요"</string>
|
||||
<string name="a11y_read_receipts_multiple">"읽은 사람 %1$s 그리고 %2$s"</string>
|
||||
|
|
@ -40,7 +43,7 @@
|
|||
<string name="a11y_remove_reaction_with">"%1$s 반응을 제거하세요"</string>
|
||||
<string name="a11y_room_avatar">"방 아바타"</string>
|
||||
<string name="a11y_send_files">"파일 보내기"</string>
|
||||
<string name="a11y_session_verification_time_limited_action_required">"시간 제한 조치가 필요합니다"</string>
|
||||
<string name="a11y_session_verification_time_limited_action_required">"제한 시간 내 인증이 필요합니다.1분 안에 확인해 주세요."</string>
|
||||
<string name="a11y_show_password">"비밀번호 표시"</string>
|
||||
<string name="a11y_start_call">"통화 시작"</string>
|
||||
<string name="a11y_tombstoned_room">"묘비 방"</string>
|
||||
|
|
@ -54,6 +57,7 @@
|
|||
<string name="a11y_your_avatar">"당신의 아바타"</string>
|
||||
<string name="action_accept">"수락"</string>
|
||||
<string name="action_add_caption">"캡션 추가"</string>
|
||||
<string name="action_add_existing_rooms">"기존 방 추가"</string>
|
||||
<string name="action_add_to_timeline">"타임라인에 추가"</string>
|
||||
<string name="action_back">"이전"</string>
|
||||
<string name="action_call">"통화"</string>
|
||||
|
|
@ -73,11 +77,13 @@
|
|||
<string name="action_copy_text">"텍스트 복사"</string>
|
||||
<string name="action_create">"만들기"</string>
|
||||
<string name="action_create_room">"방 만들기"</string>
|
||||
<string name="action_create_space">"스페이스 만들기"</string>
|
||||
<string name="action_deactivate">"비활성화"</string>
|
||||
<string name="action_deactivate_account">"계정 비활성화"</string>
|
||||
<string name="action_decline">"거절"</string>
|
||||
<string name="action_decline_and_block">"거부 및 차단"</string>
|
||||
<string name="action_delete_poll">"투표 삭제"</string>
|
||||
<string name="action_deselect_all">"전체 선택 해제"</string>
|
||||
<string name="action_disable">"비활성화"</string>
|
||||
<string name="action_discard">"취소"</string>
|
||||
<string name="action_dismiss">"닫기"</string>
|
||||
|
|
@ -88,10 +94,13 @@
|
|||
<string name="action_enable">"활성화"</string>
|
||||
<string name="action_end_poll">"투표 종료"</string>
|
||||
<string name="action_enter_pin">"PIN을 입력하세요"</string>
|
||||
<string name="action_explore_public_spaces">"공개 스페이스 탐색"</string>
|
||||
<string name="action_finish">"완료"</string>
|
||||
<string name="action_forgot_password">"비밀번호를 잊으셨나요?"</string>
|
||||
<string name="action_forward">"전달"</string>
|
||||
<string name="action_go_back">"뒤로 가기"</string>
|
||||
<string name="action_go_to_roles_and_permissions">"역할 및 권한 설정으로 이동"</string>
|
||||
<string name="action_go_to_settings">"설정으로 이동"</string>
|
||||
<string name="action_ignore">"무시하다"</string>
|
||||
<string name="action_invite">"초대"</string>
|
||||
<string name="action_invite_friends">"사람 초대하기"</string>
|
||||
|
|
@ -103,10 +112,13 @@
|
|||
<string name="action_leave">"떠나기"</string>
|
||||
<string name="action_leave_conversation">"대화에서 나가기"</string>
|
||||
<string name="action_leave_room">"방 떠나기"</string>
|
||||
<string name="action_leave_space">"스페이스 떠나기"</string>
|
||||
<string name="action_load_more">"더 불러오기"</string>
|
||||
<string name="action_manage_account">"계정 관리"</string>
|
||||
<string name="action_manage_devices">"기기 관리"</string>
|
||||
<string name="action_manage_rooms">"방 관리"</string>
|
||||
<string name="action_message">"메시지"</string>
|
||||
<string name="action_minimize">"최소화"</string>
|
||||
<string name="action_next">"다음"</string>
|
||||
<string name="action_no">"아니오"</string>
|
||||
<string name="action_not_now">"나중에"</string>
|
||||
|
|
@ -135,16 +147,18 @@
|
|||
<string name="action_retry_decryption">"복호화 재시도"</string>
|
||||
<string name="action_save">"저장"</string>
|
||||
<string name="action_search">"검색"</string>
|
||||
<string name="action_select_all">"전체 선택"</string>
|
||||
<string name="action_send">"보내기"</string>
|
||||
<string name="action_send_edited_message">"편집한 메시지 보내기"</string>
|
||||
<string name="action_send_message">"메시지 보내기"</string>
|
||||
<string name="action_send_voice_message">"음성 메세지 보내기"</string>
|
||||
<string name="action_share">"공유"</string>
|
||||
<string name="action_share_link">"링크 공유"</string>
|
||||
<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>
|
||||
|
|
@ -153,6 +167,7 @@
|
|||
<string name="action_static_map_load">"탭해서 지도 불러오기"</string>
|
||||
<string name="action_take_photo">"사진 찍기"</string>
|
||||
<string name="action_tap_for_options">"옵션을 보려면 탭하세요"</string>
|
||||
<string name="action_translate">"번역"</string>
|
||||
<string name="action_try_again">"다시 시도하기"</string>
|
||||
<string name="action_unpin">"고정 해제"</string>
|
||||
<string name="action_view">"보기"</string>
|
||||
|
|
@ -164,6 +179,8 @@
|
|||
<string name="banner_migrate_to_native_sliding_sync_title">"업그레이드 가능"</string>
|
||||
<string name="common_about">"정보"</string>
|
||||
<string name="common_acceptable_use_policy">"이용 목적 제한 방침"</string>
|
||||
<string name="common_add_account">"계정 추가"</string>
|
||||
<string name="common_add_another_account">"다른 계정 추가"</string>
|
||||
<string name="common_adding_caption">"캡션 추가"</string>
|
||||
<string name="common_advanced_settings">"고급 설정"</string>
|
||||
<string name="common_an_image">"이미지"</string>
|
||||
|
|
@ -172,6 +189,7 @@
|
|||
<string name="common_android_shortcuts_remove_reason_session_logged_out">"세션에서 로그아웃되었습니다."</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_started">"통화 시작"</string>
|
||||
|
|
@ -179,11 +197,14 @@
|
|||
<string name="common_copied_to_clipboard">"클립보드에 복사됨"</string>
|
||||
<string name="common_copyright">"저작권"</string>
|
||||
<string name="common_creating_room">"방 만드는 중…"</string>
|
||||
<string name="common_creating_space">"스페이스 만드는중…"</string>
|
||||
<string name="common_current_user_canceled_knock">"요청이 취소되었습니다"</string>
|
||||
<string name="common_current_user_left_room">"방 떠남"</string>
|
||||
<string name="common_current_user_left_space">"스페이스 떠남"</string>
|
||||
<string name="common_current_user_rejected_invite">"초대 거부됨"</string>
|
||||
<string name="common_dark">"다크"</string>
|
||||
<string name="common_decryption_error">"복호화 오류"</string>
|
||||
<string name="common_description">"설명"</string>
|
||||
<string name="common_developer_options">"개발자 설정"</string>
|
||||
<string name="common_device_id">"기기 ID"</string>
|
||||
<string name="common_direct_chat">"다이렉트 채팅"</string>
|
||||
|
|
@ -218,9 +239,11 @@
|
|||
<string name="common_install_apk_android">"APK 설치"</string>
|
||||
<string name="common_invite_unknown_profile">"Matrix ID를 찾을 수 없기 때문에 초대가 수신되지 않을 수도 있습니다."</string>
|
||||
<string name="common_leaving_room">"방을 떠나는 중"</string>
|
||||
<string name="common_leaving_space">"스페이스 떠나는 중"</string>
|
||||
<string name="common_light">"라이트"</string>
|
||||
<string name="common_line_copied_to_clipboard">"줄이 클립보드에 복사되었습니다."</string>
|
||||
<string name="common_link_copied_to_clipboard">"링크가 클립보드에 복사됨"</string>
|
||||
<string name="common_link_new_device">"새 기기 연결"</string>
|
||||
<string name="common_loading">"로딩 중…"</string>
|
||||
<string name="common_loading_more">"더 많은 내용이 로딩 중…"</string>
|
||||
<plurals name="common_many_members">
|
||||
|
|
@ -231,13 +254,16 @@
|
|||
</plurals>
|
||||
<string name="common_message">"메시지"</string>
|
||||
<string name="common_message_actions">"메시지 작업"</string>
|
||||
<string name="common_message_failed_to_send">"메시지 전송 실패"</string>
|
||||
<string name="common_message_layout">"메시지 레이아웃"</string>
|
||||
<string name="common_message_removed">"메시지 제거됨"</string>
|
||||
<string name="common_modern">"모던"</string>
|
||||
<string name="common_mute">"음소거"</string>
|
||||
<string name="common_name">"이름"</string>
|
||||
<string name="common_name_and_id">"%1$s (%2$s)"</string>
|
||||
<string name="common_no_results">"결과 없음"</string>
|
||||
<string name="common_no_room_name">"방 이름 없음"</string>
|
||||
<string name="common_no_space_name">"스페이스 이름 없음"</string>
|
||||
<string name="common_not_encrypted">"암호화되지 않음"</string>
|
||||
<string name="common_offline">"오프라인"</string>
|
||||
<string name="common_open_source_licenses">"오픈 소스 라이선스"</string>
|
||||
|
|
@ -258,8 +284,10 @@
|
|||
</plurals>
|
||||
<string name="common_preparing">"준비 중…"</string>
|
||||
<string name="common_privacy_policy">"개인정보 처리방침"</string>
|
||||
<string name="common_private">"비공개"</string>
|
||||
<string name="common_private_room">"비공개 방"</string>
|
||||
<string name="common_private_space">"비공개 스페이스"</string>
|
||||
<string name="common_public">"공개"</string>
|
||||
<string name="common_public_room">"공개 방"</string>
|
||||
<string name="common_public_space">"공개 스페이스"</string>
|
||||
<string name="common_reaction">"반응"</string>
|
||||
|
|
@ -267,6 +295,7 @@
|
|||
<string name="common_reason">"이유"</string>
|
||||
<string name="common_recovery_key">"복구 키"</string>
|
||||
<string name="common_refreshing">"새로고침 중…"</string>
|
||||
<string name="common_removing">"삭제 중…"</string>
|
||||
<plurals name="common_replies">
|
||||
<item quantity="other">"%1$d 답변"</item>
|
||||
</plurals>
|
||||
|
|
@ -275,6 +304,7 @@
|
|||
<string name="common_report_a_problem">"문제 보고"</string>
|
||||
<string name="common_report_submitted">"보고 제출됨"</string>
|
||||
<string name="common_rich_text_editor">"리치 텍스트 편집기"</string>
|
||||
<string name="common_role">"역할"</string>
|
||||
<string name="common_room">"방"</string>
|
||||
<string name="common_room_name">"방 이름"</string>
|
||||
<string name="common_room_name_placeholder">"예: 프로젝트명"</string>
|
||||
|
|
@ -288,25 +318,36 @@
|
|||
<string name="common_search_results">"검색 결과"</string>
|
||||
<string name="common_security">"보안"</string>
|
||||
<string name="common_seen_by">"본 사람"</string>
|
||||
<string name="common_select_account">"계정 선택"</string>
|
||||
<plurals name="common_selected_count">
|
||||
<item quantity="other">"%1$d개 선택됨"</item>
|
||||
</plurals>
|
||||
<string name="common_send_to">"보내기"</string>
|
||||
<string name="common_sending">"전송 중…"</string>
|
||||
<string name="common_sending_failed">"전송 실패"</string>
|
||||
<string name="common_sent">"보냄"</string>
|
||||
<string name="common_sentence_delimiter">". "</string>
|
||||
<string name="common_server_not_supported">"지원되지 않는 서버"</string>
|
||||
<string name="common_server_unreachable">"서버에 연결할 수 없습니다"</string>
|
||||
<string name="common_server_url">"서버 URL"</string>
|
||||
<string name="common_settings">"설정"</string>
|
||||
<string name="common_share_space">"스페이스 공유"</string>
|
||||
<string name="common_shared_history">"새 멤버에게 대화 기록 공개"</string>
|
||||
<string name="common_shared_location">"공유된 위치"</string>
|
||||
<string name="common_signing_out">"로그아웃"</string>
|
||||
<string name="common_shared_space">"공유된 스페이스"</string>
|
||||
<string name="common_signing_out">"기기 제거"</string>
|
||||
<string name="common_something_went_wrong">"뭔가 잘못됐어요"</string>
|
||||
<string name="common_something_went_wrong_message">"문제가 발생했습니다. 다시 시도해 주세요."</string>
|
||||
<string name="common_space">"스페이스"</string>
|
||||
<string name="common_space_members">"스페이스 멤버"</string>
|
||||
<string name="common_space_topic_placeholder">"이 스페이스는 어떤 곳인가요?"</string>
|
||||
<plurals name="common_spaces">
|
||||
<item quantity="other">"%1$d 스페이스"</item>
|
||||
</plurals>
|
||||
<string name="common_starting_chat">"채팅 시작 중…"</string>
|
||||
<string name="common_sticker">"스티커"</string>
|
||||
<string name="common_success">"성공"</string>
|
||||
<string name="common_suggested">"공유된 스페이스"</string>
|
||||
<string name="common_suggestions">"제안"</string>
|
||||
<string name="common_syncing">"동기화 중"</string>
|
||||
<string name="common_system">"시스템"</string>
|
||||
|
|
@ -314,7 +355,7 @@
|
|||
<string name="common_third_party_notices">"제3자 고지"</string>
|
||||
<string name="common_thread">"스레드"</string>
|
||||
<string name="common_topic">"주제"</string>
|
||||
<string name="common_topic_placeholder">"여기는 무슨 방인가요?"</string>
|
||||
<string name="common_topic_placeholder">"이 방은 어떤 곳인가요?"</string>
|
||||
<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>
|
||||
|
|
@ -343,13 +384,18 @@
|
|||
<string name="common_voice_message">"음성 메시지"</string>
|
||||
<string name="common_waiting">"대기 중…"</string>
|
||||
<string name="common_waiting_for_decryption_key">"이 메시지를 기다리고 있습니다"</string>
|
||||
<string name="common_world_readable_history">"누구나 대화 기록 보기 가능"</string>
|
||||
<string name="common_you">"당신"</string>
|
||||
<string name="crypto_event_key_forwarded_known_profile_dialog_content">"%1$s(%2$s)님이 이 메시지를 공유했습니다. 메시지 전송 당시 귀하가 방에 참여 중이 아니었기 때문입니다."</string>
|
||||
<string name="crypto_event_key_forwarded_unknown_profile_dialog_content">"%1$s님이 이 메시지를 공유했습니다. 메시지 전송 당시 귀하가 방에 참여 중이 아니었기 때문입니다."</string>
|
||||
<string name="crypto_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_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_withdraw_verification_action">"확인 취소"</string>
|
||||
<string name="dialog_allow_access">"액세스 허용"</string>
|
||||
<string name="dialog_confirm_link_message">"%1$s 링크는 다른 사이트로 이동합니다 %2$s
|
||||
|
||||
정말 계속 진행하시겠습니까?"</string>
|
||||
|
|
@ -372,6 +418,8 @@
|
|||
"</string>
|
||||
<string name="dialog_video_quality_selector_subtitle_no_file_size">"업로드할 비디오의 품질을 선택하세요."</string>
|
||||
<string name="dialog_video_quality_selector_title">"비디오 업로드 품질 선택"</string>
|
||||
<string name="emoji_picker_search_placeholder">"이모지 검색"</string>
|
||||
<string name="error_account_already_logged_in">"이 기기에서 이미 %1$s님으로 로그인되어 있습니다."</string>
|
||||
<string name="error_account_creation_not_possible">"Matrix Authentication Service 및 계정 생성을 지원하려면 홈서버를 업그레이드해야 합니다."</string>
|
||||
<string name="error_failed_creating_the_permalink">"퍼머링크 생성 실패"</string>
|
||||
<string name="error_failed_loading_map">"%1$s에서 맵을 로딩할 수 없습니다. 다시 시도해주세요."</string>
|
||||
|
|
@ -404,8 +452,8 @@
|
|||
<item quantity="other">"%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>
|
||||
|
|
@ -431,9 +479,11 @@
|
|||
<string name="screen_share_open_apple_maps">"Apple Maps에서 열기"</string>
|
||||
<string name="screen_share_open_google_maps">"Google Maps에서 열기"</string>
|
||||
<string name="screen_share_open_osm_maps">"OpenStreetMap에서 열기"</string>
|
||||
<string name="screen_share_this_location_action">"이 위치 공유"</string>
|
||||
<string name="screen_share_this_location_action">"선택한 위치 공유"</string>
|
||||
<string name="screen_space_list_description">"당신이 스페이스를 만들거나 가입했습니다."</string>
|
||||
<string name="screen_space_list_details">"%1$s•%2$s"</string>
|
||||
<string name="screen_space_list_empty_state_title">"스페이스를 생성하여 방을 체계적으로 관리해 보세요."</string>
|
||||
<string name="screen_space_list_parent_space">"%1$s 스페이스"</string>
|
||||
<string name="screen_space_list_title">"스페이스"</string>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@
|
|||
<string name="action_forward">"Persiųsti"</string>
|
||||
<string name="action_invite">"Kviesti"</string>
|
||||
<string name="action_invite_friends">"Pakviesti žmonių"</string>
|
||||
<string name="action_invite_friends_to_app">"Kviesti žmones į %1$s"</string>
|
||||
<string name="action_invite_friends_to_app">"Kviesti žmones į „%1$s“"</string>
|
||||
<string name="action_invite_people_to_app">"Kviesti žmones į „%1$s“"</string>
|
||||
<string name="action_invites_list">"Kvietimai"</string>
|
||||
<string name="action_learn_more">"Sužinoti daugiau"</string>
|
||||
<string name="action_leave">"Išeiti"</string>
|
||||
<string name="action_leave_conversation">"Palikti pokalbį"</string>
|
||||
<string name="action_leave_room">"Palikti kambarį"</string>
|
||||
<string name="action_leave_room">"Išeiti iš kambario"</string>
|
||||
<string name="action_load_more">"Įkelti daugiau"</string>
|
||||
<string name="action_next">"Tolesnis"</string>
|
||||
<string name="action_no">"Ne"</string>
|
||||
|
|
@ -50,6 +50,7 @@
|
|||
<string name="action_open_with">"Atverti su"</string>
|
||||
<string name="action_quick_reply">"Sparčiai atsakyti"</string>
|
||||
<string name="action_quote">"Cituoti"</string>
|
||||
<string name="action_react">"Reaguoti"</string>
|
||||
<string name="action_remove">"Šalinti"</string>
|
||||
<string name="action_reply">"Atsakyti"</string>
|
||||
<string name="action_report_bug">"Pranešti apie riktą"</string>
|
||||
|
|
@ -86,7 +87,7 @@
|
|||
<string name="common_decryption_error">"Iššifravimo klaida"</string>
|
||||
<string name="common_developer_options">"Kūrėjo nustatymai"</string>
|
||||
<string name="common_direct_chat">"Asmeninis pokalbis"</string>
|
||||
<string name="common_edited_suffix">"(taisyta)"</string>
|
||||
<string name="common_edited_suffix">"(redaguota)"</string>
|
||||
<string name="common_editing">"Taisymas"</string>
|
||||
<string name="common_emote">"* %1$s %2$s"</string>
|
||||
<string name="common_encryption_enabled">"Šifravimas įjungtas"</string>
|
||||
|
|
@ -114,10 +115,16 @@
|
|||
<string name="common_offline">"Neprisijungta"</string>
|
||||
<string name="common_password">"Slaptažodis"</string>
|
||||
<string name="common_people">"Žmonės"</string>
|
||||
<string name="common_permalink">"Nuolatinė nuoroda"</string>
|
||||
<string name="common_permalink">"Pastovi nuoroda"</string>
|
||||
<plurals name="common_poll_votes_count">
|
||||
<item quantity="one">"%d balsas"</item>
|
||||
<item quantity="few">"%d balsai"</item>
|
||||
<item quantity="other">"%d balsų"</item>
|
||||
</plurals>
|
||||
<string name="common_privacy_policy">"Privatumo politika"</string>
|
||||
<string name="common_private_room">"Privatus kambarys"</string>
|
||||
<string name="common_reactions">"Reakcijos"</string>
|
||||
<string name="common_refreshing">"Atnaujinama…"</string>
|
||||
<string name="common_replying_to">"Atsakant %1$s"</string>
|
||||
<string name="common_report_a_bug">"Pranešti apie klaidą"</string>
|
||||
<string name="common_report_submitted">"Skundas pateiktas"</string>
|
||||
|
|
@ -130,6 +137,7 @@
|
|||
<string name="common_server_not_supported">"Serveris nepalaikomas"</string>
|
||||
<string name="common_server_url">"Serverio URL"</string>
|
||||
<string name="common_settings">"Nustatymai"</string>
|
||||
<string name="common_shared_location">"Bendrinta vieta"</string>
|
||||
<string name="common_signing_out">"Atsijungiama"</string>
|
||||
<string name="common_starting_chat">"Pradedamas pokalbis…"</string>
|
||||
<string name="common_sticker">"Lipdukas"</string>
|
||||
|
|
@ -144,7 +152,7 @@
|
|||
<string name="common_unable_to_invite_title">"Nepavyko išsiųsti kvietimo (-ų)"</string>
|
||||
<string name="common_unmute">"Atšaukti nutildymą"</string>
|
||||
<string name="common_unsupported_event">"Nepalaikomas įvykis"</string>
|
||||
<string name="common_username">"Vartotojo vardas"</string>
|
||||
<string name="common_username">"Naudotojo vardas"</string>
|
||||
<string name="common_verification_cancelled">"Patvirtinimas atšauktas"</string>
|
||||
<string name="common_verification_complete">"Patvirtinimas baigtas"</string>
|
||||
<string name="common_video">"Vaizdo įrašas"</string>
|
||||
|
|
@ -164,6 +172,13 @@
|
|||
<string name="screen_media_picker_error_failed_selection">"Nepavyko pasirinkti laikmenos, pabandykite dar kartą."</string>
|
||||
<string name="screen_room_error_failed_processing_media">"Nepavyko apdoroti įkeliamos laikmenos, bandykite dar kartą."</string>
|
||||
<string name="screen_room_error_failed_retrieving_user_details">"Nepavyko gauti naudotojo išsamios informacijos."</string>
|
||||
<string name="screen_share_location_title">"Bendrinti vietą"</string>
|
||||
<string name="screen_share_my_location_action">"Bendrinti mano vietą"</string>
|
||||
<string name="screen_share_open_apple_maps">"Atverti programoje „Apple Maps“"</string>
|
||||
<string name="screen_share_open_google_maps">"Atverti „Google“ žemėlapiuose"</string>
|
||||
<string name="screen_share_open_osm_maps">"Atverti programoje „OpenStreetMap“"</string>
|
||||
<string name="screen_share_this_location_action">"Bendrinti pasirinktą vietą"</string>
|
||||
<string name="screen_view_location_title">"Vieta"</string>
|
||||
<string name="settings_version_number">"Versija: %1$s (%2$s)"</string>
|
||||
<string name="test_language_identifier">"lt"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -162,8 +162,8 @@
|
|||
<string name="action_share_live_location">"Del posisjon i sanntid"</string>
|
||||
<string name="action_show">"Vis"</string>
|
||||
<string name="action_sign_in_again">"Logg på igjen"</string>
|
||||
<string name="action_signout">"Logg ut"</string>
|
||||
<string name="action_signout_anyway">"Logg ut likevel"</string>
|
||||
<string name="action_signout">"Fjern denne enheten"</string>
|
||||
<string name="action_signout_anyway">"Fjern denne enheten likevel"</string>
|
||||
<string name="action_skip">"Hopp over"</string>
|
||||
<string name="action_start">"Start"</string>
|
||||
<string name="action_start_chat">"Start chat"</string>
|
||||
|
|
@ -345,7 +345,7 @@
|
|||
<string name="common_shared_history">"Nye medlemmer ser historikk"</string>
|
||||
<string name="common_shared_location">"Delt posisjon"</string>
|
||||
<string name="common_shared_space">"Delt område"</string>
|
||||
<string name="common_signing_out">"Logger av"</string>
|
||||
<string name="common_signing_out">"Fjerner enheten"</string>
|
||||
<string name="common_something_went_wrong">"Noe gikk galt"</string>
|
||||
<string name="common_something_went_wrong_message">"Vi har støtt på et problem. Vennligst prøv igjen."</string>
|
||||
<string name="common_space">"Område"</string>
|
||||
|
|
@ -397,7 +397,7 @@
|
|||
<string name="common_waiting_for_decryption_key">"Venter på denne meldingen"</string>
|
||||
<string name="common_world_readable_history">"Alle kan se historikk"</string>
|
||||
<string name="common_you">"Du"</string>
|
||||
<string name="crypto_event_key_forwarded_known_profile_dialog_content">"%1$s(%2$s ) delte denne meldingen siden du ikke var i rommet da den ble sendt."</string>
|
||||
<string name="crypto_event_key_forwarded_known_profile_dialog_content">"%1$s (%2$s) delte denne meldingen siden du ikke var i rommet da den ble sendt."</string>
|
||||
<string name="crypto_event_key_forwarded_unknown_profile_dialog_content">"%1$s delte denne meldingen siden du ikke var i rommet da den ble sendt."</string>
|
||||
<string name="crypto_history_visible">"Dette rommet er konfigurert slik at nye medlemmer kan lese historikken.%1$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation">"%1$s\'s identitet ble tilbakestilt. %2$s"</string>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<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_collapse_message_text_field">"Свернуть поле ввода"</string>
|
||||
<string name="a11y_delete">"Удалить"</string>
|
||||
<plurals name="a11y_digits_entered">
|
||||
<item quantity="one">"Введена %1$d цифра"</item>
|
||||
<item quantity="few">"Ведено %1$d цифры"</item>
|
||||
<item quantity="many">"Введено много цифр"</item>
|
||||
<item quantity="few">"Введено %1$d цифры"</item>
|
||||
<item quantity="many">"Введено %1$d цифр"</item>
|
||||
</plurals>
|
||||
<string name="a11y_edit_avatar">"Изменить аватар"</string>
|
||||
<string name="a11y_edit_room_address_hint">"Полный адрес %1$s"</string>
|
||||
<string name="a11y_encryption_details">"Сведения о шифровании"</string>
|
||||
<string name="a11y_expand_message_text_field">"Развернуть поле текста сообщения"</string>
|
||||
<string name="a11y_expand_message_text_field">"Развернуть поле ввода"</string>
|
||||
<string name="a11y_hide_password">"Скрыть пароль"</string>
|
||||
<string name="a11y_join_call">"Присоединиться к звонку"</string>
|
||||
<string name="a11y_jump_to_bottom">"Перейти вниз"</string>
|
||||
<string name="a11y_move_the_map_to_my_location">"Переместить карту на мое местоположение"</string>
|
||||
<string name="a11y_move_the_map_to_my_location">"Переместить карту к моему местоположению"</string>
|
||||
<string name="a11y_notifications_mentions_only">"Только упоминания"</string>
|
||||
<string name="a11y_notifications_muted">"Звук отключен"</string>
|
||||
<string name="a11y_notifications_muted">"Без звука"</string>
|
||||
<string name="a11y_notifications_new_mentions">"Новые упоминания"</string>
|
||||
<string name="a11y_notifications_new_messages">"Новые сообшения"</string>
|
||||
<string name="a11y_notifications_ongoing_call">"Текущий вызов"</string>
|
||||
|
|
@ -27,9 +28,12 @@
|
|||
<string name="a11y_pause">"Приостановить"</string>
|
||||
<string name="a11y_paused_voice_message">"Голосовое сообщение, длительность: %1$s, текущая позиция: %2$s"</string>
|
||||
<string name="a11y_pin_field">"Поле PIN-кода"</string>
|
||||
<string name="a11y_pinned_location">"Закреплённое местоположение"</string>
|
||||
<string name="a11y_play">"Воспроизвести"</string>
|
||||
<string name="a11y_playback_speed">"Скорость воспроизведения"</string>
|
||||
<string name="a11y_poll">"Опрос"</string>
|
||||
<string name="a11y_poll_end">"Завершённый опрос"</string>
|
||||
<string name="a11y_qr_code">"QR-код"</string>
|
||||
<string name="a11y_react_with">"Реагировать вместе с %1$s"</string>
|
||||
<string name="a11y_react_with_other_emojis">"Реакция с помощью эмодзи"</string>
|
||||
<string name="a11y_read_receipts_multiple">"Прочитано %1$s и %2$s"</string>
|
||||
|
|
@ -44,9 +48,11 @@
|
|||
<string name="a11y_remove_reaction_with">"Удалить реакцию %1$s"</string>
|
||||
<string name="a11y_room_avatar">"Аватар комнаты"</string>
|
||||
<string name="a11y_send_files">"Отправить файлы"</string>
|
||||
<string name="a11y_sender_location">"Местоположение отправителя"</string>
|
||||
<string name="a11y_session_verification_time_limited_action_required">"Требуется действие, на которое есть ограничение по времени, у вас есть одна минута для проверки"</string>
|
||||
<string name="a11y_show_password">"Показать пароль"</string>
|
||||
<string name="a11y_start_call">"Начать звонок"</string>
|
||||
<string name="a11y_start_voice_call">"Начать голосовой вызов"</string>
|
||||
<string name="a11y_tombstoned_room">"Брошенная комната"</string>
|
||||
<string name="a11y_user_avatar">"Аватар пользователя"</string>
|
||||
<string name="a11y_user_menu">"Меню пользователя"</string>
|
||||
|
|
@ -63,13 +69,13 @@
|
|||
<string name="action_back">"Назад"</string>
|
||||
<string name="action_call">"Позвонить"</string>
|
||||
<string name="action_cancel">"Отмена"</string>
|
||||
<string name="action_cancel_for_now">"Отмените сейчас"</string>
|
||||
<string name="action_cancel_for_now">"Пока отменить"</string>
|
||||
<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_confirm_password">"Подтверждение пароля"</string>
|
||||
<string name="action_continue">"Продолжить"</string>
|
||||
<string name="action_copy">"Копировать"</string>
|
||||
<string name="action_copy_caption">"Скопировать подпись"</string>
|
||||
|
|
@ -106,7 +112,7 @@
|
|||
<string name="action_invite">"Пригласить"</string>
|
||||
<string name="action_invite_friends">"Пригласить в комнату"</string>
|
||||
<string name="action_invite_friends_to_app">"Пригласить в %1$s"</string>
|
||||
<string name="action_invite_people_to_app">"Пригласите пользователей в %1$s"</string>
|
||||
<string name="action_invite_people_to_app">"Пригласить в %1$s"</string>
|
||||
<string name="action_invites_list">"Приглашения"</string>
|
||||
<string name="action_join">"Присоединиться"</string>
|
||||
<string name="action_learn_more">"Подробнее"</string>
|
||||
|
|
@ -115,50 +121,51 @@
|
|||
<string name="action_leave_room">"Покинуть комнату"</string>
|
||||
<string name="action_leave_space">"Покинуть пространство"</string>
|
||||
<string name="action_load_more">"Загрузить еще"</string>
|
||||
<string name="action_manage_account">"Настройки учетной записи"</string>
|
||||
<string name="action_manage_account">"Настройки аккаунта"</string>
|
||||
<string name="action_manage_devices">"Управление устройствами"</string>
|
||||
<string name="action_manage_rooms">"Управление комнатами"</string>
|
||||
<string name="action_message">"Сообщение"</string>
|
||||
<string name="action_message">"Написать"</string>
|
||||
<string name="action_minimize">"Свернуть"</string>
|
||||
<string name="action_next">"Далее"</string>
|
||||
<string name="action_no">"Нет"</string>
|
||||
<string name="action_not_now">"Не сейчас"</string>
|
||||
<string name="action_ok">"Ок"</string>
|
||||
<string name="action_open_context_menu">"Открыть контекстное меню"</string>
|
||||
<string name="action_ok">"ОК"</string>
|
||||
<string name="action_open_context_menu">"Открыть меню"</string>
|
||||
<string name="action_open_settings">"Открыть настройки"</string>
|
||||
<string name="action_open_with">"Открыть с помощью"</string>
|
||||
<string name="action_pin">"Закрепить"</string>
|
||||
<string name="action_quick_reply">"Быстрый ответ"</string>
|
||||
<string name="action_quote">"Цитата"</string>
|
||||
<string name="action_react">"Реакция"</string>
|
||||
<string name="action_quote">"Цитировать"</string>
|
||||
<string name="action_react">"Отреагировать"</string>
|
||||
<string name="action_reject">"Отклонить"</string>
|
||||
<string name="action_remove">"Удалить"</string>
|
||||
<string name="action_remove_caption">"Удалить подпись"</string>
|
||||
<string name="action_remove_message">"Удалить сообщение"</string>
|
||||
<string name="action_reply">"Ответить"</string>
|
||||
<string name="action_reply_in_thread">"Ответить в теме"</string>
|
||||
<string name="action_report">"Отчет"</string>
|
||||
<string name="action_reply_in_thread">"Ответить в ветке"</string>
|
||||
<string name="action_report">"Пожаловаться"</string>
|
||||
<string name="action_report_bug">"Сообщить об ошибке"</string>
|
||||
<string name="action_report_content">"Пожаловаться на содержание"</string>
|
||||
<string name="action_report_dm">"Пожаловаться на беседу"</string>
|
||||
<string name="action_report_room">"Комната отчетов"</string>
|
||||
<string name="action_report_room">"Пожаловаться на комнату"</string>
|
||||
<string name="action_reset">"Сбросить"</string>
|
||||
<string name="action_reset_identity">"Сбросить идентификацию"</string>
|
||||
<string name="action_reset_identity">"Сбросить личность"</string>
|
||||
<string name="action_retry">"Повторить"</string>
|
||||
<string name="action_retry_decryption">"Повторите расшифровку"</string>
|
||||
<string name="action_save">"Сохранить"</string>
|
||||
<string name="action_search">"Поиск"</string>
|
||||
<string name="action_select_all">"Выбрать все"</string>
|
||||
<string name="action_send">"Отправить"</string>
|
||||
<string name="action_send_edited_message">"Отправить изменённое сообщение"</string>
|
||||
<string name="action_send_edited_message">"Отправить отредактированное сообщение"</string>
|
||||
<string name="action_send_message">"Отправить сообщение"</string>
|
||||
<string name="action_send_voice_message">"Отправить голосовое сообщение"</string>
|
||||
<string name="action_share">"Поделиться"</string>
|
||||
<string name="action_share_link">"Поделиться ссылкой"</string>
|
||||
<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_sign_in_again">"Повторить вход"</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>
|
||||
|
|
@ -167,14 +174,14 @@
|
|||
<string name="action_static_map_load">"Нажмите, чтобы загрузить карту"</string>
|
||||
<string name="action_take_photo">"Сделать фото"</string>
|
||||
<string name="action_tap_for_options">"Нажмите для просмотра вариантов"</string>
|
||||
<string name="action_translate">"Перевод"</string>
|
||||
<string name="action_translate">"Перевести"</string>
|
||||
<string name="action_try_again">"Повторить попытку"</string>
|
||||
<string name="action_unpin">"Открепить"</string>
|
||||
<string name="action_view">"Просмотр"</string>
|
||||
<string name="action_view_in_timeline">"Просмотр в хронологии"</string>
|
||||
<string name="action_view">"Просмотреть"</string>
|
||||
<string name="action_view_in_timeline">"Просмотреть в хронологии"</string>
|
||||
<string name="action_view_source">"Показать источник"</string>
|
||||
<string name="action_yes">"Да"</string>
|
||||
<string name="action_yes_try_again">"Да, попробуйте еще раз"</string>
|
||||
<string name="action_yes_try_again">"Да, попробовать еще раз"</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_description">"Теперь ваш сервер поддерживает новый, более быстрый протокол. Чтобы обновить его прямо сейчас, выйдите и войдите в свою учётную запись снова. Сделав это сейчас, вы сможете избежать принудительного выхода из системы при последующем удалении старого протокола."</string>
|
||||
<string name="banner_migrate_to_native_sliding_sync_title">"Доступно обновление"</string>
|
||||
<string name="common_about">"О приложении"</string>
|
||||
|
|
@ -182,11 +189,12 @@
|
|||
<string name="common_add_account">"Добавить аккаунт"</string>
|
||||
<string name="common_add_another_account">"Добавить другой аккаунт"</string>
|
||||
<string name="common_adding_caption">"Добавление подписи"</string>
|
||||
<string name="common_advanced_settings">"Дополнительные настройки"</string>
|
||||
<string name="common_advanced_settings">"Расширенные настройки"</string>
|
||||
<string name="common_an_image">"изображение"</string>
|
||||
<string name="common_analytics">"Аналитика"</string>
|
||||
<string name="common_android_fetching_notifications_title">"Синхронизация уведомлений…"</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_android_shortcuts_remove_reason_session_logged_out">"Вы вышли из сессии"</string>
|
||||
<string name="common_appearance">"Внешний вид"</string>
|
||||
<string name="common_audio">"Аудио"</string>
|
||||
<string name="common_beta">"Бета-версия"</string>
|
||||
|
|
@ -205,8 +213,8 @@
|
|||
<string name="common_dark">"Темная"</string>
|
||||
<string name="common_decryption_error">"Ошибка расшифровки"</string>
|
||||
<string name="common_description">"Описание"</string>
|
||||
<string name="common_developer_options">"Для разработчика"</string>
|
||||
<string name="common_device_id">"Идентификатор устройства"</string>
|
||||
<string name="common_developer_options">"Для разработчиков"</string>
|
||||
<string name="common_device_id">"ID устройства"</string>
|
||||
<string name="common_direct_chat">"Личный чат"</string>
|
||||
<string name="common_do_not_show_this_again">"Не показывать больше"</string>
|
||||
<string name="common_download_failed">"Ошибка скачивания"</string>
|
||||
|
|
@ -234,16 +242,18 @@
|
|||
<string name="common_forward_message">"Переслать сообщение"</string>
|
||||
<string name="common_frequently_used">"Часто используемые"</string>
|
||||
<string name="common_gif">"GIF"</string>
|
||||
<string name="common_image">"Изображения"</string>
|
||||
<string name="common_in_reply_to">"В ответ на %1$s"</string>
|
||||
<string name="common_image">"Изображение"</string>
|
||||
<string name="common_in_reply_to">"В ответ %1$s"</string>
|
||||
<string name="common_install_apk_android">"Установить APK"</string>
|
||||
<string name="common_invite_unknown_profile">"Идентификатор Matrix ID не найден, приглашение может быть не получено."</string>
|
||||
<string name="common_leaving_room">"Покидаем комнату"</string>
|
||||
<string name="common_leaving_space">"Покинуть пространство"</string>
|
||||
<string name="common_leaving_space">"Покидаем пространство"</string>
|
||||
<string name="common_light">"Светлое"</string>
|
||||
<string name="common_line_copied_to_clipboard">"Строка скопирована в буфер обмена"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Ссылка скопирована в буфер обмена"</string>
|
||||
<string name="common_link_new_device">"Привязать новое устройство"</string>
|
||||
<string name="common_live_location">"Местоположение в реальном времени"</string>
|
||||
<string name="common_live_location_ended">"Трансляция местоположения завершена"</string>
|
||||
<string name="common_loading">"Загрузка…"</string>
|
||||
<string name="common_loading_more">"Загрузить больше…"</string>
|
||||
<plurals name="common_many_members">
|
||||
|
|
@ -272,11 +282,12 @@
|
|||
<string name="common_offline">"Не в сети"</string>
|
||||
<string name="common_open_source_licenses">"Лицензии с открытым исходным кодом"</string>
|
||||
<string name="common_or">"или"</string>
|
||||
<string name="common_other_options">"Другие опции"</string>
|
||||
<string name="common_password">"Пароль"</string>
|
||||
<string name="common_people">"Пользователи"</string>
|
||||
<string name="common_permalink">"Постоянная ссылка"</string>
|
||||
<string name="common_permission">"Разрешение"</string>
|
||||
<string name="common_pinned">"Закрепленный"</string>
|
||||
<string name="common_pinned">"Закреплено"</string>
|
||||
<string name="common_please_check_internet_connection">"Пожалуйста, проверьте подключение к Интернету"</string>
|
||||
<string name="common_please_wait">"Подождите…"</string>
|
||||
<string name="common_poll_end_confirmation">"Вы действительно хотите завершить данный опрос?"</string>
|
||||
|
|
@ -290,9 +301,11 @@
|
|||
</plurals>
|
||||
<string name="common_preparing">"Подготовка…"</string>
|
||||
<string name="common_privacy_policy">"Политика конфиденциальности"</string>
|
||||
<string name="common_private">"Приватный"</string>
|
||||
<string name="common_private_room">"Частная комната"</string>
|
||||
<string name="common_private_space">"Приватное пространство"</string>
|
||||
<string name="common_public_room">"Общедоступная комната"</string>
|
||||
<string name="common_private_space">"Частное пространство"</string>
|
||||
<string name="common_public">"Публичный"</string>
|
||||
<string name="common_public_room">"Публичная комната"</string>
|
||||
<string name="common_public_space">"Публичное пространство"</string>
|
||||
<string name="common_reaction">"Реакция"</string>
|
||||
<string name="common_reactions">"Реакции"</string>
|
||||
|
|
@ -305,11 +318,11 @@
|
|||
<item quantity="few">"%1$d ответа"</item>
|
||||
<item quantity="many">"%1$d ответов"</item>
|
||||
</plurals>
|
||||
<string name="common_replying_to">"Отвечает на %1$s"</string>
|
||||
<string name="common_replying_to">"Отвечает %1$s"</string>
|
||||
<string name="common_report_a_bug">"Сообщить об ошибке"</string>
|
||||
<string name="common_report_a_problem">"Сообщить о проблеме"</string>
|
||||
<string name="common_report_submitted">"Отчет отправлен"</string>
|
||||
<string name="common_rich_text_editor">"Редактор форматированного текста"</string>
|
||||
<string name="common_rich_text_editor">"Форматирование"</string>
|
||||
<string name="common_role">"Роль"</string>
|
||||
<string name="common_room">"Комната"</string>
|
||||
<string name="common_room_name">"Название комнаты"</string>
|
||||
|
|
@ -321,12 +334,12 @@
|
|||
</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>
|
||||
<string name="common_seen_by">"Просмотрено"</string>
|
||||
<string name="common_select_account">"Выберите учетную запись"</string>
|
||||
<string name="common_seen_by">"Просмотрели"</string>
|
||||
<string name="common_select_account">"Выберите аккаунт"</string>
|
||||
<plurals name="common_selected_count">
|
||||
<item quantity="one">"%1$d выбран"</item>
|
||||
<item quantity="few">"%1$d выбрано"</item>
|
||||
|
|
@ -343,20 +356,21 @@
|
|||
<string name="common_settings">"Настройки"</string>
|
||||
<string name="common_share_space">"Поделиться пространством"</string>
|
||||
<string name="common_shared_history">"Новые участники видят историю"</string>
|
||||
<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>
|
||||
<string name="common_space_members">"Участники пространства"</string>
|
||||
<string name="common_space_topic_placeholder">"О чём это пространство?"</string>
|
||||
<plurals name="common_spaces">
|
||||
<item quantity="one">"%1$d Пространство"</item>
|
||||
<item quantity="few">"%1$d Пространств"</item>
|
||||
<item quantity="many">"%1$d Пространств"</item>
|
||||
<item quantity="one">"%1$d пространство"</item>
|
||||
<item quantity="few">"%1$d пространства"</item>
|
||||
<item quantity="many">"%1$d пространств"</item>
|
||||
</plurals>
|
||||
<string name="common_starting_chat">"Чат запускается…"</string>
|
||||
<string name="common_starting_chat">"Создаем чат…"</string>
|
||||
<string name="common_sticker">"Стикер"</string>
|
||||
<string name="common_success">"Успешно"</string>
|
||||
<string name="common_suggested">"Рекомендуемые"</string>
|
||||
|
|
@ -364,8 +378,8 @@
|
|||
<string name="common_syncing">"Синхронизация"</string>
|
||||
<string name="common_system">"Системное"</string>
|
||||
<string name="common_text">"Текст"</string>
|
||||
<string name="common_third_party_notices">"Уведомление о третьей стороне"</string>
|
||||
<string name="common_thread">"Обсуждение"</string>
|
||||
<string name="common_third_party_notices">"Уведомления о третьих лицах"</string>
|
||||
<string name="common_thread">"Ветка"</string>
|
||||
<string name="common_topic">"Тема"</string>
|
||||
<string name="common_topic_placeholder">"О чем эта комната?"</string>
|
||||
<string name="common_unable_to_decrypt">"Невозможно расшифровать"</string>
|
||||
|
|
@ -382,8 +396,8 @@
|
|||
<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_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>
|
||||
|
|
@ -392,29 +406,32 @@
|
|||
<string name="common_video_quality_low">"Низкое качество"</string>
|
||||
<string name="common_video_quality_low_description">"Быстрая скорость загрузки и меньший размер файла"</string>
|
||||
<string name="common_video_quality_standard">"Стандартное качество"</string>
|
||||
<string name="common_video_quality_standard_description">"Сочетание качества и скорости загрузки"</string>
|
||||
<string name="common_video_quality_standard_description">"Баланс между качеством и скоростью загрузки"</string>
|
||||
<string name="common_voice_message">"Голосовое сообщение"</string>
|
||||
<string name="common_waiting">"Ожидание…"</string>
|
||||
<string name="common_waiting_for_decryption_key">"Ожидание ключа расшифровки"</string>
|
||||
<string name="common_waiting_live_location">"Ожидание трансляции местоположения…"</string>
|
||||
<string name="common_world_readable_history">"Любой может видеть историю"</string>
|
||||
<string name="common_you">"Вы"</string>
|
||||
<string name="crypto_event_key_forwarded_known_profile_dialog_content">"%1$s (%2$s) поделился этим сообщением, поскольку вас не было в комнате, когда оно было отправлено."</string>
|
||||
<string name="crypto_event_key_forwarded_unknown_profile_dialog_content">"%1$s поделился этим сообщением, поскольку вас не было в комнате, когда оно было отправлено."</string>
|
||||
<string name="crypto_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_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_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_withdraw_verification_action">"Вывод верификации"</string>
|
||||
<string name="dialog_confirm_link_message">"Ссылка %1$s ведет вас на другой сайт %2$s
|
||||
<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
|
||||
|
||||
Вы действительно хотите продолжить?"</string>
|
||||
Вы уверены, что хотите продолжить?"</string>
|
||||
<string name="dialog_confirm_link_title">"Перепроверьте эту ссылку"</string>
|
||||
<string name="dialog_default_video_quality_selector_subtitle">"Выберите качество загружаемых видео по умолчанию."</string>
|
||||
<string name="dialog_default_video_quality_selector_title">"Качество загружаемого видео"</string>
|
||||
<string name="dialog_file_too_large_to_upload_subtitle">"Максимально допустимый размер файла: %1$s"</string>
|
||||
<string name="dialog_file_too_large_to_upload_title">"Размер файла слишком большой для загрузки."</string>
|
||||
<string name="dialog_room_reported">"Сообщение о комнате"</string>
|
||||
<string name="dialog_room_reported">"Жалоба на комнату отправлена"</string>
|
||||
<string name="dialog_room_reported_and_left">"Пожаловался и покинул комнату"</string>
|
||||
<string name="dialog_title_confirmation">"Подтверждение"</string>
|
||||
<string name="dialog_title_error">"Ошибка"</string>
|
||||
|
|
@ -424,28 +441,29 @@
|
|||
<string name="dialog_unsaved_changes_description_android">"Изменения не сохранены. Вы действительно хотите вернуться?"</string>
|
||||
<string name="dialog_unsaved_changes_title">"Сохранить изменения?"</string>
|
||||
<string name="dialog_video_quality_selector_subtitle_file_size">"Максимально допустимый размер файла: %1$s"</string>
|
||||
<string name="dialog_video_quality_selector_subtitle_no_file_size">"Выберите качество видео, которое вы хотите загрузить."</string>
|
||||
<string name="dialog_video_quality_selector_subtitle_no_file_size">"Выберите качество для видео, которое вы хотите загрузить."</string>
|
||||
<string name="dialog_video_quality_selector_title">"Выберите качество загружаемого видео"</string>
|
||||
<string name="emoji_picker_search_placeholder">"Поиск эмодзи"</string>
|
||||
<string name="error_account_already_logged_in">"Вы уже вошли на данном устройстве как %1$s."</string>
|
||||
<string name="error_account_creation_not_possible">"Ваш домашний сервер необходимо обновить, чтобы он поддерживал Matrix Authentication Service и создание учётных записей."</string>
|
||||
<string name="error_account_creation_not_possible">"Домашний сервер необходимо обновить, чтобы он поддерживал Matrix Authentication Service и создание учётных записей."</string>
|
||||
<string name="error_failed_creating_the_permalink">"Не удалось создать постоянную ссылку"</string>
|
||||
<string name="error_failed_loading_map">"Не удалось загрузить карту %1$s. Пожалуйста, повторите попытку позже."</string>
|
||||
<string name="error_failed_loading_messages">"Не удалось загрузить сообщения"</string>
|
||||
<string name="error_failed_locating_user">"%1$s не удалось получить доступ к вашему местоположению. Пожалуйста, повторите попытку позже."</string>
|
||||
<string name="error_failed_uploading_voice_message">"Не удалось загрузить голосовое сообщение."</string>
|
||||
<string name="error_invalid_invite">"Комната больше не существует или приглашение не действительно."</string>
|
||||
<string name="error_invalid_invite">"Комната не существует или приглашение недействительно."</string>
|
||||
<string name="error_location_service_disabled_android">"Включите геолокацию для доступа к её функциям."</string>
|
||||
<string name="error_message_not_found">"Сообщение не найдено"</string>
|
||||
<string name="error_missing_location_auth_android">"У %1$s нет разрешения на доступ к вашему местоположению. Вы можете разрешить доступ в Настройках."</string>
|
||||
<string name="error_missing_location_rationale_android">"У %1$s нет разрешения на доступ к вашему местоположению. Разрешите доступ ниже."</string>
|
||||
<string name="error_missing_microphone_voice_rationale_android">"%1$s не имеет разрешения на доступ к вашему микрофону. Разрешите доступ к записи голосового сообщения."</string>
|
||||
<string name="error_network_or_server_issue">"Это может быть связано с проблемами сети или сервера."</string>
|
||||
<string name="error_room_address_already_exists">"Такой адрес комнаты уже существует, попробуйте отредактировать поле адреса комнаты или изменить название комнаты"</string>
|
||||
<string name="error_room_address_invalid_symbols">"Некоторые символы не допускаются. Поддерживаются только буквы, цифры и следующие символы! $ & \'() * +/; =? @ [] - . _"</string>
|
||||
<string name="error_room_address_already_exists">"Такой адрес комнаты уже существует. Смените адрес или имя комнаты"</string>
|
||||
<string name="error_room_address_invalid_symbols">"Некоторые символы запрещены. Поддерживаются только буквы A-Z, цифры и символы ! $ & \'() * +/; =? @ [] - . _"</string>
|
||||
<string name="error_some_messages_have_not_been_sent">"Некоторые сообщения не были отправлены"</string>
|
||||
<string name="error_unknown">"Извините, произошла ошибка"</string>
|
||||
<string name="error_unknown">"Произошла ошибка"</string>
|
||||
<string name="invite_friends_rich_title">"🔐️ Присоединяйтесь ко мне в %1$s"</string>
|
||||
<string name="invite_friends_text">"Привет, поговори со мной по %1$s: %2$s"</string>
|
||||
<string name="invite_friends_text">"Привет, давай поболтаем в %1$s: %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<string name="preference_rageshake">"Встряхните устройство, чтобы сообщить об ошибке"</string>
|
||||
<string name="screen_bug_report_a11y_screenshot">"Скриншот"</string>
|
||||
|
|
@ -453,8 +471,9 @@
|
|||
<string name="screen_create_poll_options_section_title">"Параметры"</string>
|
||||
<string name="screen_create_poll_remove_accessibility_label">"Удалить %1$s"</string>
|
||||
<string name="screen_create_poll_settings_section_title">"Настройки"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Не удалось выбрать носитель, попробуйте еще раз."</string>
|
||||
<string name="screen_pinned_timeline_empty_state_description">"Нажмите на сообщение и выберите “%1$s”, чтобы добавить его сюда."</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Не удалось выбрать медиа, попробуйте еще раз."</string>
|
||||
<string name="screen_onboarding_welcome_back">"С возвращением"</string>
|
||||
<string name="screen_pinned_timeline_empty_state_description">"Нажмите на сообщение и выберите «%1$s», чтобы добавить его сюда."</string>
|
||||
<string name="screen_pinned_timeline_empty_state_headline">"Закрепите важные сообщения, чтобы их можно было легко найти"</string>
|
||||
<plurals name="screen_pinned_timeline_screen_title">
|
||||
<item quantity="one">"%1$d закреплённое сообщение"</item>
|
||||
|
|
@ -462,17 +481,17 @@
|
|||
<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_resolve_send_failure_changed_identity_primary_button_title">"Отозвать статус и отправить"</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_subtitle">"Вы можете либо отозвать свой статус подтверждения и всё равно отправить это сообщение, либо отменить его сейчас и повторить попытку после повторного подтверждения %1$s."</string>
|
||||
<string name="screen_reset_identity_confirmation_subtitle">"Вы собираетесь перейти в свой аккаунт %1$s, чтобы сбросить личность. После этого Вы вернётесь в приложение."</string>
|
||||
<string name="screen_reset_identity_confirmation_title">"Не можете подтвердить? Перейдите в свой аккаунт, чтобы сбросить свою личность."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_primary_button_title">"Сбросить верификацию и отправить"</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_subtitle">"Вы можете либо сбросить подтверждение и всё равно отправить это сообщение, либо отменить его сейчас и повторить попытку после повторного подтверждения %1$s."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_title">"Ваше сообщение не было отправлено, потому что подтвержденная личность %1$s была сброшена"</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_primary_button_title">"Отправь сообщение в любом случае"</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_subtitle">"%1$s использует одно или несколько непроверенных устройств. Вы все равно можете отправить сообщение или отменить его пока и повторить попытку позже %2$s, проверив все устройства пользователя."</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_title">"Ваше сообщение не было отправлено, потому что %1$s не проверил одно или несколько устройств"</string>
|
||||
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"Одно или несколько ваших устройств не проверены. Вы можете отправить сообщение в любом случае или отменить его пока и повторить попытку позже, проверив все свои устройства."</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_primary_button_title">"Все равно отправить сообщение"</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_subtitle">"У %1$s есть одно или несколько неподтвержденных устройств. Вы все равно можете отправить сообщение или отменить его пока и повторить попытку позже, когда %2$s подтвердить все свои устройства."</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_title">"Ваше сообщение не было отправлено, потому что %1$s имеет неподтвержденные устройства"</string>
|
||||
<string name="screen_resolve_send_failure_you_unsigned_device_subtitle">"Одно или несколько ваших устройств не подтверждены. Вы можете отправить сообщение в любом случае или отменить его пока и повторить попытку позже, подтвердив все свои устройства."</string>
|
||||
<string name="screen_resolve_send_failure_you_unsigned_device_title">"Ваше сообщение не было отправлено, поскольку вы не подтвердили одно или несколько своих устройств."</string>
|
||||
<string name="screen_room_change_role_administrators_or_owners_title">"Редактировать роль владельца и администратора"</string>
|
||||
<string name="screen_room_change_role_administrators_or_owners_title">"Редактировать владельцев и администраторов"</string>
|
||||
<string name="screen_room_error_failed_processing_media">"Не удалось обработать медиафайл для загрузки, попробуйте еще раз."</string>
|
||||
<string name="screen_room_error_failed_retrieving_user_details">"Не удалось получить данные о пользователе"</string>
|
||||
<string name="screen_room_event_pill">"Сообщение в %1$s"</string>
|
||||
|
|
@ -480,7 +499,7 @@
|
|||
<string name="screen_room_grouped_state_events_reduce">"Уменьшить"</string>
|
||||
<string name="screen_room_permalink_same_room_android">"Эта комната уже просматривается!"</string>
|
||||
<string name="screen_room_pinned_banner_indicator">"%1$s из %2$s"</string>
|
||||
<string name="screen_room_pinned_banner_indicator_description">"%1$s Закрепленные сообщения"</string>
|
||||
<string name="screen_room_pinned_banner_indicator_description">"%1$s закрепленные сообщения"</string>
|
||||
<string name="screen_room_pinned_banner_loading_description">"Загрузка сообщения…"</string>
|
||||
<string name="screen_room_pinned_banner_view_all_button_title">"Посмотреть все"</string>
|
||||
<string name="screen_room_title">"Чат"</string>
|
||||
|
|
@ -489,21 +508,24 @@
|
|||
<string name="screen_share_open_apple_maps">"Открыть в Apple Maps"</string>
|
||||
<string name="screen_share_open_google_maps">"Открыть в Google Maps"</string>
|
||||
<string name="screen_share_open_osm_maps">"Открыть в OpenStreetMap"</string>
|
||||
<string name="screen_share_this_location_action">"Поделиться этим местоположением"</string>
|
||||
<string name="screen_share_this_location_action">"Поделиться выбранным местоположением"</string>
|
||||
<string name="screen_sharing_location_option_sheet_title">"Параметры общего доступа"</string>
|
||||
<string name="screen_space_list_description">"Пространства, которые вы создали или к которым присоединились."</string>
|
||||
<string name="screen_space_list_details">"%1$s • %2$s"</string>
|
||||
<string name="screen_space_list_empty_state_title">"Создавайте пространства для организации комнат"</string>
|
||||
<string name="screen_space_list_empty_state_title">"Создайте пространства для организации комнат"</string>
|
||||
<string name="screen_space_list_parent_space">"%1$s пространство"</string>
|
||||
<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>
|
||||
<string name="screen_timeline_item_menu_send_failure_unsigned_device">"Сообщение не отправлено, потому что %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_view_location_title">"Местоположение"</string>
|
||||
<string name="settings_version_number">"Версия: %1$s (%2$s)"</string>
|
||||
<string name="test_language_identifier">"ru"</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>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@
|
|||
<string name="a11y_your_avatar">"Ваш аватар"</string>
|
||||
<string name="action_accept">"Прийняти"</string>
|
||||
<string name="action_add_caption">"Додати підпис"</string>
|
||||
<string name="action_add_existing_rooms">"Додати наявні кімнати"</string>
|
||||
<string name="action_add_to_timeline">"Додати до стрічки"</string>
|
||||
<string name="action_back">"Назад"</string>
|
||||
<string name="action_call">"Зателефонувати"</string>
|
||||
|
|
@ -77,11 +78,13 @@
|
|||
<string name="action_copy_text">"Скопіювати текст"</string>
|
||||
<string name="action_create">"Створити"</string>
|
||||
<string name="action_create_room">"Створити кімнату"</string>
|
||||
<string name="action_create_space">"Створити простір"</string>
|
||||
<string name="action_deactivate">"Деактивувати"</string>
|
||||
<string name="action_deactivate_account">"Деактивувати обліковий запис"</string>
|
||||
<string name="action_decline">"Відхилити"</string>
|
||||
<string name="action_decline_and_block">"Відхилити та заблокувати"</string>
|
||||
<string name="action_delete_poll">"Видалити опитування"</string>
|
||||
<string name="action_deselect_all">"Скасувати вибір усіх"</string>
|
||||
<string name="action_disable">"Вимкнути"</string>
|
||||
<string name="action_discard">"Відкинути"</string>
|
||||
<string name="action_dismiss">"Відхилити"</string>
|
||||
|
|
@ -96,6 +99,8 @@
|
|||
<string name="action_forgot_password">"Забули пароль?"</string>
|
||||
<string name="action_forward">"Переслати"</string>
|
||||
<string name="action_go_back">"Повернутися"</string>
|
||||
<string name="action_go_to_roles_and_permissions">"Перейти до ролей і дозволів"</string>
|
||||
<string name="action_go_to_settings">"Перейти до налаштувань"</string>
|
||||
<string name="action_ignore">"Ігнорувати"</string>
|
||||
<string name="action_invite">"Запросити"</string>
|
||||
<string name="action_invite_friends">"Запросити людей"</string>
|
||||
|
|
@ -107,10 +112,13 @@
|
|||
<string name="action_leave">"Вийти"</string>
|
||||
<string name="action_leave_conversation">"Залишити розмову"</string>
|
||||
<string name="action_leave_room">"Вийти з кімнати"</string>
|
||||
<string name="action_leave_space">"Вийти з простору"</string>
|
||||
<string name="action_load_more">"Завантажити ще"</string>
|
||||
<string name="action_manage_account">"Керування обліковим записом"</string>
|
||||
<string name="action_manage_devices">"Керування пристроями"</string>
|
||||
<string name="action_manage_rooms">"Керувати кімнатами"</string>
|
||||
<string name="action_message">"Написати"</string>
|
||||
<string name="action_minimize">"Згорнути"</string>
|
||||
<string name="action_next">"Далі"</string>
|
||||
<string name="action_no">"Ні"</string>
|
||||
<string name="action_not_now">"Не зараз"</string>
|
||||
|
|
@ -139,6 +147,7 @@
|
|||
<string name="action_retry_decryption">"Повторити спробу розшифрування"</string>
|
||||
<string name="action_save">"Зберегти"</string>
|
||||
<string name="action_search">"Шукати"</string>
|
||||
<string name="action_select_all">"Вибрати все"</string>
|
||||
<string name="action_send">"Надіслати"</string>
|
||||
<string name="action_send_edited_message">"Надіслати змінене повідомлення"</string>
|
||||
<string name="action_send_message">"Надіслати повідомлення"</string>
|
||||
|
|
@ -157,6 +166,7 @@
|
|||
<string name="action_static_map_load">"Натисніть, щоб завантажити мапу"</string>
|
||||
<string name="action_take_photo">"Зробити фото"</string>
|
||||
<string name="action_tap_for_options">"Торкніться, щоб переглянути параметри"</string>
|
||||
<string name="action_translate">"Перекласти"</string>
|
||||
<string name="action_try_again">"Спробуйте ще раз"</string>
|
||||
<string name="action_unpin">"Відкріпити"</string>
|
||||
<string name="action_view">"Переглянути"</string>
|
||||
|
|
@ -178,6 +188,7 @@
|
|||
<string name="common_android_shortcuts_remove_reason_session_logged_out">"Ви вийшли з сеансу"</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_started">"Виклик розпочато"</string>
|
||||
|
|
@ -185,11 +196,13 @@
|
|||
<string name="common_copied_to_clipboard">"Скопійовано до буферу обміну"</string>
|
||||
<string name="common_copyright">"Авторське право"</string>
|
||||
<string name="common_creating_room">"Створення кімнати…"</string>
|
||||
<string name="common_creating_space">"Створення простору…"</string>
|
||||
<string name="common_current_user_canceled_knock">"Запит скасовано"</string>
|
||||
<string name="common_current_user_left_room">"Виходить з кімнати"</string>
|
||||
<string name="common_current_user_rejected_invite">"Запрошення відхилено"</string>
|
||||
<string name="common_dark">"Темна"</string>
|
||||
<string name="common_decryption_error">"Помилка розшифрування"</string>
|
||||
<string name="common_description">"Опис"</string>
|
||||
<string name="common_developer_options">"Налаштування розробника"</string>
|
||||
<string name="common_device_id">"Ідентифікатор пристрою"</string>
|
||||
<string name="common_direct_chat">"Особиста бесіда"</string>
|
||||
|
|
@ -224,9 +237,11 @@
|
|||
<string name="common_install_apk_android">"Встановити APK"</string>
|
||||
<string name="common_invite_unknown_profile">"Цей Matrix-ID не знайдено, тому запрошення може не бути отримано."</string>
|
||||
<string name="common_leaving_room">"Вихід з кімнати"</string>
|
||||
<string name="common_leaving_space">"Вихід з простору"</string>
|
||||
<string name="common_light">"Світла"</string>
|
||||
<string name="common_line_copied_to_clipboard">"Рядок скопійовано до буфера обміну"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Посилання скопійовано в буфер обміну"</string>
|
||||
<string name="common_link_new_device">"Під\'єднати новий пристрій"</string>
|
||||
<string name="common_loading">"Завантаження"</string>
|
||||
<string name="common_loading_more">"Завантаження наступних…"</string>
|
||||
<plurals name="common_many_members">
|
||||
|
|
@ -241,13 +256,16 @@
|
|||
</plurals>
|
||||
<string name="common_message">"Повідомлення"</string>
|
||||
<string name="common_message_actions">"Дії з повідомленнями"</string>
|
||||
<string name="common_message_failed_to_send">"Не вдалося надіслати повідомлення"</string>
|
||||
<string name="common_message_layout">"Макет повідомлень"</string>
|
||||
<string name="common_message_removed">"Повідомлення вилучено"</string>
|
||||
<string name="common_modern">"Модерн"</string>
|
||||
<string name="common_mute">"Вимкнути звук"</string>
|
||||
<string name="common_name">"Назва"</string>
|
||||
<string name="common_name_and_id">"%1$s (%2$s)"</string>
|
||||
<string name="common_no_results">"Немає результатів"</string>
|
||||
<string name="common_no_room_name">"Немає назви кімнати"</string>
|
||||
<string name="common_no_space_name">"Без назви простору"</string>
|
||||
<string name="common_not_encrypted">"Не зашифровано"</string>
|
||||
<string name="common_offline">"Не в мережі"</string>
|
||||
<string name="common_open_source_licenses">"Ліцензії відкритого коду"</string>
|
||||
|
|
@ -279,6 +297,7 @@
|
|||
<string name="common_reason">"Причина"</string>
|
||||
<string name="common_recovery_key">"Ключ відновлення"</string>
|
||||
<string name="common_refreshing">"Оновлення…"</string>
|
||||
<string name="common_removing">"Вилучення…"</string>
|
||||
<plurals name="common_replies">
|
||||
<item quantity="one">"%1$d відповідь"</item>
|
||||
<item quantity="few">"%1$d відповіді"</item>
|
||||
|
|
@ -289,9 +308,10 @@
|
|||
<string name="common_report_a_problem">"Повідомити про проблему"</string>
|
||||
<string name="common_report_submitted">"Звіт подано"</string>
|
||||
<string name="common_rich_text_editor">"Багатоформатний текстовий редактор"</string>
|
||||
<string name="common_role">"Роль"</string>
|
||||
<string name="common_room">"Кімната"</string>
|
||||
<string name="common_room_name">"Назва кімнати"</string>
|
||||
<string name="common_room_name_placeholder">"напр., назва вашого проєкту"</string>
|
||||
<string name="common_room_name_placeholder">"наприклад, назва вашого проєкту"</string>
|
||||
<plurals name="common_rooms">
|
||||
<item quantity="one">"%1$d кімната"</item>
|
||||
<item quantity="few">"%1$d кімнати"</item>
|
||||
|
|
@ -311,13 +331,16 @@
|
|||
<string name="common_sent">"Надіслано"</string>
|
||||
<string name="common_sentence_delimiter">". "</string>
|
||||
<string name="common_server_not_supported">"Сервер не підтримується"</string>
|
||||
<string name="common_server_unreachable">"Сервер недоступний"</string>
|
||||
<string name="common_server_url">"URL-адреса сервера"</string>
|
||||
<string name="common_settings">"Налаштування"</string>
|
||||
<string name="common_shared_history">"Нові учасники бачать історію"</string>
|
||||
<string name="common_shared_location">"Поширене розташування"</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>
|
||||
<string name="common_space_topic_placeholder">"Про що цей простір?"</string>
|
||||
<plurals name="common_spaces">
|
||||
<item quantity="one">"%1$d простір"</item>
|
||||
<item quantity="few">"%1$d простори"</item>
|
||||
|
|
@ -362,6 +385,7 @@
|
|||
<string name="common_voice_message">"Голосове повідомлення"</string>
|
||||
<string name="common_waiting">"Очікування…"</string>
|
||||
<string name="common_waiting_for_decryption_key">"Чекаємо на це повідомлення"</string>
|
||||
<string name="common_world_readable_history">"Будь-хто може переглянути історію"</string>
|
||||
<string name="common_you">"Ви"</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>
|
||||
|
|
@ -389,7 +413,7 @@
|
|||
<string name="dialog_video_quality_selector_subtitle_file_size">"Максимально дозволений розмір файлу: %1$s"</string>
|
||||
<string name="dialog_video_quality_selector_subtitle_no_file_size">"Виберіть якість відео, яке ви хочете вивантажити."</string>
|
||||
<string name="dialog_video_quality_selector_title">"Виберіть якість вивантажуваного відео"</string>
|
||||
<string name="emoji_picker_search_placeholder">"Пошук емодзі"</string>
|
||||
<string name="emoji_picker_search_placeholder">"Пошук емоджі"</string>
|
||||
<string name="error_account_already_logged_in">"Ви вже ввійшли на цьому пристрої як %1$s."</string>
|
||||
<string name="error_account_creation_not_possible">"Ваш домашній сервер потрібно оновити, щоб він підтримував службу автентифікації Matrix і створення облікових записів."</string>
|
||||
<string name="error_failed_creating_the_permalink">"Не вдалося створити постійне посилання"</string>
|
||||
|
|
@ -455,6 +479,7 @@
|
|||
<string name="screen_share_this_location_action">"Поділитися цим місцем перебування"</string>
|
||||
<string name="screen_space_list_description">"Простори, які ви створили або до яких приєдналися."</string>
|
||||
<string name="screen_space_list_details">"%1$s • %2$s"</string>
|
||||
<string name="screen_space_list_parent_space">"Простір %1$s"</string>
|
||||
<string name="screen_space_list_title">"Простори"</string>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@
|
|||
<string name="action_leave_space">"Leave space"</string>
|
||||
<string name="action_load_more">"Load more"</string>
|
||||
<string name="action_manage_account">"Manage account"</string>
|
||||
<string name="action_manage_account_and_devices">"Manage account & devices"</string>
|
||||
<string name="action_manage_devices">"Manage devices"</string>
|
||||
<string name="action_manage_rooms">"Manage rooms"</string>
|
||||
<string name="action_message">"Message"</string>
|
||||
|
|
@ -162,14 +163,15 @@
|
|||
<string name="action_share_live_location">"Share live location"</string>
|
||||
<string name="action_show">"Show"</string>
|
||||
<string name="action_sign_in_again">"Sign in again"</string>
|
||||
<string name="action_signout">"Sign out"</string>
|
||||
<string name="action_signout_anyway">"Sign out anyway"</string>
|
||||
<string name="action_signout">"Remove this device"</string>
|
||||
<string name="action_signout_anyway">"Remove this device anyway"</string>
|
||||
<string name="action_skip">"Skip"</string>
|
||||
<string name="action_start">"Start"</string>
|
||||
<string name="action_start_chat">"Start chat"</string>
|
||||
<string name="action_start_over">"Start over"</string>
|
||||
<string name="action_start_verification">"Start verification"</string>
|
||||
<string name="action_static_map_load">"Tap to load map"</string>
|
||||
<string name="action_stop">"Stop"</string>
|
||||
<string name="action_take_photo">"Take photo"</string>
|
||||
<string name="action_tap_for_options">"Tap for options"</string>
|
||||
<string name="action_translate">"Translate"</string>
|
||||
|
|
@ -190,6 +192,7 @@
|
|||
<string name="common_advanced_settings">"Advanced settings"</string>
|
||||
<string name="common_an_image">"an image"</string>
|
||||
<string name="common_analytics">"Analytics"</string>
|
||||
<string name="common_android_fetching_notifications_title">"Syncing notifications…"</string>
|
||||
<string name="common_android_shortcuts_remove_reason_left_room">"You left the room"</string>
|
||||
<string name="common_android_shortcuts_remove_reason_session_logged_out">"You were logged out of the session"</string>
|
||||
<string name="common_appearance">"Appearance"</string>
|
||||
|
|
@ -223,6 +226,7 @@
|
|||
<string name="common_empty_file">"Empty file"</string>
|
||||
<string name="common_encryption">"Encryption"</string>
|
||||
<string name="common_encryption_enabled">"Encryption enabled"</string>
|
||||
<string name="common_ends_at">"Ends at %1$s"</string>
|
||||
<string name="common_enter_your_pin">"Enter your PIN"</string>
|
||||
<string name="common_error">"Error"</string>
|
||||
<string name="common_error_registering_pusher_android">"An error occurred, you may not receive notifications for new messages. Please troubleshoot notifications from the settings.
|
||||
|
|
@ -249,6 +253,8 @@ Reason: %1$s."</string>
|
|||
<string name="common_line_copied_to_clipboard">"Line copied to clipboard"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Link copied to clipboard"</string>
|
||||
<string name="common_link_new_device">"Link new device"</string>
|
||||
<string name="common_live_location">"Live location"</string>
|
||||
<string name="common_live_location_ended">"Live location ended"</string>
|
||||
<string name="common_loading">"Loading…"</string>
|
||||
<string name="common_loading_more">"Loading more…"</string>
|
||||
<plurals name="common_many_members">
|
||||
|
|
@ -345,9 +351,10 @@ Reason: %1$s."</string>
|
|||
<string name="common_settings">"Settings"</string>
|
||||
<string name="common_share_space">"Share space"</string>
|
||||
<string name="common_shared_history">"New members see history"</string>
|
||||
<string name="common_shared_live_location">"Shared live location"</string>
|
||||
<string name="common_shared_location">"Shared location"</string>
|
||||
<string name="common_shared_space">"Shared space"</string>
|
||||
<string name="common_signing_out">"Signing out"</string>
|
||||
<string name="common_signing_out">"Removing device"</string>
|
||||
<string name="common_something_went_wrong">"Something went wrong"</string>
|
||||
<string name="common_something_went_wrong_message">"We encountered an issue. Please try again."</string>
|
||||
<string name="common_space">"Space"</string>
|
||||
|
|
@ -372,7 +379,7 @@ Reason: %1$s."</string>
|
|||
<string name="common_unable_to_decrypt">"Unable to decrypt"</string>
|
||||
<string name="common_unable_to_decrypt_insecure_device">"Sent from an insecure device"</string>
|
||||
<string name="common_unable_to_decrypt_no_access">"You don\'t have access to this message"</string>
|
||||
<string name="common_unable_to_decrypt_verification_violation">"Sender\'s verified identity was reset"</string>
|
||||
<string name="common_unable_to_decrypt_verification_violation">"Sender\'s verified digital identity was reset"</string>
|
||||
<string name="common_unable_to_invite_message">"Invites couldn\'t be sent to one or more users."</string>
|
||||
<string name="common_unable_to_invite_title">"Unable to send invite(s)"</string>
|
||||
<string name="common_unlock">"Unlock"</string>
|
||||
|
|
@ -397,16 +404,17 @@ Reason: %1$s."</string>
|
|||
<string name="common_voice_message">"Voice message"</string>
|
||||
<string name="common_waiting">"Waiting…"</string>
|
||||
<string name="common_waiting_for_decryption_key">"Waiting for this message"</string>
|
||||
<string name="common_waiting_live_location">"Waiting for live location…"</string>
|
||||
<string name="common_world_readable_history">"Anyone can see history"</string>
|
||||
<string name="common_you">"You"</string>
|
||||
<string name="crypto_event_key_forwarded_known_profile_dialog_content">"%1$s (%2$s) shared this message since you were not in the room when it was sent."</string>
|
||||
<string name="crypto_event_key_forwarded_unknown_profile_dialog_content">"%1$s shared this message since you were not in the room when it was sent."</string>
|
||||
<string name="crypto_history_visible">"This room has been configured so that new members can read history. %1$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation">"%1$s\'s identity was reset. %2$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation_new">"%1$s’s %2$s identity was reset. %3$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation">"%1$s\'s digital identity was reset. %2$s"</string>
|
||||
<string name="crypto_identity_change_pin_violation_new">"%1$s’s %2$s digital identity was reset. %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’s identity was reset."</string>
|
||||
<string name="crypto_identity_change_verification_violation_new">"%1$s’s %2$s identity was reset. %3$s"</string>
|
||||
<string name="crypto_identity_change_profile_pin_violation">"%1$s’s digital identity was reset."</string>
|
||||
<string name="crypto_identity_change_verification_violation_new">"%1$s’s %2$s digital identity was reset. %3$s"</string>
|
||||
<string name="crypto_identity_change_withdraw_verification_action">"Withdraw verification"</string>
|
||||
<string name="dialog_allow_access">"Allow access"</string>
|
||||
<string name="dialog_confirm_link_message">"The link %1$s is taking you to another site %2$s
|
||||
|
|
@ -466,11 +474,11 @@ Are you sure you want to continue?"</string>
|
|||
<item quantity="other">"%1$d Pinned messages"</item>
|
||||
</plurals>
|
||||
<string name="screen_pinned_timeline_screen_title_empty">"Pinned messages"</string>
|
||||
<string name="screen_reset_identity_confirmation_subtitle">"You\'re about to go to your %1$s account to reset your identity. Afterwards you\'ll be taken back to the app."</string>
|
||||
<string name="screen_reset_identity_confirmation_title">"Can\'t confirm? Go to your account to reset your identity."</string>
|
||||
<string name="screen_reset_identity_confirmation_subtitle">"You\'re about to go to your %1$s account to reset your digital identity. Afterwards you\'ll be taken back to the app."</string>
|
||||
<string name="screen_reset_identity_confirmation_title">"Can\'t confirm? Go to your account to reset your digital identity."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_primary_button_title">"Withdraw verification and send"</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_subtitle">"You can withdraw your verification and send this message anyway, or you can cancel for now and try again later after reverifying %1$s."</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_title">"Your message was not sent because %1$s’s verified identity was reset"</string>
|
||||
<string name="screen_resolve_send_failure_changed_identity_title">"Your message was not sent because %1$s’s verified digital identity was reset"</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_primary_button_title">"Send message anyway"</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_subtitle">"%1$s is using one or more unverified devices. You can send the message anyway, or you can cancel for now and try again later after %2$s has verified all their devices."</string>
|
||||
<string name="screen_resolve_send_failure_unsigned_device_title">"Your message was not sent because %1$s has not verified all devices"</string>
|
||||
|
|
@ -488,12 +496,14 @@ Are you sure you want to continue?"</string>
|
|||
<string name="screen_room_pinned_banner_loading_description">"Loading message…"</string>
|
||||
<string name="screen_room_pinned_banner_view_all_button_title">"View All"</string>
|
||||
<string name="screen_room_title">"Chat"</string>
|
||||
<string name="screen_share_location_live_location_duration_picker_title">"Choose how long to share your live location."</string>
|
||||
<string name="screen_share_location_title">"Share location"</string>
|
||||
<string name="screen_share_my_location_action">"Share my location"</string>
|
||||
<string name="screen_share_open_apple_maps">"Open in Apple Maps"</string>
|
||||
<string name="screen_share_open_google_maps">"Open in Google Maps"</string>
|
||||
<string name="screen_share_open_osm_maps">"Open in OpenStreetMap"</string>
|
||||
<string name="screen_share_this_location_action">"Share selected location"</string>
|
||||
<string name="screen_sharing_location_option_sheet_title">"Sharing options"</string>
|
||||
<string name="screen_space_list_description">"Spaces you have created or joined."</string>
|
||||
<string name="screen_space_list_details">"%1$s • %2$s"</string>
|
||||
<string name="screen_space_list_empty_state_title">"Create spaces to organize rooms"</string>
|
||||
|
|
@ -501,7 +511,7 @@ Are you sure you want to continue?"</string>
|
|||
<string name="screen_space_list_title">"Spaces"</string>
|
||||
<string name="screen_static_location_sheet_timestamp_description">"Shared %1$s"</string>
|
||||
<string name="screen_static_location_sheet_title">"On the map"</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Message not sent because %1$s’s verified identity was reset."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_changed_identity">"Message not sent because %1$s’s verified digital identity was reset."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_unsigned_device">"Message not sent because %1$s has not verified all devices."</string>
|
||||
<string name="screen_timeline_item_menu_send_failure_you_unsigned_device">"Message not sent because you have not verified one or more of your devices."</string>
|
||||
<string name="screen_view_location_title">"Location"</string>
|
||||
|
|
@ -512,5 +522,5 @@ Are you sure you want to continue?"</string>
|
|||
<string name="timeline_decryption_failure_historical_event_unverified_device">"You need to verify this device for access to historical messages"</string>
|
||||
<string name="timeline_decryption_failure_historical_event_user_not_joined">"You don\'t have access to this message"</string>
|
||||
<string name="timeline_decryption_failure_unable_to_decrypt">"Unable to decrypt message"</string>
|
||||
<string name="timeline_decryption_failure_withheld_unverified">"This message was blocked either because you did not verify your device or because the sender needs to verify your identity."</string>
|
||||
<string name="timeline_decryption_failure_withheld_unverified">"This message was blocked either because you did not verify your device or because the sender needs to verify your digital identity."</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.graphics
|
||||
|
||||
import androidx.compose.ui.draw.CacheDrawScope
|
||||
import androidx.compose.ui.graphics.Outline
|
||||
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
|
||||
import androidx.compose.ui.graphics.layer.CompositingStrategy
|
||||
import androidx.compose.ui.graphics.layer.drawLayer
|
||||
import androidx.compose.ui.graphics.layer.setOutline
|
||||
|
||||
/**
|
||||
* Draws the content of [recordBlock] in a separate layer, which can be customized using [composingStrategy], [outline] and [clip].
|
||||
*/
|
||||
context(scope: androidx.compose.ui.graphics.drawscope.DrawScope)
|
||||
fun CacheDrawScope.drawInLayer(
|
||||
composingStrategy: CompositingStrategy = CompositingStrategy.Auto,
|
||||
outline: Outline? = null,
|
||||
clip: Boolean = false,
|
||||
recordBlock: ContentDrawScope.() -> Unit,
|
||||
) {
|
||||
val layer = obtainGraphicsLayer().apply {
|
||||
this.compositingStrategy = composingStrategy
|
||||
this.clip = clip
|
||||
outline?.let { this.setOutline(it) }
|
||||
|
||||
record(block = recordBlock)
|
||||
}
|
||||
scope.drawLayer(layer)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue