Merge branch 'develop' into feature/fga/room_list_api
This commit is contained in:
commit
2a24d0196e
113 changed files with 2259 additions and 132 deletions
|
|
@ -17,9 +17,11 @@
|
|||
package io.element.android.libraries.androidutils.file
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
import java.util.UUID
|
||||
|
||||
fun File.safeDelete() {
|
||||
|
|
@ -52,3 +54,99 @@ fun Context.createTmpFile(baseDir: File = cacheDir, extension: String? = null):
|
|||
val suffix = extension?.let { ".$extension" }
|
||||
return File.createTempFile(UUID.randomUUID().toString(), suffix, baseDir).apply { mkdirs() }
|
||||
}
|
||||
|
||||
// Implementation should return true in case of success
|
||||
typealias ActionOnFile = (file: File) -> Boolean
|
||||
|
||||
/* ==========================================================================================
|
||||
* Log
|
||||
* ========================================================================================== */
|
||||
|
||||
fun lsFiles(context: Context) {
|
||||
Timber.v("Content of cache dir:")
|
||||
recursiveActionOnFile(context.cacheDir, ::logAction)
|
||||
|
||||
Timber.v("Content of files dir:")
|
||||
recursiveActionOnFile(context.filesDir, ::logAction)
|
||||
}
|
||||
|
||||
private fun logAction(file: File): Boolean {
|
||||
if (file.isDirectory) {
|
||||
Timber.v(file.toString())
|
||||
} else {
|
||||
Timber.v("$file ${file.length()} bytes")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* Private
|
||||
* ========================================================================================== */
|
||||
|
||||
/**
|
||||
* Return true in case of success.
|
||||
*/
|
||||
private fun recursiveActionOnFile(file: File, action: ActionOnFile): Boolean {
|
||||
if (file.isDirectory) {
|
||||
file.list()?.forEach {
|
||||
val result = recursiveActionOnFile(File(file, it), action)
|
||||
|
||||
if (!result) {
|
||||
// Break the loop
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return action.invoke(file)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file extension of a fileUri or a filename.
|
||||
*
|
||||
* @param fileUri the fileUri (can be a simple filename)
|
||||
* @return the file extension, in lower case, or null is extension is not available or empty
|
||||
*/
|
||||
fun getFileExtension(fileUri: String): String? {
|
||||
var reducedStr = fileUri
|
||||
|
||||
if (reducedStr.isNotEmpty()) {
|
||||
// Remove fragment
|
||||
reducedStr = reducedStr.substringBeforeLast('#')
|
||||
|
||||
// Remove query
|
||||
reducedStr = reducedStr.substringBeforeLast('?')
|
||||
|
||||
// Remove path
|
||||
val filename = reducedStr.substringAfterLast('/')
|
||||
|
||||
// Contrary to method MimeTypeMap.getFileExtensionFromUrl, we do not check the pattern
|
||||
// See https://stackoverflow.com/questions/14320527/android-should-i-use-mimetypemap-getfileextensionfromurl-bugs
|
||||
if (filename.isNotEmpty()) {
|
||||
val dotPos = filename.lastIndexOf('.')
|
||||
if (0 <= dotPos) {
|
||||
val ext = filename.substring(dotPos + 1)
|
||||
|
||||
if (ext.isNotBlank()) {
|
||||
return ext.lowercase(Locale.ROOT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* Size
|
||||
* ========================================================================================== */
|
||||
|
||||
@WorkerThread
|
||||
fun File.getSizeOfFiles(): Long {
|
||||
return walkTopDown()
|
||||
.onEnter {
|
||||
Timber.v("Get size of ${it.absolutePath}")
|
||||
true
|
||||
}
|
||||
.sumOf { it.length() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.androidutils.filesize
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.text.format.Formatter
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class AndroidFileSizeFormatter @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
) : FileSizeFormatter {
|
||||
override fun format(fileSize: Long, useShortFormat: Boolean): String {
|
||||
// Since Android O, the system considers that 1kB = 1000 bytes instead of 1024 bytes.
|
||||
// We want to avoid that.
|
||||
val normalizedSize = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
|
||||
fileSize
|
||||
} else {
|
||||
// First convert the size
|
||||
when {
|
||||
fileSize < 1024 -> fileSize
|
||||
fileSize < 1024 * 1024 -> fileSize * 1000 / 1024
|
||||
fileSize < 1024 * 1024 * 1024 -> fileSize * 1000 / 1024 * 1000 / 1024
|
||||
else -> fileSize * 1000 / 1024 * 1000 / 1024 * 1000 / 1024
|
||||
}
|
||||
}
|
||||
|
||||
return if (useShortFormat) {
|
||||
Formatter.formatShortFileSize(context, normalizedSize)
|
||||
} else {
|
||||
Formatter.formatFileSize(context, normalizedSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.androidutils.filesize
|
||||
|
||||
class FakeFileSizeFormatter : FileSizeFormatter {
|
||||
override fun format(fileSize: Long, useShortFormat: Boolean): String {
|
||||
return "$fileSize Bytes"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.androidutils.filesize
|
||||
|
||||
interface FileSizeFormatter {
|
||||
/**
|
||||
* Formats a content size to be in the form of bytes, kilobytes, megabytes, etc.
|
||||
*/
|
||||
fun format(fileSize: Long, useShortFormat: Boolean = true): String
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
package io.element.android.libraries.designsystem.components.preferences
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
|
@ -26,7 +27,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.progressSemantics
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.BugReport
|
||||
|
|
@ -55,7 +55,7 @@ fun PreferenceText(
|
|||
tintColor: Color? = null,
|
||||
onClick: () -> Unit = {},
|
||||
) {
|
||||
val minHeight = if (subtitle == null) preferenceMinHeightOnlyTitle else preferenceMinHeight
|
||||
val minHeight = if (subtitle == null) preferenceMinHeightOnlyTitle else preferenceMinHeight
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -69,9 +69,10 @@ fun PreferenceText(
|
|||
.padding(vertical = preferencePaddingVertical)
|
||||
) {
|
||||
PreferenceIcon(icon = icon, tintColor = tintColor)
|
||||
Column(modifier = Modifier
|
||||
.weight(1f)
|
||||
.align(Alignment.CenterVertically)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.align(Alignment.CenterVertically)
|
||||
) {
|
||||
if (title != null) {
|
||||
Text(
|
||||
|
|
@ -92,15 +93,24 @@ fun PreferenceText(
|
|||
}
|
||||
}
|
||||
if (currentValue != null) {
|
||||
Text(currentValue, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.secondary)
|
||||
Spacer(Modifier.width(16.dp))
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(horizontal = 16.dp),
|
||||
text = currentValue,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
} else if (loadingCurrentValue) {
|
||||
CircularProgressIndicator(modifier = Modifier
|
||||
.progressSemantics()
|
||||
.size(20.dp), strokeWidth = 2.dp)
|
||||
Spacer(Modifier.width(16.dp))
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.progressSemantics()
|
||||
.padding(horizontal = 16.dp)
|
||||
.size(20.dp)
|
||||
.align(Alignment.CenterVertically),
|
||||
strokeWidth = 2.dp
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -111,9 +121,39 @@ internal fun PreferenceTextPreview() = ElementThemedPreview { ContentToPreview()
|
|||
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
PreferenceText(
|
||||
title = "Title",
|
||||
subtitle = "Some content",
|
||||
icon = Icons.Default.BugReport,
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp)
|
||||
) {
|
||||
PreferenceText(
|
||||
title = "Title",
|
||||
icon = Icons.Default.BugReport,
|
||||
)
|
||||
PreferenceText(
|
||||
title = "Title",
|
||||
subtitle = "Some content",
|
||||
icon = Icons.Default.BugReport,
|
||||
)
|
||||
PreferenceText(
|
||||
title = "Title",
|
||||
subtitle = "Some content",
|
||||
icon = Icons.Default.BugReport,
|
||||
currentValue = "123",
|
||||
)
|
||||
PreferenceText(
|
||||
title = "Title",
|
||||
subtitle = "Some content",
|
||||
icon = Icons.Default.BugReport,
|
||||
loadingCurrentValue = true,
|
||||
)
|
||||
PreferenceText(
|
||||
title = "Title",
|
||||
icon = Icons.Default.BugReport,
|
||||
currentValue = "123",
|
||||
)
|
||||
PreferenceText(
|
||||
title = "Title",
|
||||
icon = Icons.Default.BugReport,
|
||||
loadingCurrentValue = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import androidx.compose.material3.SnackbarDuration
|
|||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -28,27 +29,31 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
/**
|
||||
* A global dispatcher of [SnackbarMessage] to be displayed in [Snackbar] via a [SnackbarHostState].
|
||||
*/
|
||||
class SnackbarDispatcher {
|
||||
private val mutex = Mutex()
|
||||
|
||||
private val snackbarState = MutableStateFlow<SnackbarMessage?>(null)
|
||||
val snackbarMessage: Flow<SnackbarMessage?> = snackbarState
|
||||
private val _snackbarMessage = MutableStateFlow<SnackbarMessage?>(null)
|
||||
val snackbarMessage: Flow<SnackbarMessage?> = _snackbarMessage.asStateFlow()
|
||||
|
||||
suspend fun post(message: SnackbarMessage) {
|
||||
mutex.withLock {
|
||||
snackbarState.update { message }
|
||||
_snackbarMessage.update { message }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clear() {
|
||||
mutex.withLock {
|
||||
snackbarState.update { null }
|
||||
_snackbarMessage.update { null }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,10 +64,8 @@ val LocalSnackbarDispatcher = compositionLocalOf<SnackbarDispatcher> {
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun handleSnackbarMessage(
|
||||
snackbarDispatcher: SnackbarDispatcher
|
||||
): SnackbarMessage? {
|
||||
return snackbarDispatcher.snackbarMessage.collectAsState(initial = null).value
|
||||
fun SnackbarDispatcher.collectSnackbarMessageAsState(): State<SnackbarMessage?> {
|
||||
return snackbarMessage.collectAsState(initial = null)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
<?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">"(obrázok bol tiež zmenený)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s zmenili svoj obrázok"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Zmenili ste svoj obrázok"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s zmenili svoje zobrazované meno z %2$s na %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Zmenili ste si zobrazované meno z %1$s na %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s odstránili svoje zobrazované meno (predtým bolo %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Odstránili ste svoje zobrazované meno (predtým bolo %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s nastavili svoje zobrazované meno na %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Svoje zobrazované meno ste nastavili na %1$s"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s zmenil/a obrázok miestnosti"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Zmenili ste obrázok miestnosti"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s odstránil/a obrázok miestnosti"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Odstránili ste obrázok miestnosti"</string>
|
||||
<string name="state_event_room_ban">"%1$s zakázal/a používateľa %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"Zakázali ste používateľa %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s vytvoril/a miestnosť"</string>
|
||||
<string name="state_event_room_created_by_you">"Vytvorili ste miestnosť"</string>
|
||||
<string name="state_event_room_invite">"%1$s pozval/a používateľa %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s prijal/a pozvanie"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Prijali ste pozvánku"</string>
|
||||
<string name="state_event_room_invite_by_you">"Pozvali ste používateľa %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s vás pozval/a"</string>
|
||||
<string name="state_event_room_join">"%1$s sa pripojil/a do miestnosti"</string>
|
||||
<string name="state_event_room_join_by_you">"Vstúpili ste do miestnosti"</string>
|
||||
<string name="state_event_room_knock">"%1$s požiadal o pripojenie"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s umožnil/a používateľovi %2$s pripojiť sa"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s vám umožnil/a pripojiť sa"</string>
|
||||
<string name="state_event_room_knock_by_you">"Požiadali ste o pripojenie"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Zrušili ste svoju žiadosť o pripojenie"</string>
|
||||
<string name="state_event_room_leave">"%1$s opustil/a miestnosť"</string>
|
||||
<string name="state_event_room_leave_by_you">"Opustili ste miestnosť"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s zmenil/a názov miestnosti na: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Zmenili ste názov miestnosti na: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s odstránil/a názov miestnosti"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Odstránili ste názov miestnosti"</string>
|
||||
<string name="state_event_room_reject">"%1$s odmietol/a pozvánku"</string>
|
||||
<string name="state_event_room_reject_by_you">"Odmietli ste pozvánku"</string>
|
||||
<string name="state_event_room_remove">"%1$s odstránil/a %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"Odstránili ste %1$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s poslal/a pozvánku používateľovi %2$s, aby sa pripojil k miestnosti"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Poslali ste pozvánku používateľovi %1$s, aby sa pripojil do miestnosti"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s zmenil/a tému na: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Zmenili ste tému na: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s odstránil/a tému miestnosti"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Odstránili ste tému miestnosti"</string>
|
||||
<string name="state_event_room_unban">"%1$s zrušil/a zákaz pre %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"Zrušili ste zákaz pre %1$s"</string>
|
||||
</resources>
|
||||
|
|
@ -49,6 +49,12 @@ interface MatrixClient : Closeable {
|
|||
fun sessionVerificationService(): SessionVerificationService
|
||||
fun pushersService(): PushersService
|
||||
fun notificationService(): NotificationService
|
||||
suspend fun getCacheSize(): Long
|
||||
|
||||
/**
|
||||
* Will close the client and delete the cache data.
|
||||
*/
|
||||
suspend fun clearCache()
|
||||
suspend fun logout()
|
||||
suspend fun loadUserDisplayName(): Result<String>
|
||||
suspend fun loadUserAvatarURLString(): Result<String?>
|
||||
|
|
|
|||
|
|
@ -21,5 +21,5 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
|
||||
interface NotificationService {
|
||||
fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId): Result<NotificationData?>
|
||||
fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean): Result<NotificationData?>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,4 +116,13 @@ interface MatrixRoom : Closeable {
|
|||
suspend fun setTopic(topic: String): Result<Unit>
|
||||
|
||||
suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit>
|
||||
|
||||
/**
|
||||
* Share a location message in the room.
|
||||
*
|
||||
* @param body A human readable textual representation of the location.
|
||||
* @param geoUri A geo URI (RFC 5870) representing the location e.g. `geo:51.5008,0.1247;u=35`.
|
||||
* Respectively: latitude, longitude, and (optional) uncertainty.
|
||||
*/
|
||||
suspend fun sendLocation(body: String, geoUri: String): Result<Unit>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ dependencies {
|
|||
// api(projects.libraries.rustsdk)
|
||||
implementation(libs.matrix.sdk)
|
||||
implementation(projects.libraries.di)
|
||||
implementation(projects.libraries.androidutils)
|
||||
implementation(projects.services.toolbox.api)
|
||||
api(projects.libraries.matrix.api)
|
||||
implementation(libs.dagger)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package io.element.android.libraries.matrix.impl
|
||||
|
||||
import io.element.android.libraries.androidutils.file.getSizeOfFiles
|
||||
import io.element.android.libraries.androidutils.file.safeDelete
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.coroutine.childScopeOf
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
|
|
@ -229,15 +231,25 @@ class RustMatrixClient constructor(
|
|||
client.destroy()
|
||||
}
|
||||
|
||||
override suspend fun getCacheSize(): Long {
|
||||
// Do not use client.userId since it can throw if client has been closed (during clear cache)
|
||||
return baseDirectory.getCacheSize(userID = sessionId.value)
|
||||
}
|
||||
|
||||
override suspend fun clearCache() {
|
||||
close()
|
||||
baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = false)
|
||||
}
|
||||
|
||||
override suspend fun logout() = withContext(dispatchers.io) {
|
||||
try {
|
||||
client.logout()
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "Fail to call logout on HS. Still delete local files.")
|
||||
}
|
||||
baseDirectory.deleteSessionDirectory(userID = client.userId())
|
||||
sessionStore.removeSession(client.userId())
|
||||
close()
|
||||
baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = true)
|
||||
sessionStore.removeSession(sessionId.value)
|
||||
}
|
||||
|
||||
override suspend fun loadUserDisplayName(): Result<String> = withContext(dispatchers.io) {
|
||||
|
|
@ -271,11 +283,48 @@ class RustMatrixClient constructor(
|
|||
|
||||
override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver
|
||||
|
||||
private fun File.deleteSessionDirectory(userID: String): Boolean {
|
||||
private suspend fun File.getCacheSize(
|
||||
userID: String,
|
||||
includeCryptoDb: Boolean = false,
|
||||
): Long = withContext(dispatchers.io) {
|
||||
// Rust sanitises the user ID replacing invalid characters with an _
|
||||
val sanitisedUserID = userID.replace(":", "_")
|
||||
val sessionDirectory = File(this, sanitisedUserID)
|
||||
return sessionDirectory.deleteRecursively()
|
||||
val sessionDirectory = File(this@getCacheSize, sanitisedUserID)
|
||||
if (includeCryptoDb) {
|
||||
sessionDirectory.getSizeOfFiles()
|
||||
} else {
|
||||
listOf(
|
||||
"matrix-sdk-state.sqlite3",
|
||||
"matrix-sdk-state.sqlite3-shm",
|
||||
"matrix-sdk-state.sqlite3-wal",
|
||||
).map { fileName ->
|
||||
File(sessionDirectory, fileName)
|
||||
}.sumOf { file ->
|
||||
file.length()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun File.deleteSessionDirectory(
|
||||
userID: String,
|
||||
deleteCryptoDb: Boolean = false,
|
||||
): Boolean = withContext(dispatchers.io) {
|
||||
// Rust sanitises the user ID replacing invalid characters with an _
|
||||
val sanitisedUserID = userID.replace(":", "_")
|
||||
val sessionDirectory = File(this@deleteSessionDirectory, sanitisedUserID)
|
||||
if (deleteCryptoDb) {
|
||||
// Delete the folder and all its content
|
||||
sessionDirectory.deleteRecursively()
|
||||
} else {
|
||||
// Delete only the state.db file
|
||||
sessionDirectory.listFiles().orEmpty()
|
||||
.filter { it.name.contains("matrix-sdk-state") }
|
||||
.forEach { file ->
|
||||
Timber.w("Deleting file ${file.name}...")
|
||||
file.safeDelete()
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,10 +32,11 @@ class RustNotificationService(
|
|||
override fun getNotification(
|
||||
userId: SessionId,
|
||||
roomId: RoomId,
|
||||
eventId: EventId
|
||||
eventId: EventId,
|
||||
filterByPushRules: Boolean,
|
||||
): Result<NotificationData?> {
|
||||
return runCatching {
|
||||
client.getNotificationItem(roomId.value, eventId.value)?.use(notificationMapper::map)
|
||||
client.getNotificationItem(roomId.value, eventId.value, filterByPushRules)?.use(notificationMapper::map)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -374,4 +374,13 @@ class RustMatrixRoom(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String
|
||||
): Result<Unit> = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
innerRoom.sendLocation(body, geoUri, genTransactionId())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,6 +101,13 @@ class FakeMatrixClient(
|
|||
|
||||
override fun syncService() = syncService
|
||||
|
||||
override suspend fun getCacheSize(): Long {
|
||||
return 0
|
||||
}
|
||||
|
||||
override suspend fun clearCache() {
|
||||
}
|
||||
|
||||
override suspend fun logout() {
|
||||
delay(100)
|
||||
logoutFailure?.let { throw it }
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.flowOf
|
|||
val A_OIDC_DATA = OidcDetails(url = "a-url")
|
||||
|
||||
class FakeAuthenticationService : MatrixAuthenticationService {
|
||||
private var homeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
|
||||
private val homeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
|
||||
private var oidcError: Throwable? = null
|
||||
private var oidcCancelError: Throwable? = null
|
||||
private var loginError: Throwable? = null
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationData
|
|||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
|
||||
class FakeNotificationService : NotificationService {
|
||||
override fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId): Result<NotificationData?> {
|
||||
override fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean): Result<NotificationData?> {
|
||||
return Result.success(null)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ class FakeMatrixRoom(
|
|||
private var cancelSendResult = Result.success(Unit)
|
||||
private var forwardEventResult = Result.success(Unit)
|
||||
private var reportContentResult = Result.success(Unit)
|
||||
private var sendLocationResult = Result.success(Unit)
|
||||
|
||||
var sendMediaCount = 0
|
||||
private set
|
||||
|
|
@ -93,6 +94,9 @@ class FakeMatrixRoom(
|
|||
var reportedContentCount: Int = 0
|
||||
private set
|
||||
|
||||
var sendLocationCount: Int = 0
|
||||
private set
|
||||
|
||||
var isInviteAccepted: Boolean = false
|
||||
private set
|
||||
|
||||
|
|
@ -262,6 +266,14 @@ class FakeMatrixRoom(
|
|||
return reportContentResult
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendLocationCount++
|
||||
return sendLocationResult
|
||||
}
|
||||
|
||||
override fun close() = Unit
|
||||
|
||||
fun givenLeaveRoomError(throwable: Throwable?) {
|
||||
|
|
@ -355,4 +367,8 @@ class FakeMatrixRoom(
|
|||
fun givenReportContentResult(result: Result<Unit>) {
|
||||
reportContentResult = result
|
||||
}
|
||||
|
||||
fun givenSendLocationResult(result: Result<Unit>) {
|
||||
sendLocationResult = result
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,16 +26,17 @@ import io.element.android.libraries.di.ApplicationContext
|
|||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import okhttp3.OkHttpClient
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
class LoggedInImageLoaderFactory @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val okHttpClient: OkHttpClient,
|
||||
private val okHttpClient: Provider<OkHttpClient>,
|
||||
) : ImageLoaderFactory {
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
return ImageLoader
|
||||
.Builder(context)
|
||||
.okHttpClient(okHttpClient)
|
||||
.okHttpClient { okHttpClient.get() }
|
||||
.components {
|
||||
// Add gif support
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
|
|
@ -54,12 +55,12 @@ class LoggedInImageLoaderFactory @Inject constructor(
|
|||
|
||||
class NotLoggedInImageLoaderFactory @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val okHttpClient: OkHttpClient,
|
||||
private val okHttpClient: Provider<OkHttpClient>,
|
||||
) : ImageLoaderFactory {
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
return ImageLoader
|
||||
.Builder(context)
|
||||
.okHttpClient(okHttpClient)
|
||||
.okHttpClient { okHttpClient.get() }
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ class NotifiableEventResolver @Inject constructor(
|
|||
userId = sessionId,
|
||||
roomId = roomId,
|
||||
eventId = eventId,
|
||||
filterByPushRules = true,
|
||||
)
|
||||
}.fold(
|
||||
{
|
||||
|
|
|
|||
50
libraries/push/impl/src/main/res/values-sk/translations.xml
Normal file
50
libraries/push/impl/src/main/res/values-sk/translations.xml
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="notification_channel_call">"Zavolať"</string>
|
||||
<string name="notification_channel_silent">"Tiché oznámenia"</string>
|
||||
<string name="notification_inline_reply_failed">"** Nepodarilo sa odoslať - prosím otvorte miestnosť"</string>
|
||||
<string name="notification_invitation_action_join">"Pripojiť sa"</string>
|
||||
<string name="notification_invitation_action_reject">"Zamietnuť"</string>
|
||||
<string name="notification_new_messages">"Nové správy"</string>
|
||||
<string name="notification_room_action_mark_as_read">"Označiť ako prečítané"</string>
|
||||
<string name="notification_test_push_notification_content">"Prezeráte si oznámenie! Kliknite na mňa!"</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>
|
||||
<string name="notification_unread_notified_messages_and_invitation">"%1$s a %2$s"</string>
|
||||
<string name="notification_unread_notified_messages_in_room">"%1$s v %2$s"</string>
|
||||
<string name="notification_unread_notified_messages_in_room_and_invitation">"%1$s v %2$s a %3$s"</string>
|
||||
<plurals name="notification_compat_summary_line_for_room">
|
||||
<item quantity="one">"%1$s: %2$d správa"</item>
|
||||
<item quantity="few">"%1$s: %2$d správy"</item>
|
||||
<item quantity="other">"%1$s: %2$d správ"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_compat_summary_title">
|
||||
<item quantity="one">"%d oznámenie"</item>
|
||||
<item quantity="few">"%d oznámenia"</item>
|
||||
<item quantity="other">"%d oznámení"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_invitations">
|
||||
<item quantity="one">"%d pozvánka"</item>
|
||||
<item quantity="few">"%d pozvánky"</item>
|
||||
<item quantity="other">"%d pozvánok"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_new_messages_for_room">
|
||||
<item quantity="one">"%d nová správa"</item>
|
||||
<item quantity="few">"%d nové správy"</item>
|
||||
<item quantity="other">"%d nových správ"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_unread_notified_messages">
|
||||
<item quantity="one">"%d neprečítaná oznámená správa"</item>
|
||||
<item quantity="few">"%d neprečítané oznámené správy"</item>
|
||||
<item quantity="other">"%d neprečítaných oznámených správ"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_unread_notified_messages_in_room_rooms">
|
||||
<item quantity="one">"%d miestnosť"</item>
|
||||
<item quantity="few">"%d miestnosti"</item>
|
||||
<item quantity="other">"%d miestností"</item>
|
||||
</plurals>
|
||||
<string name="push_choose_distributor_dialog_title_android">"Vyberte spôsob prijímania oznámení"</string>
|
||||
<string name="push_distributor_background_sync_android">"Synchronizácia na pozadí"</string>
|
||||
<string name="push_distributor_firebase_android">"Služby Google"</string>
|
||||
<string name="notification_room_action_quick_reply">"Rýchla odpoveď"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="rich_text_editor_composer_placeholder">"Správa…"</string>
|
||||
<string name="rich_text_editor_format_bold">"Použiť tučný formát"</string>
|
||||
<string name="rich_text_editor_format_italic">"Použiť formát kurzívy"</string>
|
||||
<string name="rich_text_editor_format_strikethrough">"Použiť formát prečiarknutia"</string>
|
||||
<string name="rich_text_editor_format_underline">"Použiť formát podčiarknutia"</string>
|
||||
<string name="rich_text_editor_link">"Nastaviť odkaz"</string>
|
||||
</resources>
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
<string name="action_invite">"Pozvat"</string>
|
||||
<string name="action_invite_friends">"Pozvat přátele"</string>
|
||||
<string name="action_invite_friends_to_app">"Pozvat přátele do %1$s"</string>
|
||||
<string name="action_invite_people_to_app">"Pozvat lidi na %1$s"</string>
|
||||
<string name="action_invites_list">"Pozvánky"</string>
|
||||
<string name="action_learn_more">"Zjistit více"</string>
|
||||
<string name="action_leave">"Odejít"</string>
|
||||
|
|
@ -108,6 +109,7 @@
|
|||
<string name="common_server_not_supported">"Server není podporován"</string>
|
||||
<string name="common_server_url">"URL serveru"</string>
|
||||
<string name="common_settings">"Nastavení"</string>
|
||||
<string name="common_shared_location">"Sdílená poloha"</string>
|
||||
<string name="common_starting_chat">"Zahajování chatu…"</string>
|
||||
<string name="common_sticker">"Nálepka"</string>
|
||||
<string name="common_success">"Úspěch"</string>
|
||||
|
|
@ -163,6 +165,8 @@
|
|||
<string name="screen_media_upload_preview_error_failed_processing">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
|
||||
<string name="screen_report_content_block_user_hint">"Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele"</string>
|
||||
<string name="screen_share_location_title">"Sdílet polohu"</string>
|
||||
<string name="screen_share_my_location_action">"Sdílet moji polohu"</string>
|
||||
<string name="settings_rageshake">"Rageshake"</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Práh detekce"</string>
|
||||
<string name="settings_title_general">"Obecné"</string>
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@
|
|||
<string name="screen_analytics_settings_share_data">"Teile Analyse-Daten"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Medienauswahl fehlgeschlagen, bitte versuche es erneut."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuchen Sie es erneut."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Medien hochladen fehlgeschlagen. Bitte versuchen Sie es erneut."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Hochladen von Medien fehlgeschlagen, bitte versuchen Sie es erneut."</string>
|
||||
<string name="screen_report_content_block_user_hint">"Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest"</string>
|
||||
<string name="settings_rageshake">"Rageshake"</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Erkennungsschwelle"</string>
|
||||
|
|
|
|||
158
libraries/ui-strings/src/main/res/values-sk/translations.xml
Normal file
158
libraries/ui-strings/src/main/res/values-sk/translations.xml
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="a11y_hide_password">"Skryť heslo"</string>
|
||||
<string name="a11y_send_files">"Odoslať súbory"</string>
|
||||
<string name="a11y_show_password">"Zobraziť heslo"</string>
|
||||
<string name="a11y_user_menu">"Používateľské menu"</string>
|
||||
<string name="action_accept">"Prijať"</string>
|
||||
<string name="action_back">"Späť"</string>
|
||||
<string name="action_cancel">"Zrušiť"</string>
|
||||
<string name="action_choose_photo">"Vybrať fotku"</string>
|
||||
<string name="action_clear">"Vyčistiť"</string>
|
||||
<string name="action_close">"Zavrieť"</string>
|
||||
<string name="action_complete_verification">"Dokončiť overenie"</string>
|
||||
<string name="action_confirm">"Potvrdiť"</string>
|
||||
<string name="action_continue">"Pokračovať"</string>
|
||||
<string name="action_copy">"Kopírovať"</string>
|
||||
<string name="action_copy_link">"Kopírovať odkaz"</string>
|
||||
<string name="action_copy_link_to_message">"Kopírovať odkaz do správy"</string>
|
||||
<string name="action_create">"Vytvoriť"</string>
|
||||
<string name="action_create_a_room">"Vytvoriť miestnosť"</string>
|
||||
<string name="action_decline">"Odmietnuť"</string>
|
||||
<string name="action_disable">"Vypnúť"</string>
|
||||
<string name="action_done">"Hotovo"</string>
|
||||
<string name="action_edit">"Upraviť"</string>
|
||||
<string name="action_enable">"Povoliť"</string>
|
||||
<string name="action_forgot_password">"Zabudnuté heslo?"</string>
|
||||
<string name="action_invite">"Pozvať"</string>
|
||||
<string name="action_invite_friends">"Pozvať priateľov"</string>
|
||||
<string name="action_invite_friends_to_app">"Pozvať priateľov do %1$s"</string>
|
||||
<string name="action_invite_people_to_app">"Pozvať ľudí do %1$s"</string>
|
||||
<string name="action_invites_list">"Pozvánky"</string>
|
||||
<string name="action_learn_more">"Zistiť viac"</string>
|
||||
<string name="action_leave">"Opustiť"</string>
|
||||
<string name="action_leave_room">"Opustiť miestnosť"</string>
|
||||
<string name="action_next">"Ďalej"</string>
|
||||
<string name="action_no">"Nie"</string>
|
||||
<string name="action_not_now">"Teraz nie"</string>
|
||||
<string name="action_ok">"OK"</string>
|
||||
<string name="action_open_with">"Otvoriť pomocou"</string>
|
||||
<string name="action_quick_reply">"Rýchla odpoveď"</string>
|
||||
<string name="action_quote">"Citovať"</string>
|
||||
<string name="action_remove">"Odstrániť"</string>
|
||||
<string name="action_reply">"Odpovedať"</string>
|
||||
<string name="action_report_bug">"Nahlásiť chybu"</string>
|
||||
<string name="action_report_content">"Nahlásiť obsah"</string>
|
||||
<string name="action_retry">"Skúsiť znova"</string>
|
||||
<string name="action_retry_decryption">"Opakovať dešifrovanie"</string>
|
||||
<string name="action_save">"Uložiť"</string>
|
||||
<string name="action_search">"Hľadať"</string>
|
||||
<string name="action_send">"Odoslať"</string>
|
||||
<string name="action_send_message">"Odoslať správu"</string>
|
||||
<string name="action_share">"Zdieľať"</string>
|
||||
<string name="action_share_link">"Zdieľať odkaz"</string>
|
||||
<string name="action_skip">"Preskočiť"</string>
|
||||
<string name="action_start">"Spustiť"</string>
|
||||
<string name="action_start_chat">"Začať konverzáciu"</string>
|
||||
<string name="action_start_verification">"Spustiť overovanie"</string>
|
||||
<string name="action_static_map_load">"Ťuknutím načítate mapu"</string>
|
||||
<string name="action_take_photo">"Urobiť fotku"</string>
|
||||
<string name="action_view_source">"Zobraziť zdroj"</string>
|
||||
<string name="action_yes">"Áno"</string>
|
||||
<string name="common_about">"O aplikácii"</string>
|
||||
<string name="common_analytics">"Analytika"</string>
|
||||
<string name="common_audio">"Zvuk"</string>
|
||||
<string name="common_bubbles">"Bubliny"</string>
|
||||
<string name="common_copyright">"Autorské práva"</string>
|
||||
<string name="common_creating_room">"Vytváranie miestnosti…"</string>
|
||||
<string name="common_current_user_left_room">"Opustil/a miestnosť"</string>
|
||||
<string name="common_decryption_error">"Chyba dešifrovania"</string>
|
||||
<string name="common_developer_options">"Možnosti pre vývojárov"</string>
|
||||
<string name="common_edited_suffix">"(upravené)"</string>
|
||||
<string name="common_editing">"Upravuje sa"</string>
|
||||
<string name="common_emote">"* %1$s %2$s"</string>
|
||||
<string name="common_encryption_enabled">"Šifrovanie zapnuté"</string>
|
||||
<string name="common_error">"Chyba"</string>
|
||||
<string name="common_file">"Súbor"</string>
|
||||
<string name="common_file_saved_on_disk_android">"Súbor bol uložený do priečinka Stiahnuté súbory"</string>
|
||||
<string name="common_forward_message">"Preposlať správu"</string>
|
||||
<string name="common_gif">"GIF"</string>
|
||||
<string name="common_image">"Obrázok"</string>
|
||||
<string name="common_invite_unknown_profile">"Nedokážeme overiť Matrix ID tohto používateľa. Pozvánka nemusí byť prijatá."</string>
|
||||
<string name="common_link_copied_to_clipboard">"Odkaz bol skopírovaný do schránky"</string>
|
||||
<string name="common_loading">"Načítava sa…"</string>
|
||||
<string name="common_message">"Správa"</string>
|
||||
<string name="common_message_layout">"Rozloženie správy"</string>
|
||||
<string name="common_message_removed">"Správa odstránená"</string>
|
||||
<string name="common_modern">"Moderné"</string>
|
||||
<string name="common_no_results">"Žiadne výsledky"</string>
|
||||
<string name="common_offline">"Offline"</string>
|
||||
<string name="common_password">"Heslo"</string>
|
||||
<string name="common_people">"Ľudia"</string>
|
||||
<string name="common_permalink">"Trvalý odkaz"</string>
|
||||
<string name="common_reactions">"Reakcie"</string>
|
||||
<string name="common_report_a_bug">"Nahlásiť chybu"</string>
|
||||
<string name="common_report_submitted">"Nahlásenie bolo odoslané"</string>
|
||||
<string name="common_room_name">"Názov miestnosti"</string>
|
||||
<string name="common_search_results">"Výsledky hľadania"</string>
|
||||
<string name="common_security">"Bezpečnosť"</string>
|
||||
<string name="common_select_your_server">"Vyberte svoj server"</string>
|
||||
<string name="common_sending">"Odosiela sa…"</string>
|
||||
<string name="common_server_not_supported">"Server nie je podporovaný"</string>
|
||||
<string name="common_server_url">"URL adresa servera"</string>
|
||||
<string name="common_settings">"Nastavenia"</string>
|
||||
<string name="common_shared_location">"Zdieľaná poloha"</string>
|
||||
<string name="common_sticker">"Nálepka"</string>
|
||||
<string name="common_success">"Úspech"</string>
|
||||
<string name="common_suggestions">"Návrhy"</string>
|
||||
<string name="common_syncing">"Synchronizuje sa"</string>
|
||||
<string name="common_topic">"Téma"</string>
|
||||
<string name="common_topic_placeholder">"O čom je táto miestnosť?"</string>
|
||||
<string name="common_unable_to_decrypt">"Nie je možné dešifrovať"</string>
|
||||
<string name="common_unsupported_event">"Nepodporovaná udalosť"</string>
|
||||
<string name="common_username">"Používateľské meno"</string>
|
||||
<string name="common_verification_cancelled">"Overovanie zrušené"</string>
|
||||
<string name="common_verification_complete">"Overovanie je dokončené"</string>
|
||||
<string name="common_video">"Video"</string>
|
||||
<string name="common_waiting">"Čaká sa…"</string>
|
||||
<string name="dialog_title_confirmation">"Potvrdenie"</string>
|
||||
<string name="dialog_title_warning">"Upozornenie"</string>
|
||||
<string name="emoji_picker_category_activity">"Aktivity"</string>
|
||||
<string name="emoji_picker_category_flags">"Vlajky"</string>
|
||||
<string name="emoji_picker_category_foods">"Jedlo a nápoje"</string>
|
||||
<string name="emoji_picker_category_nature">"Zvieratá a príroda"</string>
|
||||
<string name="emoji_picker_category_objects">"Predmety"</string>
|
||||
<string name="emoji_picker_category_people">"Smajlíky a ľudia"</string>
|
||||
<string name="emoji_picker_category_places">"Cestovanie a miesta"</string>
|
||||
<string name="emoji_picker_category_symbols">"Symboly"</string>
|
||||
<string name="error_failed_loading_messages">"Načítanie správ zlyhalo"</string>
|
||||
<string name="error_some_messages_have_not_been_sent">"Niektoré správy neboli odoslané"</string>
|
||||
<string name="error_unknown">"Prepáčte, vyskytla sa chyba"</string>
|
||||
<string name="invite_friends_rich_title">"🔐️ Pripojte sa ku mne na %1$s"</string>
|
||||
<string name="invite_friends_text">"Ahoj, porozprávajte sa so mnou na %1$s: %2$s"</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Ste si istí, že chcete opustiť túto miestnosť? Ste tu jediná osoba. Ak odídete, nikto sa do nej nebude môcť v budúcnosti pripojiť, vrátane vás."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Ste si istí, že chcete opustiť túto miestnosť? Táto miestnosť nie je verejná a bez pozvania sa do nej nebudete môcť vrátiť."</string>
|
||||
<string name="leave_room_alert_subtitle">"Ste si istí, že chcete opustiť miestnosť?"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d člen"</item>
|
||||
<item quantity="few">"%1$d členovia"</item>
|
||||
<item quantity="other">"%1$d členov"</item>
|
||||
</plurals>
|
||||
<string name="preference_rageshake">"Zúrivo potriasť pre nahlásenie chyby"</string>
|
||||
<string name="rageshake_dialog_content">"Zdá sa, že zúrivo trasiete telefónom. Chcete otvoriť obrazovku s nahlásením chýb?"</string>
|
||||
<string name="room_timeline_beginning_of_room">"Toto je začiatok %1$s."</string>
|
||||
<string name="room_timeline_beginning_of_room_no_name">"Toto je začiatok tejto konverzácie."</string>
|
||||
<string name="room_timeline_read_marker_title">"Nové"</string>
|
||||
<string name="screen_share_location_title">"Zdieľať polohu"</string>
|
||||
<string name="screen_share_my_location_action">"Zdieľať moju polohu"</string>
|
||||
<string name="screen_share_this_location_action">"Zdieľajte túto polohu"</string>
|
||||
<string name="settings_rageshake">"Zúrivé potrasenie"</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Prahová hodnota detekcie"</string>
|
||||
<string name="settings_title_general">"Všeobecné"</string>
|
||||
<string name="settings_version_number">"Verzia: %1$s (%2$s)"</string>
|
||||
<string name="test_language_identifier">"sk"</string>
|
||||
<string name="dialog_title_error">"Chyba"</string>
|
||||
<string name="dialog_title_success">"Úspech"</string>
|
||||
<string name="screen_report_content_block_user">"Zablokovať používateľa"</string>
|
||||
</resources>
|
||||
|
|
@ -164,6 +164,9 @@
|
|||
<string name="screen_media_upload_preview_error_failed_processing">"Failed processing media to upload, please try again."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Failed uploading media, please try again."</string>
|
||||
<string name="screen_report_content_block_user_hint">"Check if you want to hide all current and future messages from this user"</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_this_location_action">"Share this location"</string>
|
||||
<string name="settings_rageshake">"Rageshake"</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Detection threshold"</string>
|
||||
<string name="settings_title_general">"General"</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue