Merge branch 'release/25.04.3' into main

This commit is contained in:
ganfra 2025-04-23 11:05:37 +02:00
commit a69d126e26
414 changed files with 4326 additions and 1704 deletions

View file

@ -20,7 +20,7 @@ jobs:
- run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger
uses: danger/danger-js@12.3.4
uses: danger/danger-js@13.0.4
with:
args: "--dangerfile ./tools/danger/dangerfile.js"
env:

View file

@ -294,7 +294,7 @@ jobs:
yarn add danger-plugin-lint-report --dev
- name: Danger lint
if: always()
uses: danger/danger-js@12.3.4
uses: danger/danger-js@13.0.4
with:
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
env:

View file

@ -2,8 +2,8 @@
<!--- TOC -->
* [Contributing code to Matrix](#contributing-code-to-matrix)
* [Developer onboarding](#developer-onboarding)
* [Contributing code to Matrix](#contributing-code-to-matrix)
* [Android Studio settings](#android-studio-settings)
* [Compilation](#compilation)
* [Strings](#strings)
@ -28,18 +28,18 @@
<!--- END -->
## Contributing code to Matrix
Please read https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.md
Element X Android support can be found in this room: [![Element X Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org).
The rest of the document contains specific rules for Matrix Android projects
## Developer onboarding
For a detailed overview of the project, see [Developer Onboarding](./docs/_developer_onboarding.md).
## Contributing code to Matrix
If instead of contributing to the Element X Android project, you want to contribute to Synapse, the homeserver implementation, please read the [Synapse contribution guide](https://element-hq.github.io/synapse/latest/development/contributing_guide.html).
Element X Android support can be found in this room: [![Element X Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org).
The rest of the document contains specific rules for Matrix Android projects.
## Android Studio settings
Please set the "hard wrap" setting of Android Studio to 160 chars, this is the setting we use internally to format the source code (Menu `Settings/Editor/Code Style` then `Hard wrap at`).

View file

@ -300,6 +300,7 @@ licensee {
allow("Apache-2.0")
allow("MIT")
allow("BSD-2-Clause")
allow("BSD-3-Clause")
allowUrl("https://opensource.org/licenses/MIT")
allowUrl("https://developer.android.com/studio/terms.html")
allowUrl("https://www.zetetic.net/sqlcipher/license/")

View file

@ -3,6 +3,7 @@
<locale android:name="be"/>
<locale android:name="bg"/>
<locale android:name="cs"/>
<locale android:name="cy"/>
<locale android:name="de"/>
<locale android:name="el"/>
<locale android:name="en"/>
@ -17,6 +18,7 @@
<locale android:name="in"/>
<locale android:name="it"/>
<locale android:name="ka"/>
<locale android:name="lt"/>
<locale android:name="nb"/>
<locale android:name="nl"/>
<locale android:name="pl"/>

View file

@ -78,15 +78,21 @@ class SyncOrchestrator @AssistedInject constructor(
internal fun observeStates() = coroutineScope.launch {
Timber.tag(tag).d("start observing the app and network state")
val isAppActiveFlow = combine(
appForegroundStateService.isInForeground,
appForegroundStateService.isInCall,
appForegroundStateService.isSyncingNotificationEvent,
appForegroundStateService.hasRingingCall,
) { isInForeground, isInCall, isSyncingNotificationEvent, hasRingingCall ->
isInForeground || isInCall || isSyncingNotificationEvent || hasRingingCall
}
combine(
// small debounce to avoid spamming startSync when the state is changing quickly in case of error.
syncService.syncState.debounce(100.milliseconds),
networkMonitor.connectivity,
appForegroundStateService.isInForeground,
appForegroundStateService.isInCall,
appForegroundStateService.isSyncingNotificationEvent,
) { syncState, networkState, isInForeground, isInCall, isSyncingNotificationEvent ->
val isAppActive = isInForeground || isInCall || isSyncingNotificationEvent
isAppActiveFlow,
) { syncState, networkState, isAppActive ->
val isNetworkAvailable = networkState == NetworkStatus.Connected
Timber.tag(tag).d("isAppActive=$isAppActive, isNetworkAvailable=$isNetworkAvailable")

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="banner_migrate_to_native_sliding_sync_action">"Allgofnodi ac Uwchraddio"</string>
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"Nid yw %1$s bellach yn cefnogi\'r hen brotocol. Allgofnodwch a mewngofnodi\'n ôl i barhau i ddefnyddio\'r ap."</string>
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Nid yw eich gweinydd cartref yn cefnogi\'r hen brotocol mwyach. Allgofnodwch a mewngofnodwch yn ôl i barhau i ddefnyddio\'r ap."</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="banner_migrate_to_native_sliding_sync_action">"Logg ut og oppgrader"</string>
<string name="banner_migrate_to_native_sliding_sync_app_force_logout_title">"%1$s støtter ikke lenger den gamle protokollen. Logg ut og logg inn igjen for å fortsette å bruke appen."</string>
<string name="banner_migrate_to_native_sliding_sync_force_logout_title">"Hjemmeserveren din støtter ikke lenger den gamle protokollen. Vennligst logg ut og inn igjen for å fortsette å bruke appen."</string>
</resources>

View file

@ -236,6 +236,50 @@ class SyncOrchestratorTest {
stopSyncRecorder.assertions().isCalledOnce()
}
@Test
fun `when the app was in background and we have an incoming ringing call, a sync will be started`() = runTest {
val startSyncRecorder = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
val stopSyncRecorder = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
val syncService = FakeSyncService(initialSyncState = SyncState.Idle).apply {
startSyncLambda = startSyncRecorder
stopSyncLambda = stopSyncRecorder
}
val networkMonitor = FakeNetworkMonitor(initialStatus = NetworkStatus.Connected)
val appForegroundStateService = FakeAppForegroundStateService(
initialForegroundValue = false,
initialIsSyncingNotificationEventValue = false,
initialHasRingingCall = false,
)
val syncOrchestrator = createSyncOrchestrator(
syncService = syncService,
networkMonitor = networkMonitor,
appForegroundStateService = appForegroundStateService,
)
// We start observing
syncOrchestrator.observeStates()
// Advance the time to make sure the orchestrator has had time to start processing the inputs
advanceTimeBy(100.milliseconds)
// Start sync was never called
startSyncRecorder.assertions().isNeverCalled()
// Now we receive a ringing call
appForegroundStateService.updateHasRingingCall(true)
// Start sync will be called shortly after
advanceTimeBy(1.milliseconds)
startSyncRecorder.assertions().isCalledOnce()
// If the sync is running and the ringing call notification is now over, the sync stops after a delay
syncService.emitSyncState(SyncState.Running)
appForegroundStateService.updateHasRingingCall(false)
advanceTimeBy(10.seconds)
stopSyncRecorder.assertions().isCalledOnce()
}
@Test
fun `when the app is in foreground, we sync for a notification and a call is ongoing, the sync will only stop when all conditions are false`() = runTest {
val startSyncRecorder = lambdaRecorder<Result<Unit>> { Result.success(Unit) }

View file

@ -10,7 +10,7 @@
* [Sync](#sync)
* [Rust SDK](#rust-sdk)
* [Matrix Rust Component Kotlin](#matrix-rust-component-kotlin)
* [Build the SDK locally](#build-the-sdk-locally)
* [Building the SDK locally](#building-the-sdk-locally)
* [The Android project](#the-android-project)
* [Application](#application)
* [Jetpack Compose](#jetpack-compose)
@ -107,7 +107,7 @@ This is the goal of https://github.com/matrix-org/matrix-rust-components-kotlin.
This repository is used for distributing kotlin releases of the Matrix Rust SDK.
It'll provide the corresponding aar and also publish them on maven.
Most of the time you want to use the releases made on maven with gradle:
Most of the time **you want to use the releases made on maven with gradle**:
```groovy
implementation("org.matrix.rustcomponents:sdk-android:latest-version")
@ -115,7 +115,9 @@ implementation("org.matrix.rustcomponents:sdk-android:latest-version")
You can also have access to the aars through the [release](https://github.com/matrix-org/matrix-rust-components-kotlin/releases) page.
#### Build the SDK locally
#### Building the SDK locally
If you want to make changes to the SDK or test them before integrating it with your codebase, you can build the SDK locally too.
Prerequisites:
* Install the Android NDK (Native Development Kit). To do this from within

@ -1 +1 @@
Subproject commit 665a15a1907a116816ffd1653bccfeaeeb7a2968
Subproject commit 0299b8ec4f4233a39230d4c35b97f89728c35fd1

View file

@ -0,0 +1,2 @@
Main changes in this version: bug fixes and improvements.
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_settings_read_terms_content_link">"yma"</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_settings_help_us_improve">"Dalinkitės anoniminiais naudojimo duomenimis ir padėkite mums nustatyti problemas."</string>
<string name="screen_analytics_settings_read_terms">"Galite perskaityti visas mūsų sąlygas %1$s ."</string>
<string name="screen_analytics_settings_read_terms_content_link">"čia"</string>
<string name="screen_analytics_settings_share_data">"Dalytis analitiniais duomenimis"</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_prompt_read_terms_content_link">"yma"</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_analytics_prompt_data_usage">"Mes nekaupsime ir neprofiliuosime jokių asmens duomenų"</string>
<string name="screen_analytics_prompt_help_us_improve">"Dalinkitės anoniminiais naudojimo duomenimis ir padėkite mums nustatyti problemas."</string>
<string name="screen_analytics_prompt_read_terms">"Galite perskaityti visas mūsų sąlygas %1$s ."</string>
<string name="screen_analytics_prompt_read_terms_content_link">"čia"</string>
<string name="screen_analytics_prompt_settings">"Tai galite bet kada išjungti"</string>
<string name="screen_analytics_prompt_third_party_sharing">"Mes nesidalinsime Jūsų duomenimis su trečiosiomis šalimis"</string>
<string name="screen_analytics_prompt_title">"Padėkite pagerinti %1$s"</string>
</resources>

View file

@ -66,6 +66,7 @@ dependencies {
implementation(projects.appconfig)
implementation(projects.features.enterprise.api)
implementation(projects.libraries.architecture)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.featureflag.api)

View file

@ -8,6 +8,10 @@
package io.element.android.features.call.impl.ui
import android.annotation.SuppressLint
import android.content.Context
import android.media.AudioDeviceCallback
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.util.Log
import android.view.ViewGroup
import android.webkit.ConsoleMessage
@ -21,12 +25,17 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.getSystemService
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.call.impl.R
import io.element.android.features.call.impl.pip.PictureInPictureEvents
@ -35,6 +44,8 @@ import io.element.android.features.call.impl.pip.PictureInPictureStateProvider
import io.element.android.features.call.impl.pip.aPictureInPictureState
import io.element.android.features.call.impl.utils.WebViewPipController
import io.element.android.features.call.impl.utils.WebViewWidgetMessageInterceptor
import io.element.android.libraries.androidutils.compat.disableExternalAudioDevice
import io.element.android.libraries.androidutils.compat.enableExternalAudioDevice
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.button.BackButton
@ -147,9 +158,11 @@ private fun CallWebView(
Text("WebView - can't be previewed")
}
} else {
var audioDeviceCallback: AudioDeviceCallback? by remember { mutableStateOf(null) }
AndroidView(
modifier = modifier,
factory = { context ->
audioDeviceCallback = context.setupAudioConfiguration()
WebView(context).apply {
onWebViewCreate(this)
setup(userAgent, onPermissionsRequest)
@ -161,12 +174,41 @@ private fun CallWebView(
}
},
onRelease = { webView ->
// Reset audio mode
webView.context.releaseAudioConfiguration(audioDeviceCallback)
webView.destroy()
}
)
}
}
private fun Context.setupAudioConfiguration(): AudioDeviceCallback? {
val audioManager = getSystemService<AudioManager>() ?: return null
// Set 'voice call' mode so volume keys actually control the call volume
audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
audioManager.enableExternalAudioDevice()
return object : AudioDeviceCallback() {
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>?) {
Timber.d("Audio devices added")
audioManager.enableExternalAudioDevice()
}
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>?) {
Timber.d("Audio devices removed")
audioManager.enableExternalAudioDevice()
}
}.also {
audioManager.registerAudioDeviceCallback(it, null)
}
}
private fun Context.releaseAudioConfiguration(audioDeviceCallback: AudioDeviceCallback?) {
val audioManager = getSystemService<AudioManager>() ?: return
audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)
audioManager.disableExternalAudioDevice()
audioManager.mode = AudioManager.MODE_NORMAL
}
@SuppressLint("SetJavaScriptEnabled")
private fun WebView.setup(
userAgent: String,

View file

@ -26,6 +26,7 @@ import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.push.api.notifications.ForegroundServiceType
import io.element.android.libraries.push.api.notifications.NotificationIdProvider
import io.element.android.libraries.push.api.notifications.OnMissedCallNotificationHandler
import io.element.android.services.appnavstate.api.AppForegroundStateService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
@ -87,7 +88,9 @@ class DefaultActiveCallManager @Inject constructor(
private val notificationManagerCompat: NotificationManagerCompat,
private val matrixClientProvider: MatrixClientProvider,
private val defaultCurrentCallService: DefaultCurrentCallService,
private val appForegroundStateService: AppForegroundStateService,
) : ActiveCallManager {
private val tag = "DefaultActiveCallManager"
private var timedOutCallJob: Job? = null
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@ -106,9 +109,11 @@ class DefaultActiveCallManager @Inject constructor(
override suspend fun registerIncomingCall(notificationData: CallNotificationData) {
mutex.withLock {
appForegroundStateService.updateHasRingingCall(true)
Timber.tag(tag).d("Received incoming call for room id: ${notificationData.roomId}")
if (activeCall.value != null) {
displayMissedCallNotification(notificationData)
Timber.w("Already have an active call, ignoring incoming call: $notificationData")
Timber.tag(tag).w("Already have an active call, ignoring incoming call: $notificationData")
return
}
activeCall.value = ActiveCall(
@ -129,6 +134,7 @@ class DefaultActiveCallManager @Inject constructor(
// Acquire a wake lock to keep the device awake during the incoming call, so we can process the room info data
if (activeWakeLock?.isHeld == false) {
Timber.tag(tag).d("Acquiring partial wakelock")
activeWakeLock.acquire(ElementCallConfig.RINGING_CALL_DURATION_SECONDS * 1000L)
}
}
@ -139,10 +145,13 @@ class DefaultActiveCallManager @Inject constructor(
*/
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
suspend fun incomingCallTimedOut(displayMissedCallNotification: Boolean) = mutex.withLock {
Timber.tag(tag).d("Incoming call timed out")
val previousActiveCall = activeCall.value ?: return
val notificationData = (previousActiveCall.callState as? CallState.Ringing)?.notificationData ?: return
activeCall.value = null
if (activeWakeLock?.isHeld == true) {
Timber.tag(tag).d("Releasing partial wakelock after timeout")
activeWakeLock.release()
}
@ -155,11 +164,12 @@ class DefaultActiveCallManager @Inject constructor(
override suspend fun hungUpCall(callType: CallType) = mutex.withLock {
if (activeCall.value?.callType != callType) {
Timber.w("Call type $callType does not match the active call type, ignoring")
Timber.tag(tag).w("Call type $callType does not match the active call type, ignoring")
return
}
cancelIncomingCallNotification()
if (activeWakeLock?.isHeld == true) {
Timber.tag(tag).d("Releasing partial wakelock after hang up")
activeWakeLock.release()
}
timedOutCallJob?.cancel()
@ -169,6 +179,7 @@ class DefaultActiveCallManager @Inject constructor(
override suspend fun joinedCall(callType: CallType) = mutex.withLock {
cancelIncomingCallNotification()
if (activeWakeLock?.isHeld == true) {
Timber.tag(tag).d("Releasing partial wakelock after joining call")
activeWakeLock.release()
}
timedOutCallJob?.cancel()
@ -181,6 +192,7 @@ class DefaultActiveCallManager @Inject constructor(
@SuppressLint("MissingPermission")
private suspend fun showIncomingCallNotification(notificationData: CallNotificationData) {
Timber.tag(tag).d("Displaying ringing call notification")
val notification = ringingCallNotificationCreator.createNotification(
sessionId = notificationData.sessionId,
roomId = notificationData.roomId,
@ -204,10 +216,13 @@ class DefaultActiveCallManager @Inject constructor(
}
private fun cancelIncomingCallNotification() {
appForegroundStateService.updateHasRingingCall(false)
Timber.tag(tag).d("Ringing call notification cancelled")
notificationManagerCompat.cancel(NotificationIdProvider.getForegroundServiceNotificationId(ForegroundServiceType.INCOMING_CALL))
}
private fun displayMissedCallNotification(notificationData: CallNotificationData) {
Timber.tag(tag).d("Displaying missed call notification")
coroutineScope.launch {
onMissedCallNotificationHandler.addMissedCallNotification(
sessionId = notificationData.sessionId,
@ -227,14 +242,14 @@ class DefaultActiveCallManager @Inject constructor(
.flatMapLatest { activeCall ->
val callType = activeCall.callType as CallType.RoomCall
// Get a flow of updated `hasRoomCall` and `activeRoomCallParticipants` values for the room
matrixClientProvider.getOrRestore(callType.sessionId).getOrNull()
?.getRoom(callType.roomId)
?.roomInfoFlow
?.map {
Timber.d("Has room call status changed for ringing call: ${it.hasRoomCall}")
it.hasRoomCall to (callType.sessionId in it.activeRoomCallParticipants)
}
?: flowOf()
val room = matrixClientProvider.getOrRestore(callType.sessionId).getOrNull()?.getRoom(callType.roomId) ?: run {
Timber.tag(tag).d("Couldn't find room for incoming call: $activeCall")
return@flatMapLatest flowOf()
}
room.roomInfoFlow.map {
Timber.tag(tag).d("Has room call status changed for ringing call: ${it.hasRoomCall}")
it.hasRoomCall to (callType.sessionId in it.activeRoomCallParticipants)
}
}
// We only want to check if the room active call status changes
.distinctUntilChanged()

View file

@ -8,6 +8,7 @@
package io.element.android.features.call.impl.utils
import android.net.Uri
import androidx.core.net.toUri
import javax.inject.Inject
class CallIntentDataParser @Inject constructor() {
@ -17,7 +18,7 @@ class CallIntentDataParser @Inject constructor() {
)
fun parse(data: String?): String? {
val parsedUrl = data?.let { Uri.parse(data) } ?: return null
val parsedUrl = data?.toUri() ?: return null
val scheme = parsedUrl.scheme
return when {
scheme in validHttpSchemes -> parsedUrl
@ -37,7 +38,7 @@ class CallIntentDataParser @Inject constructor() {
private fun Uri.getUrlParameter(): Uri? {
return getQueryParameter("url")
?.let { urlParameter ->
Uri.parse(urlParameter).takeIf { uri ->
urlParameter.toUri().takeIf { uri ->
uri.scheme in validHttpSchemes && !uri.host.isNullOrBlank()
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="call_foreground_service_channel_title_android">"Galwad cyfredol"</string>
<string name="call_foreground_service_message_android">"Tapio i ddychwelyd i\'r alwad"</string>
<string name="call_foreground_service_title_android">"☎️ Galwad ar y gweill"</string>
</resources>

View file

@ -35,6 +35,7 @@ import io.element.android.libraries.push.api.notifications.NotificationIdProvide
import io.element.android.libraries.push.test.notifications.FakeImageLoaderHolder
import io.element.android.libraries.push.test.notifications.FakeOnMissedCallNotificationHandler
import io.element.android.libraries.push.test.notifications.push.FakeNotificationBitmapLoader
import io.element.android.services.appnavstate.test.FakeAppForegroundStateService
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.mockk.mockk
@ -323,5 +324,6 @@ class DefaultActiveCallManagerTest {
notificationManagerCompat = notificationManagerCompat,
matrixClientProvider = matrixClientProvider,
defaultCurrentCallService = DefaultCurrentCallService(),
appForegroundStateService = FakeAppForegroundStateService(),
)
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_room_add_people_title">"Gwahodd pobl"</string>
<string name="screen_create_room_room_access_section_anyone_option_description">"Gall unrhyw un ymuno â\'r ystafell hon"</string>
<string name="screen_create_room_room_access_section_anyone_option_title">"Unrhyw un"</string>
<string name="screen_create_room_room_access_section_header">"Mynediad i\'r Ystafell"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Gall unrhyw un ofyn am gael ymuno â\'r ystafell ond bydd rhaid i weinyddwr neu gymedrolwr dderbyn y cais"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Gofyn i gael ymuno"</string>
<string name="screen_create_room_room_address_section_footer">"Er mwyn i\'r ystafell hon fod yn weladwy yn y cyfeiriadur ystafelloedd cyhoeddus, bydd angen cyfeiriad ystafell arnoch."</string>
<string name="screen_create_room_room_address_section_title">"Cyfeiriad yr ystafell"</string>
<string name="screen_create_room_room_name_label">"Enw\'r ystafell"</string>
<string name="screen_create_room_room_visibility_section_title">"Gwelededd yr ystafell"</string>
<string name="screen_create_room_title">"Creu ystafell"</string>
</resources>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_room_action_create_room">"Naujas kambarys"</string>
<string name="screen_create_room_add_people_title">"Pakviesti žmonių"</string>
<string name="screen_create_room_error_creating_room">"Kuriant kambarį įvyko klaida"</string>
<string name="screen_create_room_private_option_description">"Į šį kambarį gali patekti tik pakviesti žmonės. Visi pranešimai yra užšifruoti nuo pradžios iki galo."</string>
<string name="screen_create_room_private_option_title">"Privatus kambarys"</string>
<string name="screen_create_room_public_option_description">"Bet kas gali rasti šį kambarį.
Tai galite bet kada pakeisti kambario nustatymuose."</string>
<string name="screen_create_room_room_name_label">"Kambario pavadinimas"</string>
<string name="screen_create_room_title">"Kurti kambarį"</string>
<string name="screen_create_room_topic_label">"Tema (nebūtina)"</string>
<string name="screen_start_chat_error_starting_chat">"Bandant pradėti pokalbį įvyko klaida"</string>
</resources>

View file

@ -9,8 +9,11 @@
Du kan endre dette når som helst i rominnstillingene."</string>
<string name="screen_create_room_public_option_title">"Offentlig rom"</string>
<string name="screen_create_room_room_access_section_anyone_option_description">"Alle kan bli med i dette rommet"</string>
<string name="screen_create_room_room_access_section_anyone_option_title">"Alle"</string>
<string name="screen_create_room_room_access_section_header">"Tilgang til rom"</string>
<string name="screen_create_room_room_access_section_knocking_option_description">"Alle kan be om å få bli med i rommet, men en administrator eller moderator må godta forespørselen"</string>
<string name="screen_create_room_room_access_section_knocking_option_title">"Be om å bli med"</string>
<string name="screen_create_room_room_address_section_footer">"For at dette rommet skal være synlig i den offentlige romkatalogen, trenger du en romadresse."</string>
<string name="screen_create_room_room_address_section_title">"Romadresse"</string>
<string name="screen_create_room_room_name_label">"Romnavn"</string>
<string name="screen_create_room_room_visibility_section_title">"Romsynlighet"</string>
@ -18,7 +21,10 @@ Du kan endre dette når som helst i rominnstillingene."</string>
<string name="screen_create_room_topic_label">"Emne (valgfritt)"</string>
<string name="screen_room_directory_search_title">"Romkatalog"</string>
<string name="screen_start_chat_error_starting_chat">"Det oppstod en feil når du prøvde å starte en chat"</string>
<string name="screen_start_chat_join_room_by_address_action">"Bli med i rommet med adresse"</string>
<string name="screen_start_chat_join_room_by_address_invalid_address">"Ikke en gyldig adresse"</string>
<string name="screen_start_chat_join_room_by_address_placeholder">"Gå inn…"</string>
<string name="screen_start_chat_join_room_by_address_room_found">"Matchende rom funnet"</string>
<string name="screen_start_chat_join_room_by_address_room_not_found">"Rom ikke funnet"</string>
<string name="screen_start_chat_join_room_by_address_supporting_text">"f.eks. #rom-navn:matrix.org"</string>
</resources>

View file

@ -3,7 +3,7 @@
<string name="screen_create_room_action_create_room">"Nieuwe kamer"</string>
<string name="screen_create_room_add_people_title">"Mensen uitnodigen"</string>
<string name="screen_create_room_error_creating_room">"Er is een fout opgetreden bij het aanmaken van de kamer"</string>
<string name="screen_create_room_private_option_description">"Berichten in deze kamer zijn versleuteld. Versleuteling kan achteraf niet worden uitgeschakeld."</string>
<string name="screen_create_room_private_option_description">"Alleen uitgenodigde personen hebben toegang tot deze kamer. Alle berichten zijn end-to-end versleuteld."</string>
<string name="screen_create_room_private_option_title">"Privé kamer"</string>
<string name="screen_create_room_public_option_description">"Iedereen kan deze kamer vinden.
Je kunt dit op elk gewenst moment wijzigen in de kamerinstellingen."</string>

View file

@ -4,7 +4,7 @@
<string name="screen_create_room_add_people_title">"Convidar pessoas"</string>
<string name="screen_create_room_error_creating_room">"Ocorreu um erro ao criar a sala"</string>
<string name="screen_create_room_private_option_description">"Apenas as pessoas convidadas podem aceder a esta sala. Todas as mensagens são criptografadas de ponta a ponta."</string>
<string name="screen_create_room_private_option_title">"Sala privativa (somente por convite)"</string>
<string name="screen_create_room_private_option_title">"Sala privada"</string>
<string name="screen_create_room_public_option_description">"Qualquer um pode encontrar esta sala.
Você pode mudar isso a qualquer momento nas configurações da sala."</string>
<string name="screen_create_room_room_name_label">"Nome da sala"</string>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_deactivate_account_title">"Cau cyfrif"</string>
</resources>

View file

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_deactivate_account_delete_all_messages">"Ezabatu nire mezu guztiak"</string>
<string name="screen_deactivate_account_description">"Kontuaren desaktibazioa %1$s, honakoa eragingo du:"</string>
<string name="screen_deactivate_account_description_bold_part">"ezin da desegin"</string>
<string name="screen_deactivate_account_list_item_1_bold_part">"Ezgaitu betiko"</string>
<string name="screen_deactivate_account_list_item_2">"Kendu zure burua txat gela guztietatik."</string>
<string name="screen_deactivate_account_title">"Desaktibatu kontua"</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_welcome_button">"Ffwrdd â ni!"</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_join_room_decline_and_block_alert_confirmation">"Iawn, gwrthod a rhwystro"</string>
<string name="screen_join_room_decline_and_block_alert_message">"Ydych chi\'n siŵr eich bod am wrthod y gwahoddiad i ymuno â\'r ystafell hon? Bydd hyn hefyd yn atal %1$s rhag cysylltu â chi neu eich gwahodd i ystafelloedd."</string>
<string name="screen_join_room_decline_and_block_alert_title">"Gwrthod gwahoddiad a rhwystro"</string>
<string name="screen_join_room_decline_and_block_button_title">"Gwrthod a rhwystro"</string>
</resources>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_invites_decline_chat_message">"Ar tikrai norite atmesti kvietimą prisijungti prie %1$s?"</string>
<string name="screen_invites_decline_chat_title">"Atmesti kvietimą"</string>
<string name="screen_invites_decline_direct_chat_message">"Ar tikrai norite atmesti šį privatų pokalbį su %1$s ?"</string>
<string name="screen_invites_decline_direct_chat_title">"Atmesti pokalbį"</string>
<string name="screen_invites_empty_list">"Jokių kvietimų"</string>
<string name="screen_invites_invited_you">"%1$s(%2$s) pakvietė Jus"</string>
</resources>

View file

@ -7,6 +7,7 @@
<string name="screen_invites_empty_list">"Ingen invitasjoner"</string>
<string name="screen_invites_invited_you">"%1$s(%2$s) inviterte deg"</string>
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ja, avslå og blokker"</string>
<string name="screen_join_room_decline_and_block_alert_message">"Er du sikker på at du vil avslå invitasjonen til å bli med i dette rommet? Dette vil også forhindre %1$s fra å kontakte deg eller invitere deg til rom."</string>
<string name="screen_join_room_decline_and_block_alert_title">"Avslå invitasjon og blokker"</string>
<string name="screen_join_room_decline_and_block_button_title">"Avslå og blokker"</string>
</resources>

View file

@ -47,7 +47,7 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
),
aJoinRoomState(
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin),
joinAction = AsyncAction.Failure(ClientException.Generic("Something went wrong"))
joinAction = AsyncAction.Failure(ClientException.Generic("Something went wrong", null))
),
aJoinRoomState(
contentState = aLoadedContentState(joinAuthorisationStatus = JoinAuthorisationStatus.IsInvited(null))

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_join_room_cancel_knock_action">"Diddymu cais"</string>
<string name="screen_join_room_cancel_knock_alert_confirmation">"Iawn, diddymu"</string>
<string name="screen_join_room_cancel_knock_alert_description">"Ydych chi\'n siŵr eich bod am ddiddymu\'ch cais i ymuno â\'r ystafell hon?"</string>
<string name="screen_join_room_cancel_knock_alert_title">"Diddymu cais i ymuno"</string>
<string name="screen_join_room_decline_and_block_alert_confirmation">"Iawn, gwrthod a rhwystro"</string>
<string name="screen_join_room_decline_and_block_alert_message">"Ydych chi\'n siŵr eich bod am wrthod y gwahoddiad i ymuno â\'r ystafell hon? Bydd hyn hefyd yn atal %1$s rhag cysylltu â chi neu eich gwahodd i ystafelloedd."</string>
<string name="screen_join_room_decline_and_block_alert_title">"Gwrthod gwahoddiad a rhwystro"</string>
<string name="screen_join_room_decline_and_block_button_title">"Gwrthod a rhwystro"</string>
<string name="screen_join_room_knock_message_description">"Neges (dewisol)"</string>
<string name="screen_join_room_knock_sent_description">"Byddwch yn derbyn gwahoddiad i ymuno â\'r ystafell os caiff eich cais ei dderbyn."</string>
<string name="screen_join_room_knock_sent_title">"Anfonwyd y cais i ymuno"</string>
<string name="screen_join_room_loading_alert_message">"Doedd dim modd dangos rhagolwg yr ystafell. Gall hyn fod oherwydd problemau rhwydwaith neu weinydd."</string>
<string name="screen_join_room_loading_alert_title">"Doedd dim modd dangos rhagolwg yr ystafell hon"</string>
</resources>

View file

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_join_room_ban_reason">"Arrazoia: %1$s."</string>
<string name="screen_join_room_cancel_knock_action">"Utzi eskaera bertan behera"</string>
<string name="screen_join_room_cancel_knock_alert_confirmation">"Bai, utzi bertan behera"</string>
<string name="screen_join_room_forget_action">"Ahaztu gela hau"</string>
<string name="screen_join_room_join_action">"Sartu gelan"</string>
<string name="screen_join_room_knock_action">"Bidali batzeko eskaera"</string>
<string name="screen_join_room_knock_message_description">"Mezua (aukerakoa)"</string>

View file

@ -8,6 +8,7 @@
<string name="screen_join_room_cancel_knock_alert_description">"Er du sikker på at du vil kansellere forespørselen din om å bli med i dette rommet?"</string>
<string name="screen_join_room_cancel_knock_alert_title">"Avbryt forespørsel om å bli med"</string>
<string name="screen_join_room_decline_and_block_alert_confirmation">"Ja, avslå og blokker"</string>
<string name="screen_join_room_decline_and_block_alert_message">"Er du sikker på at du vil avslå invitasjonen til å bli med i dette rommet? Dette vil også forhindre %1$s fra å kontakte deg eller invitere deg til rom."</string>
<string name="screen_join_room_decline_and_block_alert_title">"Avslå invitasjon og blokker"</string>
<string name="screen_join_room_decline_and_block_button_title">"Avslå og blokker"</string>
<string name="screen_join_room_fail_message">"Å bli med i rommet mislyktes."</string>
@ -20,6 +21,8 @@
<string name="screen_join_room_knock_message_description">"Melding (valgfritt)"</string>
<string name="screen_join_room_knock_sent_description">"Du vil motta en invitasjon til å bli med i rommet hvis forespørselen din blir akseptert."</string>
<string name="screen_join_room_knock_sent_title">"Forespørsel om å bli med sendt"</string>
<string name="screen_join_room_loading_alert_message">"Vi kunne ikke vise forhåndsvisningen av rommet. Dette kan skyldes nettverks- eller serverproblemer."</string>
<string name="screen_join_room_loading_alert_title">"Vi kunne ikke vise forhåndsvisning av dette rommet"</string>
<string name="screen_join_room_space_not_supported_description">"%1$s støtter ikke områder ennå. Du kan få tilgang til områder på nett."</string>
<string name="screen_join_room_space_not_supported_title">"Områder støttes ikke ennå"</string>
<string name="screen_join_room_subtitle_knock">"Klikk på knappen nedenfor, så vil en romadministrator bli varslet. Du vil kunne delta i samtalen når den er godkjent."</string>

View file

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_join_room_cancel_knock_action">"Verzoek annuleren"</string>
<string name="screen_join_room_join_action">"Toetreden tot de kamer"</string>
<string name="screen_join_room_knock_action">"Klop om deel te nemen"</string>
<string name="screen_join_room_knock_message_description">"Bericht (optioneel)"</string>
<string name="screen_join_room_knock_sent_description">"Je ontvangt een uitnodiging om deel te nemen aan de kamer als je aanvraag wordt geaccepteerd."</string>
<string name="screen_join_room_knock_sent_title">"Verzoek om toe te treden verzonden"</string>
<string name="screen_join_room_space_not_supported_description">"%1$s ondersteunt nog geen spaces. Je kunt spaces benaderen via de webbrowser."</string>
<string name="screen_join_room_space_not_supported_title">"Spaces worden nog niet ondersteund"</string>

View file

@ -250,7 +250,7 @@ class JoinRoomPresenterTest {
val presenter = createJoinRoomPresenter(
roomDescription = Optional.of(roomDescription),
joinRoomLambda = { _, _, _ ->
Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden"))
Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden", null))
},
)
presenter.test {
@ -742,7 +742,7 @@ class JoinRoomPresenterTest {
fun `present - when room is not known RoomPreview is loaded with error Forbidden`() = runTest {
val client = FakeMatrixClient(
getRoomPreviewResult = { _, _ ->
Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden"))
Result.failure(ClientException.MatrixApi(ErrorKind.Forbidden, "403", "Forbidden", null))
}
)
val presenter = createJoinRoomPresenter(

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_knock_requests_list_accept_all_alert_confirm_button_title">"Iawn, derbyn y cyfan"</string>
<string name="screen_knock_requests_list_accept_all_alert_description">"Ydych chi\'n siŵr eich bod am dderbyn pob cais i ymuno?"</string>
<string name="screen_knock_requests_list_accept_all_alert_title">"Derbyn pob cais"</string>
<string name="screen_knock_requests_list_accept_all_button_title">"Derbyn y cyfan"</string>
<string name="screen_knock_requests_list_accept_all_failed_alert_description">"Doedd dim modd derbyn pob cais. Hoffech chi roi cynnig arall arni?"</string>
<string name="screen_knock_requests_list_accept_all_failed_alert_title">"Wedi methu derbyn pob cais"</string>
<string name="screen_knock_requests_list_accept_all_loading_title">"Yn derbyn pob cais i ymuno"</string>
<string name="screen_knock_requests_list_accept_failed_alert_description">"Doedd dim modd derbyn y cais hwn. Hoffech chi roi cynnig arall arni?"</string>
<string name="screen_knock_requests_list_accept_failed_alert_title">"Wedi methu â derbyn y cais"</string>
<string name="screen_knock_requests_list_accept_loading_title">"Yn derbyn cais i ymuno"</string>
<string name="screen_knock_requests_list_ban_alert_confirm_button_title">"Iawn, gwrthod a gwahardd"</string>
<string name="screen_knock_requests_list_ban_alert_description">"Ydych chi\'n siŵr eich bod am wrthod a gwahardd %1$s? Bydd y defnyddiwr hwn ddim yn gallu gofyn am fynediad i ymuno â\'r ystafell hon eto."</string>
<string name="screen_knock_requests_list_ban_alert_title">"Gwrthod a gwahardd mynediad"</string>
<string name="screen_knock_requests_list_ban_loading_title">"Yn gwrthod a gwahardd mynediad"</string>
<string name="screen_knock_requests_list_decline_alert_confirm_button_title">"Iawn, gwrthod"</string>
<string name="screen_knock_requests_list_decline_alert_description">"Ydych chi\'n siŵr eich bod am wrthod %1$s cais i ymuno â\'r ystafell hon?"</string>
<string name="screen_knock_requests_list_decline_alert_title">"Gwrthod mynediad"</string>
<string name="screen_knock_requests_list_decline_and_ban_action_title">"Gwrthod a gwahardd"</string>
<string name="screen_knock_requests_list_decline_failed_alert_description">"Doedd dim modd i ni wrthod y cais hwn. Hoffech chi roi cynnig arall arni?"</string>
<string name="screen_knock_requests_list_decline_failed_alert_title">"Wedi methu â gwrthod y cais"</string>
<string name="screen_knock_requests_list_decline_loading_title">"Yn gwrthod cais i ymuno"</string>
<string name="screen_knock_requests_list_empty_state_description">"Pan fydd rhywun yn gofyn am gael ymuno â\'r ystafell, byddwch yn gallu gweld eu cais yma."</string>
<string name="screen_knock_requests_list_empty_state_title">"Dim cais i ymuno yn disgwyl"</string>
<string name="screen_knock_requests_list_initial_loading_title">"Yn llwytho ceisiadau i ymuno…"</string>
<string name="screen_knock_requests_list_title">"Ceisiadau i ymuno"</string>
<string name="screen_room_multiple_knock_requests_view_all_button_title">"Gweld y cyfan"</string>
<string name="screen_room_single_knock_request_accept_button_title">"Derbyn"</string>
<string name="screen_room_single_knock_request_title">"Mae %1$s eisiau ymuno â\'r ystafell hon"</string>
<string name="screen_room_single_knock_request_view_button_title">"Golwg"</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_room_single_knock_request_accept_button_title">"Priimti"</string>
</resources>

View file

@ -1,9 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_knock_requests_list_accept_all_alert_confirm_button_title">"Ja, godta alle"</string>
<string name="screen_knock_requests_list_accept_all_alert_description">"Er du sikker på at du vil godta alle forespørsler om å bli med?"</string>
<string name="screen_knock_requests_list_accept_all_alert_title">"Godta alle forespørsler"</string>
<string name="screen_knock_requests_list_accept_all_button_title">"Godta alle"</string>
<string name="screen_knock_requests_list_accept_all_failed_alert_description">"Vi kunne ikke godta alle forespørsler. Vil du prøve igjen?"</string>
<string name="screen_knock_requests_list_accept_all_failed_alert_title">"Kunne ikke godta alle forespørsler"</string>
<string name="screen_knock_requests_list_accept_all_loading_title">"Godtar alle forespørsler om å bli med"</string>
<string name="screen_knock_requests_list_accept_failed_alert_description">"Vi kunne ikke godta denne forespørselen. Vil du prøve igjen?"</string>
<string name="screen_knock_requests_list_accept_failed_alert_title">"Kunne ikke godta forespørselen"</string>
<string name="screen_knock_requests_list_accept_loading_title">"Godtar forespørsel om å bli med"</string>
<string name="screen_knock_requests_list_ban_alert_confirm_button_title">"Ja, avslå og utesteng"</string>
<string name="screen_knock_requests_list_ban_alert_description">"Er du sikker på at du vil avvise og utestenge %1$s? Denne brukeren vil ikke kunne be om tilgang til dette rommet igjen."</string>
<string name="screen_knock_requests_list_ban_alert_title">"Avslå og forby tilgang"</string>
<string name="screen_knock_requests_list_ban_loading_title">"Avslår og forbyr tilgang"</string>
<string name="screen_knock_requests_list_decline_alert_confirm_button_title">"Ja, avslå"</string>
<string name="screen_knock_requests_list_decline_alert_description">"Er du sikker på at du vil avslå %1$ss forespørsel om å bli med i dette rommet?"</string>
@ -12,8 +21,14 @@
<string name="screen_knock_requests_list_decline_failed_alert_description">"Vi kunne ikke avslå denne forespørselen. Vil du prøve igjen?"</string>
<string name="screen_knock_requests_list_decline_failed_alert_title">"Kunne ikke avslå forespørselen"</string>
<string name="screen_knock_requests_list_decline_loading_title">"Avslår forespørsel om å bli med"</string>
<string name="screen_knock_requests_list_empty_state_description">"Når noen ber om å bli med i rommet, vil du kunne se forespørselen deres her."</string>
<string name="screen_knock_requests_list_empty_state_title">"Ingen ventende forespørsel om å bli med"</string>
<string name="screen_knock_requests_list_initial_loading_title">"Laster inn forespørsler om å bli med…"</string>
<string name="screen_knock_requests_list_title">"Forespørsler om å bli med"</string>
<plurals name="screen_room_multiple_knock_requests_title">
<item quantity="one">"%1$s +%2$d andre ønsker å bli med i dette rommet"</item>
<item quantity="other">"%1$s +%2$d andre ønsker å bli med i dette rommet"</item>
</plurals>
<string name="screen_room_multiple_knock_requests_view_all_button_title">"Vis alle"</string>
<string name="screen_room_single_knock_request_accept_button_title">"Godta"</string>
<string name="screen_room_single_knock_request_title">"%1$s ønsker å bli med i dette rommet"</string>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="leave_conversation_alert_subtitle">"Ydych chi\'n siŵr eich bod am adael y sgwrs hon? Dyw\'r sgwrs hon ddim yn gyhoeddus a fyddwch chi ddim yn gallu ailymuno heb wahoddiad."</string>
<string name="leave_room_alert_empty_subtitle">"Ydych chi\'n siŵr eich bod am adael yr ystafell hon? Chi yw\'r unig berson yma. Os byddwch yn gadael, fydd neb yn gallu ymuno yn y dyfodol, gan gynnwys chi."</string>
<string name="leave_room_alert_private_subtitle">"Ydych chi\'n siŵr eich bod am adael yr ystafell hon? Dyw\'r ystafell hon ddim yn gyhoeddus a fyddwch chi ddim yn gallu ailymuno heb wahoddiad."</string>
<string name="leave_room_alert_subtitle">"Ydych chi\'n siŵr eich bod am adael yr ystafell?"</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="leave_room_alert_empty_subtitle">"Ar tikrai norite išeiti iš šio kambario? Jūs esate vienintelis žmogus jame. Jei išeisite, niekas negalės prisijungti ateityje, įskaitant Jus."</string>
<string name="leave_room_alert_private_subtitle">"Ar tikrai norite išeiti iš šio kambario? Šis kambarys nėra viešas ir negalėsite vėl prisijungti be kvietimo."</string>
<string name="leave_room_alert_subtitle">"Ar tikrai norite išeiti iš kambario?"</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="leave_conversation_alert_subtitle">"Tem certeza de que deseja sair dessa conversa? Essa conversa não é pública e você não poderá entrar novamente sem um convite."</string>
<string name="leave_room_alert_empty_subtitle">"Tem certeza de que deseja sair desta sala? Você é a única pessoa aqui. Se você sair, ninguém poderá ingressar no futuro, inclusive você."</string>
<string name="leave_room_alert_private_subtitle">"Tem certeza de que deseja sair desta sala? Esta sala não é pública e você não poderá entrar novamente sem um convite."</string>
<string name="leave_room_alert_subtitle">"Tem certeza de que deseja sair da sala?"</string>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_app_lock_forgot_pin">"Wedi anghofio\'ch PIN?"</string>
<string name="screen_app_lock_setup_confirm_pin">"Cadarnhau eich PIN"</string>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_signout_in_progress_dialog_content">"Atsijungiama…"</string>
</resources>

View file

@ -9,11 +9,11 @@ package io.element.android.features.login.impl.util
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.core.net.toUri
import io.element.android.appconfig.AuthenticationConfig
import io.element.android.libraries.core.data.tryOrNull
fun openLearnMorePage(context: Context) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(AuthenticationConfig.SLIDING_SYNC_READ_MORE_URL))
val intent = Intent(Intent.ACTION_VIEW, AuthenticationConfig.SLIDING_SYNC_READ_MORE_URL.toUri())
tryOrNull { context.startActivity(intent) }
}

View file

@ -7,7 +7,7 @@
package io.element.android.features.login.impl.web
import android.net.Uri
import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.appconfig.AuthenticationConfig
import io.element.android.features.login.impl.resolver.network.WellknownAPI
@ -43,7 +43,7 @@ class DefaultWebClientUrlForAuthenticationRetriever @Inject constructor(
}
val registrationHelperUrl = result.registrationHelperUrl
return if (registrationHelperUrl != null) {
Uri.parse(registrationHelperUrl)
registrationHelperUrl.toUri()
.buildUpon()
.appendQueryParameter("hs_url", homeServerUrl)
.build()

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_change_account_provider_other">"Arall"</string>
<string name="screen_create_account_title">"Creu cyfrif"</string>
<string name="screen_login_error_deactivated_account">"Mae\'r cyfrif hwn wedi\'i gau."</string>
<string name="screen_login_title">"Croeso nôl!"</string>
<string name="screen_qr_code_login_connection_note_secure_state_title">"Nid yw\'r cysylltiad yn ddiogel"</string>
<string name="screen_qr_code_login_error_cancelled_subtitle">"Cafodd y mewngofnodi ei ddiddymu ar y ddyfais arall."</string>
<string name="screen_qr_code_login_error_cancelled_title">"Cais mewngofnodi wedi\'i ddiddymu"</string>
<string name="screen_qr_code_login_error_declined_title">"Gwrthodwyd y mewngofnodi"</string>
<string name="screen_qr_code_login_error_expired_title">"Heb gwblhau\'r mewngofnodi mewn pryd"</string>
<string name="screen_qr_code_login_error_linking_not_suported_title">"Nid yw\'r cod QR yn cael ei gefnogi"</string>
<string name="screen_qr_code_login_invalid_scan_state_retry_button">"Ceisiwch eto"</string>
<string name="screen_qr_code_login_unknown_error_description">"Digwyddodd gwall annisgwyl. Ceisiwch eto."</string>
</resources>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_account_provider_change">"Keisti paskyros teikėją"</string>
<string name="screen_account_provider_signin_subtitle">"Čia bus saugomi Jūsų pokalbiai - panašiai kaip el. pašto paslaugų teikėjas saugo Jūsų el. laiškus."</string>
<string name="screen_account_provider_signup_subtitle">"Čia bus saugomi Jūsų pokalbiai - panašiai kaip el. pašto paslaugų teikėjas saugo Jūsų el. laiškus."</string>
<string name="screen_change_account_provider_title">"Keisti paskyros teikėją"</string>
<string name="screen_change_server_error_invalid_homeserver">"Nepavyko pasiekti šio serverio. Patikrinkite, ar teisingai įvedėte serverio URL. Jei URL yra teisingas, susisiekite su serverio administracija dėl tolimesnės pagalbos."</string>
<string name="screen_change_server_error_no_sliding_sync_message">"Šiuo metu šis serveris nepalaiko slenkančios sinchronizacijos (sliding sync)."</string>
<string name="screen_change_server_form_header">"Serverio URL"</string>
<string name="screen_change_server_form_notice">"Galite prisijungti tik prie serverio, palaikančio slenkančią sinchronizaciją (sliding sync). Jūsų serverio administracija turėtų ją sukonfigūruoti. %1$s"</string>
<string name="screen_change_server_subtitle">"Koks yra Jūsų serverio adresas?"</string>
<string name="screen_create_account_title">"Sukurti paskyrą"</string>
<string name="screen_login_error_deactivated_account">"Ši paskyra buvo išjungta."</string>
<string name="screen_login_error_invalid_credentials">"Neteisingas vartotojo vardas ir (arba) slaptažodis"</string>
<string name="screen_login_error_invalid_user_id">"Tai nėra tinkamas vartotojo vardas. Reikalingas formatas: \'@vartotojas:serveris.org\'"</string>
<string name="screen_login_error_unsupported_authentication">"Pasirinktas serveris nepalaiko slaptažodžio ar OIDC prisijungimo. Susisiekite su serverio administracija arba pasirinkite kitą serverį."</string>
<string name="screen_login_form_header">"Įveskite savo duomenis"</string>
<string name="screen_login_subtitle">"Matrix yra atviras tinklas, skirtas saugiam, decentralizuotam bendravimui."</string>
<string name="screen_login_title">"Sveiki sugrįžę!"</string>
<string name="screen_login_title_with_homeserver">"Prisijungti prie %1$s"</string>
<string name="screen_server_confirmation_change_server">"Keisti paskyros teikėją"</string>
<string name="screen_server_confirmation_message_login_element_dot_io">"Privatus serveris “Element” darbuotojams."</string>
<string name="screen_server_confirmation_message_login_matrix_dot_org">"Matrix yra atviras tinklas, skirtas saugiam, decentralizuotam bendravimui."</string>
<string name="screen_server_confirmation_message_register">"Čia bus saugomi Jūsų pokalbiai - panašiai kaip el. pašto paslaugų teikėjas saugo Jūsų el. laiškus."</string>
<string name="screen_server_confirmation_title_login">"Jūs ruošiatės prisijungti prie %1$s"</string>
<string name="screen_server_confirmation_title_register">"Jūs ruošiatės susikurti paskyrą %1$s"</string>
</resources>

View file

@ -55,6 +55,7 @@ Prøv å logge på manuelt, eller skann QR-koden med en annen enhet."</string>
<string name="screen_qr_code_login_error_sliding_sync_not_supported_subtitle">"Kontotilbyderen din støtter ikke %1$s."</string>
<string name="screen_qr_code_login_error_sliding_sync_not_supported_title">"%1$s støttes ikke"</string>
<string name="screen_qr_code_login_initial_state_button_title">"Klar til å skanne"</string>
<string name="screen_qr_code_login_initial_state_item_1">"Åpne %1$s på en datamaskin"</string>
<string name="screen_qr_code_login_initial_state_item_2">"Klikk på avataren din"</string>
<string name="screen_qr_code_login_initial_state_item_3">"Velg %1$s"</string>
<string name="screen_qr_code_login_initial_state_item_3_action">"«Koble til ny enhet»"</string>

View file

@ -23,6 +23,7 @@
<string name="screen_login_error_deactivated_account">"Essa conta foi desativada."</string>
<string name="screen_login_error_invalid_credentials">"Nome de usuário e/ou senha incorretos"</string>
<string name="screen_login_error_invalid_user_id">"Esse não é um identificador de usuário válido. Formato esperado: \'@usuário:servidor.org\'"</string>
<string name="screen_login_error_refresh_tokens">"Este servidor está configurado para usar tokens de atualização. Eles não são suportados ao usar login baseado em senha."</string>
<string name="screen_login_error_unsupported_authentication">"O servidor selecionado não suporta senha ou login no OIDC. Entre em contato com o administrador ou escolha outro servidor."</string>
<string name="screen_login_form_header">"Insira seus dados"</string>
<string name="screen_login_subtitle">"A Matrix é uma rede aberta para comunicação segura e descentralizada."</string>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_signout_confirmation_dialog_submit">"Allgofnodi"</string>
<string name="screen_signout_confirmation_dialog_title">"Allgofnodi"</string>
<string name="screen_signout_preference_item">"Allgofnodi"</string>
</resources>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_signout_confirmation_dialog_content">"Ar tikrai norite atsijungti?"</string>
<string name="screen_signout_in_progress_dialog_content">"Atsijungiama…"</string>
</resources>

View file

@ -4,10 +4,15 @@
<string name="screen_signout_confirmation_dialog_submit">"Sair"</string>
<string name="screen_signout_confirmation_dialog_title">"Sair"</string>
<string name="screen_signout_in_progress_dialog_content">"Saindo…"</string>
<string name="screen_signout_key_backup_disabled_subtitle">"Você está prestes a sair da sua última sessão. Se você sair agora, perderá o acesso às suas mensagens criptografadas."</string>
<string name="screen_signout_key_backup_disabled_title">"Você desativou o backup"</string>
<string name="screen_signout_key_backup_offline_subtitle">"O backup das suas chaves ainda estava sendo feito quando você ficou off-line. Reconecte-se para que você possa fazer o backup de suas chaves antes de sair."</string>
<string name="screen_signout_key_backup_offline_title">"O backup das suas chaves ainda está em andamento"</string>
<string name="screen_signout_key_backup_ongoing_subtitle">"Aguarde até que isso seja concluído antes de sair."</string>
<string name="screen_signout_key_backup_ongoing_title">"O backup das suas chaves ainda está em andamento"</string>
<string name="screen_signout_preference_item">"Sair"</string>
<string name="screen_signout_recovery_disabled_subtitle">"Você está prestes a sair da sua última sessão. Se você sair agora, perderá o acesso às suas mensagens criptografadas."</string>
<string name="screen_signout_recovery_disabled_title">"A recuperação não está configurada"</string>
<string name="screen_signout_save_recovery_key_subtitle">"Você está prestes a sair da sua última sessão. Se você sair agora, poderá perder o acesso às suas mensagens criptografadas."</string>
<string name="screen_signout_save_recovery_key_title">"Você salvou sua chave de recuperação?"</string>
</resources>

View file

@ -363,7 +363,9 @@ private fun MessagesViewContent(
},
content = { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) {
val scrollBehavior = PinnedMessagesBannerViewDefaults.rememberExitOnScrollBehavior()
val scrollBehavior = PinnedMessagesBannerViewDefaults.rememberScrollBehavior(
pinnedMessagesCount = state.pinnedMessagesBannerState.pinnedMessagesCount(),
)
TimelineView(
state = state.timelineState,
timelineProtectionState = state.timelineProtectionState,

View file

@ -39,6 +39,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.traversalIndex
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -188,6 +190,7 @@ private fun ActionListViewContent(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.clearAndSetSemantics {},
)
if (target.event.messageShield != null) {
MessageShieldView(
@ -347,12 +350,21 @@ private fun EmojiReactionsRow(
)
for (emoji in defaultEmojis) {
val isHighlighted = highlightedEmojis.contains(emoji)
EmojiButton(emoji, isHighlighted, onEmojiReactionClick)
EmojiButton(
modifier = Modifier
// Make it appear after the more useful actions for the accessibility service
.semantics {
traversalIndex = 1f
},
emoji = emoji,
isHighlighted = isHighlighted,
onClick = onEmojiReactionClick
)
}
Box(
modifier = Modifier
.size(48.dp),
contentAlignment = Alignment.Center
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = CompoundIcons.ReactionAdd(),
@ -366,6 +378,10 @@ private fun EmojiReactionsRow(
indication = ripple(bounded = false, radius = emojiRippleRadius),
interactionSource = remember { MutableInteractionSource() }
)
// Make it appear after the more useful actions for the accessibility service
.semantics {
traversalIndex = 1f
}
)
}
}
@ -413,6 +429,7 @@ private fun EmojiButton(
emoji: String,
isHighlighted: Boolean,
onClick: (String) -> Unit,
modifier: Modifier = Modifier,
) {
val backgroundColor = if (isHighlighted) {
ElementTheme.colors.bgActionPrimaryRest
@ -425,10 +442,16 @@ private fun EmojiButton(
stringResource(id = CommonStrings.a11y_react_with, emoji)
}
Box(
modifier = Modifier
modifier = modifier
.size(48.dp)
.background(backgroundColor, CircleShape)
.clearAndSetSemantics {
.clickable(
enabled = true,
onClick = { onClick(emoji) },
indication = ripple(bounded = false, radius = emojiRippleRadius),
interactionSource = remember { MutableInteractionSource() }
)
.semantics {
contentDescription = description
},
contentAlignment = Alignment.Center
@ -436,13 +459,6 @@ private fun EmojiButton(
Text(
emoji,
style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = 24.dp.toSp(), color = Color.White),
modifier = Modifier
.clickable(
enabled = true,
onClick = { onClick(emoji) },
indication = ripple(bounded = false, radius = emojiRippleRadius),
interactionSource = remember { MutableInteractionSource() }
)
)
}
}

View file

@ -48,6 +48,7 @@ internal fun DisabledComposerView(
IconColorButton(
onClick = {},
imageVector = CompoundIcons.Plus(),
contentDescription = null,
iconColorButtonStyle = IconColorButtonStyle.Disabled,
)

View file

@ -265,7 +265,7 @@ internal interface PinnedMessagesBannerViewScrollBehavior {
internal object PinnedMessagesBannerViewDefaults {
@Composable
fun rememberExitOnScrollBehavior(): PinnedMessagesBannerViewScrollBehavior = remember {
fun rememberScrollBehavior(pinnedMessagesCount: Int): PinnedMessagesBannerViewScrollBehavior = remember(pinnedMessagesCount) {
ExitOnScrollBehavior()
}
}

View file

@ -8,7 +8,6 @@
package io.element.android.features.messages.impl.timeline
import android.view.HapticFeedbackConstants
import android.view.accessibility.AccessibilityManager
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
@ -75,6 +74,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.time.isTalkbackActive
import io.element.android.wysiwyg.link.Link
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
@ -126,10 +126,7 @@ fun TimelineView(
val context = LocalContext.current
val view = LocalView.current
// Disable reverse layout when TalkBack is enabled to avoid incorrect ordering issues seen in the current Compose UI version
val useReverseLayout = remember {
val accessibilityManager = context.getSystemService(AccessibilityManager::class.java)
accessibilityManager.isTouchExplorationEnabled.not()
}
val useReverseLayout = !isTalkbackActive()
fun inReplyToClick(eventId: EventId) {
state.eventSink(TimelineEvents.FocusOnEvent(eventId))
@ -159,7 +156,7 @@ fun TimelineView(
.testTag(TestTags.timeline),
state = lazyListState,
reverseLayout = useReverseLayout,
contentPadding = PaddingValues(vertical = 8.dp),
contentPadding = PaddingValues(top = 64.dp, bottom = 8.dp),
) {
items(
items = state.timelineItems,

View file

@ -49,6 +49,7 @@ import io.element.android.libraries.designsystem.theme.messageFromMeBackground
import io.element.android.libraries.designsystem.theme.messageFromOtherBackground
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.utils.time.isTalkbackActive
private val BUBBLE_RADIUS = 12.dp
private val avatarRadius = AvatarSize.TimelineSender.dp / 2
@ -95,6 +96,17 @@ fun MessageEventBubble(
}
}
val clickableModifier = if (isTalkbackActive()) {
Modifier
} else {
Modifier.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
indication = ripple(),
interactionSource = interactionSource
)
}
// Ignore state.isHighlighted for now, we need a design decision on it.
val backgroundBubbleColor = when {
state.isMine -> ElementTheme.colors.messageFromMeBackground
@ -137,12 +149,7 @@ fun MessageEventBubble(
.toDp()
)
.clip(bubbleShape)
.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
indication = ripple(),
interactionSource = interactionSource
),
.then(clickableModifier),
color = backgroundBubbleColor,
shape = bubbleShape,
content = content

View file

@ -18,6 +18,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
@ -77,7 +79,8 @@ fun TimelineEventTimestampView(
.size(15.dp, 18.dp)
.clickable(isVerifiedUserSendFailure) {
eventSink(TimelineEvents.ComputeVerifiedUserSendFailure(event))
},
}
.semantics { invisibleToUser() }
)
}
@ -91,7 +94,8 @@ fun TimelineEventTimestampView(
.size(15.dp)
.clickable {
eventSink(TimelineEvents.ShowShieldDialog(shield))
},
}
.semantics { invisibleToUser() },
tint = shield.toIconColor(),
)
Spacer(modifier = Modifier.width(4.dp))

View file

@ -37,13 +37,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.ViewConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.semantics.traversalIndex
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
@ -96,6 +95,7 @@ import io.element.android.libraries.matrix.ui.messages.sender.SenderNameMode
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.time.isTalkbackActive
import io.element.android.wysiwyg.link.Link
import kotlinx.coroutines.launch
import kotlin.math.abs
@ -245,7 +245,7 @@ fun TimelineItemEventRow(
),
renderReadReceipts = renderReadReceipts,
onReadReceiptsClick = { onReadReceiptClick(event) },
modifier = Modifier.padding(top = 4.dp),
modifier = Modifier.padding(top = 4.dp)
)
}
}
@ -595,7 +595,10 @@ private fun MessageEventBubbleContent(
timestampPosition = timestampPosition,
eventSink = eventSink,
canShrinkContent = canShrinkContent,
modifier = timestampLayoutModifier,
modifier = timestampLayoutModifier.semantics(mergeDescendants = false) {
isTraversalGroup = true
traversalIndex = -1f
},
content = { onContentLayoutChange ->
eventContentView(contentModifier, onContentLayoutChange)
}
@ -607,17 +610,23 @@ private fun MessageEventBubbleContent(
val inReplyToModifier = Modifier
.padding(top = topPadding, start = 8.dp, end = 8.dp)
.clip(RoundedCornerShape(6.dp))
// FIXME when a node is clickable, its contents won't be added to the semantics tree of its parent
.clickable(onClick = inReplyToClick)
val talkbackCompatModifier = if (isTalkbackActive()) {
// Use z-index to make the replied to text being read after the message
// Usually, you'd use traversalIndex for that, but it's not working for some reason
inReplyToModifier.zIndex(1f)
} else {
inReplyToModifier.clickable(onClick = inReplyToClick)
}
InReplyToView(
inReplyTo = inReplyTo,
hideImage = timelineProtectionState.hideMediaContent(inReplyTo.eventId()),
modifier = inReplyToModifier,
modifier = talkbackCompatModifier,
)
}
if (inReplyToDetails != null) {
// Use SubComposeLayout only if necessary as it can have consequences on the performance.
EqualWidthColumn(modifier = modifier, spacing = 8.dp) {
EqualWidthColumn(spacing = 8.dp) {
threadDecoration()
inReplyTo(inReplyToDetails)
contentWithTimestamp()
@ -651,9 +660,7 @@ private fun MessageEventBubbleContent(
paddingBehaviour = paddingBehaviour,
inReplyToDetails = event.inReplyTo,
canShrinkContent = event.content is TimelineItemVoiceContent,
modifier = bubbleModifier.semantics(mergeDescendants = true) {
contentDescription = event.safeSenderName
}
modifier = bubbleModifier,
)
}

View file

@ -14,6 +14,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import io.element.android.features.messages.impl.R
import io.element.android.features.messages.impl.timeline.TimelineEvents
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
@ -25,6 +27,7 @@ import io.element.android.features.messages.impl.timeline.components.layout.Cont
import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState
import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState
@ -140,7 +143,16 @@ private fun TimelineItemGroupedEventsRowContent(
},
) {
Column(modifier = modifier.animateContentSize()) {
val groupedEventsTitle = pluralStringResource(
id = R.plurals.screen_room_timeline_state_changes,
count = timelineItem.events.size,
timelineItem.events.size
)
GroupHeaderView(
modifier = Modifier.clearAndSetSemantics {
val groupedEventsContent = timelineItem.events.reversed().joinToString(separator = "\n") { (it.content as TimelineItemStateContent).body }
contentDescription = groupedEventsTitle + groupedEventsContent
},
text = pluralStringResource(
id = R.plurals.screen_room_timeline_state_changes,
count = timelineItem.events.size,

View file

@ -16,6 +16,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import io.element.android.features.messages.impl.R
@ -40,7 +42,9 @@ fun TimelineItemReactionsView(
) {
var expanded: Boolean by rememberSaveable { mutableStateOf(false) }
TimelineItemReactionsView(
modifier = modifier,
modifier = modifier.semantics {
invisibleToUser()
},
reactions = reactionsState.reactions,
userCanSendReaction = userCanSendReaction,
expanded = expanded,

View file

@ -7,6 +7,8 @@
package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@ -17,6 +19,9 @@ import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
@ -28,6 +33,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
import io.element.android.libraries.designsystem.preview.ElementPreview
@ -37,8 +43,12 @@ import io.element.android.libraries.designsystem.theme.LocalBuildMeta
import io.element.android.libraries.designsystem.theme.highlightedMessageBackgroundColor
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.time.isTalkbackActive
import io.element.android.wysiwyg.link.Link
import kotlin.time.DurationUnit
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun TimelineItemRow(
timelineItem: TimelineItem,
@ -120,7 +130,28 @@ internal fun TimelineItemRow(
)
}
else -> {
val a11yVoiceMessage = stringResource(CommonStrings.a11y_voice_message)
TimelineItemEventRow(
modifier = Modifier
.semantics(mergeDescendants = true) {
contentDescription = if (timelineItem.content is TimelineItemVoiceContent) {
val voiceMessageText = String.format(a11yVoiceMessage, timelineItem.content.duration.toString(DurationUnit.MINUTES))
"${timelineItem.safeSenderName}, $voiceMessageText"
} else {
timelineItem.safeSenderName
}
}
// Custom clickable that applies over the whole item for accessibility
.then(
if (isTalkbackActive()) {
Modifier.combinedClickable(
onClick = { onContentClick(timelineItem) },
onLongClick = { onLongClick(timelineItem) }
)
} else {
Modifier
}
),
event = timelineItem,
timelineRoomInfo = timelineRoomInfo,
renderReadReceipts = renderReadReceipts,

View file

@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
@ -21,6 +22,7 @@ import io.element.android.libraries.designsystem.icons.CompoundDrawables
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun TimelineItemFileView(
@ -37,7 +39,7 @@ fun TimelineItemFileView(
icon = {
Icon(
resourceId = CompoundDrawables.ic_compound_attachment,
contentDescription = null,
contentDescription = stringResource(CommonStrings.common_file),
tint = ElementTheme.colors.iconPrimary,
modifier = Modifier
.size(16.dp)

View file

@ -33,8 +33,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
@ -55,6 +53,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.time.isTalkbackActive
import io.element.android.wysiwyg.compose.EditorStyledText
import io.element.android.wysiwyg.link.Link
@ -71,10 +70,9 @@ fun TimelineItemImageView(
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
modifier: Modifier = Modifier,
) {
val description = stringResource(CommonStrings.common_image)
Column(
modifier = modifier.semantics { contentDescription = description },
) {
val a11yLabel = stringResource(CommonStrings.common_image)
val description = content.caption?.let { "$a11yLabel: $it" } ?: a11yLabel
Column(modifier = modifier) {
val containerModifier = if (content.showCaption) {
Modifier.clip(RoundedCornerShape(10.dp))
} else {
@ -93,7 +91,16 @@ fun TimelineItemImageView(
modifier = Modifier
.fillMaxWidth()
.then(if (isLoaded) Modifier.background(Color.White) else Modifier)
.then(if (onContentClick != null) Modifier.combinedClickable(onClick = onContentClick, onLongClick = onLongClick) else Modifier),
.then(
if (!isTalkbackActive() && onContentClick != null) {
Modifier.combinedClickable(
onClick = onContentClick,
onLongClick = onLongClick
)
} else {
Modifier
}
),
model = content.thumbnailMediaRequestData,
contentScale = ContentScale.Fit,
alignment = Alignment.Center,

View file

@ -36,7 +36,7 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@ -63,6 +63,7 @@ import io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_WIDTH
import io.element.android.libraries.matrix.ui.media.MediaRequestData
import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.time.isTalkbackActive
import io.element.android.wysiwyg.compose.EditorStyledText
import io.element.android.wysiwyg.link.Link
@ -79,10 +80,10 @@ fun TimelineItemVideoView(
onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit,
modifier: Modifier = Modifier,
) {
val description = stringResource(CommonStrings.common_video)
Column(
modifier = modifier.semantics { contentDescription = description }
) {
val isTalkbackActive = isTalkbackActive()
val a11yLabel = stringResource(CommonStrings.common_video)
val description = content.caption?.let { "$a11yLabel: $it" } ?: a11yLabel
Column(modifier = modifier) {
val containerModifier = if (content.showCaption) {
Modifier
.padding(top = 6.dp)
@ -104,7 +105,16 @@ fun TimelineItemVideoView(
modifier = Modifier
.fillMaxWidth()
.then(if (isLoaded) Modifier.background(Color.White) else Modifier)
.then(if (onContentClick != null) Modifier.combinedClickable(onClick = onContentClick, onLongClick = onLongClick) else Modifier),
.then(
if (!isTalkbackActive && onContentClick != null) {
Modifier.combinedClickable(
onClick = onContentClick,
onLongClick = onLongClick
)
} else {
Modifier
}
),
model = MediaRequestData(
source = content.thumbnailSource,
kind = MediaRequestData.Kind.Thumbnail(
@ -126,6 +136,7 @@ fun TimelineItemVideoView(
imageVector = CompoundIcons.PlaySolid(),
contentDescription = stringResource(id = CommonStrings.a11y_play),
colorFilter = ColorFilter.tint(Color.White),
modifier = Modifier.semantics { invisibleToUser() }
)
}
}

View file

@ -27,10 +27,11 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.disabled
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
@ -40,7 +41,6 @@ import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContentProvider
import io.element.android.libraries.androidutils.accessibility.isScreenReaderEnabled
import io.element.android.libraries.designsystem.components.media.WaveformPlaybackView
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -49,6 +49,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.time.isTalkbackActive
import io.element.android.libraries.voiceplayer.api.VoiceMessageEvents
import io.element.android.libraries.voiceplayer.api.VoiceMessageState
import io.element.android.libraries.voiceplayer.api.VoiceMessageStateProvider
@ -66,10 +67,27 @@ fun TimelineItemVoiceView(
}
val a11y = stringResource(CommonStrings.common_voice_message)
val a11yActionLabel = stringResource(
when (state.button) {
VoiceMessageState.Button.Play -> CommonStrings.a11y_play
VoiceMessageState.Button.Pause -> CommonStrings.a11y_pause
VoiceMessageState.Button.Downloading -> CommonStrings.common_downloading
VoiceMessageState.Button.Retry -> CommonStrings.action_retry
VoiceMessageState.Button.Disabled -> CommonStrings.error_unknown
}
)
Row(
modifier = modifier
.semantics {
.clearAndSetSemantics {
contentDescription = a11y
if (state.button == VoiceMessageState.Button.Disabled) {
disabled()
} else if (state.button in listOf(VoiceMessageState.Button.Play, VoiceMessageState.Button.Pause)) {
onClick(label = a11yActionLabel) {
playPause()
true
}
}
}
.onSizeChanged {
onContentLayoutChange(
@ -81,12 +99,14 @@ fun TimelineItemVoiceView(
},
verticalAlignment = Alignment.CenterVertically,
) {
when (state.button) {
VoiceMessageState.Button.Play -> PlayButton(onClick = ::playPause)
VoiceMessageState.Button.Pause -> PauseButton(onClick = ::playPause)
VoiceMessageState.Button.Downloading -> ProgressButton()
VoiceMessageState.Button.Retry -> RetryButton(onClick = ::playPause)
VoiceMessageState.Button.Disabled -> PlayButton(onClick = {}, enabled = false)
if (!isTalkbackActive()) {
when (state.button) {
VoiceMessageState.Button.Play -> PlayButton(onClick = ::playPause)
VoiceMessageState.Button.Pause -> PauseButton(onClick = ::playPause)
VoiceMessageState.Button.Downloading -> ProgressButton()
VoiceMessageState.Button.Retry -> RetryButton(onClick = ::playPause)
VoiceMessageState.Button.Disabled -> PlayButton(onClick = {}, enabled = false)
}
}
Spacer(Modifier.width(8.dp))
Text(
@ -97,13 +117,12 @@ fun TimelineItemVoiceView(
overflow = TextOverflow.Ellipsis,
)
Spacer(Modifier.width(8.dp))
val context = LocalContext.current
WaveformPlaybackView(
showCursor = state.showCursor,
playbackProgress = state.progress,
waveform = content.waveform,
modifier = Modifier.height(34.dp),
seekEnabled = !context.isScreenReaderEnabled(),
seekEnabled = !isTalkbackActive(),
onSeek = { state.eventSink(VoiceMessageEvents.Seek(it)) },
)
}

View file

@ -26,6 +26,7 @@ import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.invisibleToUser
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
@ -56,7 +57,11 @@ fun TimelineItemReadReceiptView(
) {
if (state.receipts.isNotEmpty()) {
if (renderReadReceipts) {
ReadReceiptsRow(modifier = modifier) {
ReadReceiptsRow(
modifier = modifier.clearAndSetSemantics {
invisibleToUser()
}
) {
ReadReceiptsAvatars(
receipts = state.receipts,
modifier = Modifier

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="emoji_picker_category_activity">"Gweithgareddau"</string>
<string name="emoji_picker_category_flags">"Baneri"</string>
<string name="emoji_picker_category_foods">"Bwyd a Diod"</string>
<string name="emoji_picker_category_nature">"Anifeiliaid a Natur"</string>
<string name="emoji_picker_category_objects">"Gwrthrychau"</string>
<string name="emoji_picker_category_people">"Wynebau Hapus a Phobl"</string>
<string name="emoji_picker_category_places">"Teithio a Llefydd"</string>
<string name="emoji_picker_category_symbols">"Symbolau"</string>
<string name="screen_report_content_block_user">"Rhwystro defnyddiwr"</string>
<string name="screen_room_attachment_source_camera">"Camera"</string>
<string name="screen_room_attachment_source_camera_photo">"Cymryd llun"</string>
<string name="screen_room_attachment_source_files">"Atodiad"</string>
<string name="screen_room_attachment_source_location">"Lleoliad"</string>
<string name="screen_room_attachment_source_poll">"Pôl"</string>
<string name="screen_room_mentions_at_room_subtitle">"Rhowch wybod i\'r ystafell gyfan"</string>
<string name="screen_room_mentions_at_room_title">"Pawb"</string>
<string name="screen_room_timeline_less_reactions">"Dangos llai"</string>
<string name="screen_room_timeline_reactions_show_less">"Dangos llai"</string>
<string name="screen_room_timeline_reactions_show_more">"Dangos rhagor"</string>
<string name="screen_room_timeline_read_marker_title">"Newydd"</string>
<string name="screen_room_typing_two_members">"%1$s a %2$s"</string>
</resources>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="emoji_picker_category_activity">"Veikla"</string>
<string name="emoji_picker_category_flags">"Vėliavos"</string>
<string name="emoji_picker_category_foods">"Maistas ir Gėrimai"</string>
<string name="emoji_picker_category_nature">"Gyvūnai ir Gamta"</string>
<string name="emoji_picker_category_objects">"Objektai"</string>
<string name="emoji_picker_category_people">"Šypsenėlės ir Žmonės"</string>
<string name="emoji_picker_category_places">"Kelionės ir Vietovės"</string>
<string name="emoji_picker_category_symbols">"Simboliai"</string>
<string name="screen_report_content_block_user">"Blokuoti vartotoją"</string>
<string name="screen_report_content_block_user_hint">"Pažymėkite, jei norite paslėpti visas esamas ir būsimas šio vartotojo žinutes"</string>
<string name="screen_report_content_explanation">"Apie šią žinutę bus pranešta Jūsų serverio administracijai. Jie negalės perskaityti jokių užšifruotų žinučių."</string>
<string name="screen_report_content_hint">"Skundo dėl šio turinio priežastis"</string>
<string name="screen_room_attachment_source_camera">"Kamera"</string>
<string name="screen_room_attachment_source_camera_photo">"Fotografuoti"</string>
<string name="screen_room_attachment_source_camera_video">"Įrašyti vaizdo įrašą"</string>
<string name="screen_room_attachment_source_files">"Priedas"</string>
<string name="screen_room_attachment_source_gallery">"Nuotraukų ir vaizdo įrašų biblioteka"</string>
<string name="screen_room_timeline_beginning_of_room">"Tai yra %1$s pradžia."</string>
<string name="screen_room_timeline_beginning_of_room_no_name">"Tai yra šio pokalbio pradžia."</string>
<string name="screen_room_timeline_read_marker_title">"Naujų"</string>
<plurals name="screen_room_timeline_state_changes">
<item quantity="one">"%1$d kambario pakeitimas"</item>
<item quantity="few">"%1$d kambario pakeitimai"</item>
<item quantity="other">"%1$d kambario pakeitimų"</item>
</plurals>
</resources>

View file

@ -21,6 +21,7 @@
<string name="screen_room_attachment_source_poll">"Enquete"</string>
<string name="screen_room_attachment_text_formatting">"Formatação de texto"</string>
<string name="screen_room_encrypted_history_banner">"O histórico de mensagens não está disponível no momento."</string>
<string name="screen_room_encrypted_history_banner_unverified">"O histórico de mensagens não está disponível nesta sala. Verifique este dispositivo para ver seu histórico de mensagens."</string>
<string name="screen_room_invite_again_alert_message">"Gostaria de convidá-los de volta?"</string>
<string name="screen_room_invite_again_alert_title">"Você está sozinho neste chat"</string>
<string name="screen_room_mentions_at_room_subtitle">"Notificar a sala inteira"</string>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_onboarding_sign_up">"Creu cyfrif"</string>
</resources>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_onboarding_sign_in_manually">"Prisijunkite rankiniu būdu"</string>
<string name="screen_onboarding_sign_in_with_qr_code">"Prisijunkite naudodami QR kodą"</string>
<string name="screen_onboarding_sign_up">"Sukurti paskyrą"</string>
<string name="screen_onboarding_welcome_subtitle">"Sveiki atvykę į %1$s. Įkrautas greitumui ir paprastumui."</string>
<string name="screen_onboarding_welcome_title">"Būkite savo elemente"</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_create_poll_add_option_btn">"Ychwanegu dewis"</string>
<string name="screen_create_poll_cancel_confirmation_content_android">"Dyw eich newidiadau heb gael eu cadw. Ydych chi\'n siŵr eich bod am fynd nôl?"</string>
<string name="screen_create_poll_title">"Creu Pôl"</string>
<string name="screen_edit_poll_delete_confirmation_title">"Dileu Pôl"</string>
<string name="screen_edit_poll_title">"Golygu pôl"</string>
<string name="screen_polls_history_filter_ongoing">"Parhaus"</string>
<string name="screen_polls_history_title">"Polau"</string>
</resources>

View file

@ -12,8 +12,8 @@
<string name="screen_edit_poll_delete_confirmation_title">"Umfrage löschen"</string>
<string name="screen_edit_poll_title">"Umfrage bearbeiten"</string>
<string name="screen_polls_history_empty_ongoing">"Keine laufenden Umfragen vorhanden."</string>
<string name="screen_polls_history_empty_past">"Keine vergangenen Umfragen vorhanden."</string>
<string name="screen_polls_history_empty_past">"Keine beendeten Umfragen vorhanden."</string>
<string name="screen_polls_history_filter_ongoing">"Aktuell"</string>
<string name="screen_polls_history_filter_past">"Vergangene"</string>
<string name="screen_polls_history_filter_past">"Vergangenheit"</string>
<string name="screen_polls_history_title">"Umfragen"</string>
</resources>

View file

@ -14,5 +14,6 @@
<string name="screen_polls_history_empty_ongoing">"Finner ingen pågående avstemninger."</string>
<string name="screen_polls_history_empty_past">"Kan ikke finne noen tidligere avstemninger."</string>
<string name="screen_polls_history_filter_ongoing">"Pågående"</string>
<string name="screen_polls_history_filter_past">"Fortid"</string>
<string name="screen_polls_history_title">"Avstemninger"</string>
</resources>

View file

@ -7,16 +7,13 @@
package io.element.android.features.preferences.impl.advanced
import io.element.android.compound.theme.Theme
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
sealed interface AdvancedSettingsEvents {
data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents
data class SetSharePresenceEnabled(val enabled: Boolean) : AdvancedSettingsEvents
data class SetCompressMedia(val compress: Boolean) : AdvancedSettingsEvents
data object ChangeTheme : AdvancedSettingsEvents
data object CancelChangeTheme : AdvancedSettingsEvents
data class SetTheme(val theme: Theme) : AdvancedSettingsEvents
data class SetTheme(val theme: ThemeOption) : AdvancedSettingsEvents
data class SetTimelineMediaPreviewValue(val value: MediaPreviewValue) : AdvancedSettingsEvents
data class SetHideInviteAvatars(val value: Boolean) : AdvancedSettingsEvents
}

View file

@ -9,11 +9,10 @@ package io.element.android.features.preferences.impl.advanced
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import io.element.android.compound.theme.Theme
import io.element.android.compound.theme.mapToTheme
import io.element.android.libraries.architecture.Presenter
@ -39,11 +38,9 @@ class AdvancedSettingsPresenter @Inject constructor(
val doesCompressMedia by remember {
sessionPreferencesStore.doesCompressMedia()
}.collectAsState(initial = true)
val theme by remember {
val theme = remember {
appPreferencesStore.getThemeFlow().mapToTheme()
}.collectAsState(initial = Theme.System)
var showChangeThemeDialog by remember { mutableStateOf(false) }
val hideInviteAvatars by remember {
appPreferencesStore.getHideInviteAvatarsFlow()
}.collectAsState(false)
@ -52,6 +49,16 @@ class AdvancedSettingsPresenter @Inject constructor(
appPreferencesStore.getTimelineMediaPreviewValueFlow()
}.collectAsState(initial = MediaPreviewValue.On)
val themeOption by remember {
derivedStateOf {
when (theme.value) {
Theme.System -> ThemeOption.System
Theme.Dark -> ThemeOption.Dark
Theme.Light -> ThemeOption.Light
}
}
}
fun handleEvents(event: AdvancedSettingsEvents) {
when (event) {
is AdvancedSettingsEvents.SetDeveloperModeEnabled -> localCoroutineScope.launch {
@ -63,11 +70,12 @@ class AdvancedSettingsPresenter @Inject constructor(
is AdvancedSettingsEvents.SetCompressMedia -> localCoroutineScope.launch {
sessionPreferencesStore.setCompressMedia(event.compress)
}
AdvancedSettingsEvents.CancelChangeTheme -> showChangeThemeDialog = false
AdvancedSettingsEvents.ChangeTheme -> showChangeThemeDialog = true
is AdvancedSettingsEvents.SetTheme -> localCoroutineScope.launch {
appPreferencesStore.setTheme(event.theme.name)
showChangeThemeDialog = false
when (event.theme) {
ThemeOption.System -> appPreferencesStore.setTheme(Theme.System.name)
ThemeOption.Dark -> appPreferencesStore.setTheme(Theme.Dark.name)
ThemeOption.Light -> appPreferencesStore.setTheme(Theme.Light.name)
}
}
is AdvancedSettingsEvents.SetHideInviteAvatars -> localCoroutineScope.launch {
appPreferencesStore.setHideInviteAvatars(event.value)
@ -82,8 +90,7 @@ class AdvancedSettingsPresenter @Inject constructor(
isDeveloperModeEnabled = isDeveloperModeEnabled,
isSharePresenceEnabled = isSharePresenceEnabled,
doesCompressMedia = doesCompressMedia,
theme = theme,
showChangeThemeDialog = showChangeThemeDialog,
theme = themeOption,
hideInviteAvatars = hideInviteAvatars,
timelineMediaPreviewValue = timelineMediaPreviewValue,
eventSink = { handleEvents(it) }

View file

@ -7,16 +7,33 @@
package io.element.android.features.preferences.impl.advanced
import io.element.android.compound.theme.Theme
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import io.element.android.libraries.designsystem.components.preferences.DropdownOption
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
import io.element.android.libraries.ui.strings.CommonStrings
data class AdvancedSettingsState(
val isDeveloperModeEnabled: Boolean,
val isSharePresenceEnabled: Boolean,
val doesCompressMedia: Boolean,
val theme: Theme,
val showChangeThemeDialog: Boolean,
val theme: ThemeOption,
val hideInviteAvatars: Boolean,
val timelineMediaPreviewValue: MediaPreviewValue,
val eventSink: (AdvancedSettingsEvents) -> Unit
)
enum class ThemeOption : DropdownOption {
System {
@Composable
override fun getText(): String = stringResource(CommonStrings.common_system)
},
Dark {
@Composable
override fun getText(): String = stringResource(CommonStrings.common_dark)
},
Light {
@Composable
override fun getText(): String = stringResource(CommonStrings.common_light)
}
}

View file

@ -8,7 +8,6 @@
package io.element.android.features.preferences.impl.advanced
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.compound.theme.Theme
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
open class AdvancedSettingsStateProvider : PreviewParameterProvider<AdvancedSettingsState> {
@ -16,7 +15,6 @@ open class AdvancedSettingsStateProvider : PreviewParameterProvider<AdvancedSett
get() = sequenceOf(
aAdvancedSettingsState(),
aAdvancedSettingsState(isDeveloperModeEnabled = true),
aAdvancedSettingsState(showChangeThemeDialog = true),
aAdvancedSettingsState(isSharePresenceEnabled = true),
aAdvancedSettingsState(doesCompressMedia = true),
aAdvancedSettingsState(hideInviteAvatars = true),
@ -28,16 +26,15 @@ fun aAdvancedSettingsState(
isDeveloperModeEnabled: Boolean = false,
isSharePresenceEnabled: Boolean = false,
doesCompressMedia: Boolean = false,
showChangeThemeDialog: Boolean = false,
hideInviteAvatars: Boolean = false,
theme: ThemeOption = ThemeOption.System,
timelineMediaPreviewValue: MediaPreviewValue = MediaPreviewValue.On,
eventSink: (AdvancedSettingsEvents) -> Unit = {},
) = AdvancedSettingsState(
isDeveloperModeEnabled = isDeveloperModeEnabled,
isSharePresenceEnabled = isSharePresenceEnabled,
doesCompressMedia = doesCompressMedia,
theme = Theme.System,
showChangeThemeDialog = showChangeThemeDialog,
theme = theme,
hideInviteAvatars = hideInviteAvatars,
timelineMediaPreviewValue = timelineMediaPreviewValue,
eventSink = eventSink

View file

@ -12,14 +12,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import im.vector.app.features.analytics.plan.Interaction
import io.element.android.compound.theme.Theme
import io.element.android.compound.theme.themes
import io.element.android.features.preferences.impl.R
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
import io.element.android.libraries.designsystem.components.dialogs.ListOption
import io.element.android.libraries.designsystem.components.dialogs.SingleSelectionDialog
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceDropdown
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
@ -34,8 +31,7 @@ import io.element.android.libraries.matrix.api.media.MediaPreviewValue
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.services.analytics.compose.LocalAnalyticsService
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
@Composable
fun AdvancedSettingsView(
@ -49,15 +45,12 @@ fun AdvancedSettingsView(
onBackClick = onBackClick,
title = stringResource(id = CommonStrings.common_advanced_settings)
) {
ListItem(
headlineContent = {
Text(text = stringResource(id = CommonStrings.common_appearance))
},
trailingContent = ListItemContent.Text(
state.theme.toHumanReadable()
),
onClick = {
state.eventSink(AdvancedSettingsEvents.ChangeTheme)
PreferenceDropdown(
title = stringResource(id = CommonStrings.common_appearance),
selectedOption = state.theme,
options = ThemeOption.entries.toPersistentList(),
onSelectOption = { logLevel ->
state.eventSink(AdvancedSettingsEvents.SetTheme(logLevel))
}
)
ListItem(
@ -108,21 +101,6 @@ fun AdvancedSettingsView(
)
ModerationAndSafety(state)
}
if (state.showChangeThemeDialog) {
SingleSelectionDialog(
options = getOptions(),
initialSelection = themes.indexOf(state.theme),
onSelectOption = {
state.eventSink(
AdvancedSettingsEvents.SetTheme(
themes[it]
)
)
},
onDismissRequest = { state.eventSink(AdvancedSettingsEvents.CancelChangeTheme) },
)
}
}
@Composable
@ -176,24 +154,6 @@ private fun ModerationAndSafety(
}
}
@Composable
private fun getOptions(): ImmutableList<ListOption> {
return themes.map {
ListOption(title = it.toHumanReadable())
}.toImmutableList()
}
@Composable
private fun Theme.toHumanReadable(): String {
return stringResource(
id = when (this) {
Theme.System -> CommonStrings.common_system
Theme.Dark -> CommonStrings.common_dark
Theme.Light -> CommonStrings.common_light
}
)
}
@PreviewWithLargeHeight
@Composable
internal fun AdvancedSettingsViewLightPreview(@PreviewParameter(AdvancedSettingsStateProvider::class) state: AdvancedSettingsState) =

View file

@ -7,22 +7,28 @@
package io.element.android.features.preferences.impl.developer.tracing
import androidx.compose.runtime.Composable
import io.element.android.libraries.designsystem.components.preferences.DropdownOption
enum class LogLevelItem : DropdownOption {
ERROR {
override val text: String = "Error"
@Composable
override fun getText(): String = "Error"
},
WARN {
override val text: String = "Warn"
@Composable
override fun getText(): String = "Warn"
},
INFO {
override val text: String = "Info"
@Composable
override fun getText(): String = "Info"
},
DEBUG {
override val text: String = "Debug"
@Composable
override fun getText(): String = "Debug"
},
TRACE {
override val text: String = "Trace"
@Composable
override fun getText(): String = "Trace"
}
}

View file

@ -58,7 +58,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
@Composable
override fun present(): EditUserProfileState {
val cameraPermissionState = cameraPermissionPresenter.present()
var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.let { Uri.parse(it) }) }
var userAvatarUri by rememberSaveable { mutableStateOf(matrixUser.avatarUrl?.toUri()) }
var userDisplayName by rememberSaveable { mutableStateOf(matrixUser.displayName) }
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(
onResult = { uri ->

View file

@ -19,6 +19,11 @@
<string name="screen_advanced_settings_send_read_receipts_description">"Pokud je vypnuto, potvrzení o přečtení se nikomu neodesílají. Stále budete dostávat potvrzení o přečtení od ostatních uživatelů."</string>
<string name="screen_advanced_settings_share_presence">"Sdílejte přítomnost"</string>
<string name="screen_advanced_settings_share_presence_description">"Pokud je tato funkce vypnutá, nebudete moci odesílat ani přijímat potvrzení o přečtení ani upozornění o psaní."</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Vždy skrýt"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Vždy zobrazit"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"V soukromých místnostech"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"Skryté médium lze vždy zobrazit klepnutím na něj"</string>
<string name="screen_advanced_settings_show_media_timeline_title">"Zobrazit média na časové ose"</string>
<string name="screen_advanced_settings_view_source_description">"Povolit možnost zobrazení zdroje zprávy na časové ose."</string>
<string name="screen_blocked_users_empty">"Nemáte žádné blokované uživatele"</string>
<string name="screen_blocked_users_unblock_alert_action">"Odblokovat"</string>
@ -60,6 +65,7 @@ Pokud budete pokračovat, některá nastavení se mohou změnit."</string>
<string name="screen_notification_settings_system_notifications_action_required_content_link">"systémová nastavení"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Systémová oznámení byla vypnuta"</string>
<string name="screen_notification_settings_title">"Oznámení"</string>
<string name="troubleshoot_notifications_entry_point_push_history_title">"Historie push oznámení"</string>
<string name="troubleshoot_notifications_entry_point_section">"Odstraňování problémů"</string>
<string name="troubleshoot_notifications_entry_point_title">"Odstraňování problémů s upozorněními"</string>
</resources>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="full_screen_intent_banner_message">"Er mwyn sicrhau fyddwch chi ddim yn colli galwad bwysig, newidiwch eich gosodiadau i ganiatáu hysbysiadau sgrin lawn pan fydd eich ffôn wedi\'i gloi."</string>
<string name="full_screen_intent_banner_title">"Gwella profiad eich galwadau"</string>
<string name="screen_advanced_settings_element_call_base_url">"URL sylfaen Galwad Element Cyfaddas"</string>
<string name="screen_advanced_settings_element_call_base_url_description">"Gosod URL sylfaen cyfaddas ar gyfer Galwad Element."</string>
<string name="screen_advanced_settings_element_call_base_url_validation_error">"URL annilys, gwnewch yn siŵr eich bod yn cynnwys y protocol (http/https) a\'r cyfeiriad cywir."</string>
<string name="screen_advanced_settings_hide_invite_avatars_toggle_title">"Cuddio afatarau yn y ceisiadau gwahoddiad i ystafell"</string>
<string name="screen_advanced_settings_hide_timeline_media_toggle_title">"Cuddio rhagolygon cyfryngau yn y llinell amser"</string>
<string name="screen_advanced_settings_moderation_and_safety_section_title">"Cymedroli a Diogelwch"</string>
<string name="screen_advanced_settings_send_read_receipts">"Derbynebau darllen"</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Cuddio bob tro"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Dangos bob tro"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"Mewn ystafelloedd preifat"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"Mae modd dangos cyfrwng cudd trwy dapio arno"</string>
<string name="screen_advanced_settings_show_media_timeline_title">"Dangos cyfryngau mewn llinell amser"</string>
<string name="screen_blocked_users_unblock_alert_action">"Dad-rwystro"</string>
<string name="screen_edit_profile_display_name">"Enw dangos"</string>
<string name="screen_edit_profile_title">"Golygu proffil"</string>
<string name="screen_notification_settings_additional_settings_section_title">"Gosodiadau ychwanegol"</string>
<string name="screen_notification_settings_mentions_section_title">"Crybwyll"</string>
<string name="screen_notification_settings_mode_all">"Y Cyfan"</string>
<string name="screen_notification_settings_mode_mentions">"Crybwyll"</string>
<string name="screen_notification_settings_title">"Hysbysiadau"</string>
</resources>

View file

@ -19,6 +19,11 @@
<string name="screen_advanced_settings_send_read_receipts_description">"Wenn diese Option deaktiviert ist, werden Ihre Lesebestätigungen an niemanden gesendet. Du erhältst weiterhin Lesebestätigungen von anderen Benutzern."</string>
<string name="screen_advanced_settings_share_presence">"Präsenz teilen"</string>
<string name="screen_advanced_settings_share_presence_description">"Wenn diese Option deaktiviert ist, kannst du keine Lesebestätigungen oder Tipp-Benachrichtigungen senden oder empfangen."</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Immer verstecken"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Immer anzeigen"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"In privaten Chatrooms"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"Ein verstecktes Medium kann jederzeit durch Antippen angezeigt werden"</string>
<string name="screen_advanced_settings_show_media_timeline_title">"Medien in der Zeitleiste anzeigen"</string>
<string name="screen_advanced_settings_view_source_description">"Option aktiveren, um Nachrichtenquelle in der Zeitleiste anzuzeigen."</string>
<string name="screen_blocked_users_empty">"Du hast keine blockierten Nutzer"</string>
<string name="screen_blocked_users_unblock_alert_action">"Blockierung aufheben"</string>
@ -58,6 +63,7 @@ Wenn du fortfährst, können sich einige deiner Einstellungen ändern."</string>
<string name="screen_notification_settings_system_notifications_action_required_content_link">"Systemeinstellungen"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Systembenachrichtigungen deaktiviert"</string>
<string name="screen_notification_settings_title">"Benachrichtigungen"</string>
<string name="troubleshoot_notifications_entry_point_push_history_title">"Verlauf pushen"</string>
<string name="troubleshoot_notifications_entry_point_section">"Fehlerbehebung"</string>
<string name="troubleshoot_notifications_entry_point_title">"Fehlerbehebung für Benachrichtigungen"</string>
</resources>

View file

@ -19,6 +19,11 @@
<string name="screen_advanced_settings_send_read_receipts_description">"Kui lülitad selle valiku välja, siis mitte keegi enam ei saa sinult lugemisteatisi. Küll aga saad sina teiste kasutajate lugemisteatisi."</string>
<string name="screen_advanced_settings_share_presence">"Jaga oma olekut"</string>
<string name="screen_advanced_settings_share_presence_description">"Kui see eelistus on välja lülitatud, siis sa ei saa ega saada ei lugemisteatisi ega kirjutamise teavitusi."</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Peida alati"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Näita alati"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"Privaatsetes jututubades"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"Peidetud meediumi saad alati näha temal klõpsides"</string>
<string name="screen_advanced_settings_show_media_timeline_title">"Näita ajajoonel meediat"</string>
<string name="screen_advanced_settings_view_source_description">"Selle eelistuse sisselülitamisel on võimalik ajajoonel vaadata sõnumite lähtekoodi."</string>
<string name="screen_blocked_users_empty">"Sa pole ühtegi kasutajat blokeerinud"</string>
<string name="screen_blocked_users_unblock_alert_action">"Eemalda blokeering"</string>
@ -58,6 +63,7 @@ Kui sa jätkad muutmist, siis võivad muutuda ka need peidetud eelistused."</str
<string name="screen_notification_settings_system_notifications_action_required_content_link">"süsteemi seadistusi"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Süsteemi teavitused on välja lülitatud"</string>
<string name="screen_notification_settings_title">"Teavitused"</string>
<string name="troubleshoot_notifications_entry_point_push_history_title">"Tõuketeadete ajalugu"</string>
<string name="troubleshoot_notifications_entry_point_section">"Veaotsing"</string>
<string name="troubleshoot_notifications_entry_point_title">"Teavituste veaotsing"</string>
</resources>

View file

@ -8,14 +8,22 @@
<string name="screen_advanced_settings_element_call_base_url">"Mukautettu Element Call URL-osoite"</string>
<string name="screen_advanced_settings_element_call_base_url_description">"Aseta mukautettu URL-osoite Element Callille."</string>
<string name="screen_advanced_settings_element_call_base_url_validation_error">"Virheellinen URL-osoite. Varmista, että sisällytät protokollan (http/https) ja oikean osoitteen."</string>
<string name="screen_advanced_settings_hide_invite_avatars_toggle_title">"Piilota huoneiden avatarit kutsuista"</string>
<string name="screen_advanced_settings_hide_timeline_media_toggle_title">"Piilota median esikatselu aikajanalla"</string>
<string name="screen_advanced_settings_media_compression_description">"Lähetä valokuvia ja videoita nopeammin ja vähennä datan käyttöä."</string>
<string name="screen_advanced_settings_media_compression_title">"Optimoi median laatu"</string>
<string name="screen_advanced_settings_moderation_and_safety_section_title">"Moderointi ja Turvallisuus"</string>
<string name="screen_advanced_settings_push_provider_android">"Push-ilmoitusten tarjoaja"</string>
<string name="screen_advanced_settings_rich_text_editor_description">"Ota rikastettu tekstieditori pois käytöstä, jotta voit kirjoittaa Markdownia manuaalisesti."</string>
<string name="screen_advanced_settings_send_read_receipts">"Lukukuittaukset"</string>
<string name="screen_advanced_settings_send_read_receipts_description">"Jos tämä on poissa päältä, sinun lukukuittauksia ei lähetetä kenellekään. Vastaanotat silti lukukuittauksia muilta käyttäjiltä."</string>
<string name="screen_advanced_settings_share_presence">"Jaa läsnäolo"</string>
<string name="screen_advanced_settings_share_presence_description">"Jos tämä on poissa päältä, et lähetä tai vastaanota lukukuittauksia tai kirjoitusilmotuksia."</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Piilota aina"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Näytä aina"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"Yksityisissä huoneissa"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"Piilotetun median voi aina näyttää napauttamalla sitä"</string>
<string name="screen_advanced_settings_show_media_timeline_title">"Näytä media aikajanalla"</string>
<string name="screen_advanced_settings_view_source_description">"Ota käyttöön mahdollisuus tarkastella viestin lähdettä aikajanalla."</string>
<string name="screen_blocked_users_empty">"Et ole estänyt ketään"</string>
<string name="screen_blocked_users_unblock_alert_action">"Poista esto"</string>
@ -55,6 +63,7 @@ Jos jatkat, jotkin asetukset saattavat muuttua."</string>
<string name="screen_notification_settings_system_notifications_action_required_content_link">"järjestelmäsi asetuksia"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Järjestelmän ilmoitukset on poissa päältä"</string>
<string name="screen_notification_settings_title">"Ilmoitukset"</string>
<string name="troubleshoot_notifications_entry_point_push_history_title">"Push-historia"</string>
<string name="troubleshoot_notifications_entry_point_section">"Vianmääritys"</string>
<string name="troubleshoot_notifications_entry_point_title">"Ilmoitusten vianmääritys"</string>
</resources>

View file

@ -19,6 +19,11 @@
<string name="screen_advanced_settings_send_read_receipts_description">"En cas de désactivation, vos accusés de lecture ne seront pas envoyés aux autres membres. Vous verrez toujours les accusés des autres membres."</string>
<string name="screen_advanced_settings_share_presence">"Partager la présence"</string>
<string name="screen_advanced_settings_share_presence_description">"Si cette option est désactivée, vous ne pourrez ni envoyer ni recevoir de confirmations de lecture ni de notifications de saisie."</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Toujours cacher"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Toujours montrer"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"Dans les salons privés"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"Un média caché peut toujours être affiché en cliquant dessus"</string>
<string name="screen_advanced_settings_show_media_timeline_title">"Afficher les médias dans les discussions."</string>
<string name="screen_advanced_settings_view_source_description">"Activer cette option pour pouvoir voir la source des messages dans la discussion."</string>
<string name="screen_blocked_users_empty">"Vous navez bloqué personne"</string>
<string name="screen_blocked_users_unblock_alert_action">"Débloquer"</string>
@ -58,6 +63,7 @@ Si vous continuez, il est possible que certains de vos paramètres soient modifi
<string name="screen_notification_settings_system_notifications_action_required_content_link">"paramètres du système"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Les notifications du système sont désactivées"</string>
<string name="screen_notification_settings_title">"Notifications"</string>
<string name="troubleshoot_notifications_entry_point_push_history_title">"Historique des Push"</string>
<string name="troubleshoot_notifications_entry_point_section">"Dépannage"</string>
<string name="troubleshoot_notifications_entry_point_title">"Dépanner les notifications"</string>
</resources>

View file

@ -19,6 +19,11 @@
<string name="screen_advanced_settings_send_read_receipts_description">"Ha ki van kapcsolva, az olvasási visszaigazolások nem lesznek elküldve senkinek. A többi felhasználó olvasási visszaigazolását továbbra is meg fogja kapni."</string>
<string name="screen_advanced_settings_share_presence">"Jelenlét megosztása"</string>
<string name="screen_advanced_settings_share_presence_description">"Ha ki van kapcsolva, nem tud olvasási visszaigazolást vagy írási értesítést küldeni és fogadni"</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Elrejtés mindig"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Megjelenítés mindig"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"Privát szobákban"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"A rejtett médiatartalmak koppintással jeleníthetők meg"</string>
<string name="screen_advanced_settings_show_media_timeline_title">"Média megjelenítése az idővonalon"</string>
<string name="screen_advanced_settings_view_source_description">"Engedélyezze a beállítást az üzenet forrásának megjelenítéséhez az idővonalon."</string>
<string name="screen_blocked_users_empty">"Nincsenek letiltott felhasználók"</string>
<string name="screen_blocked_users_unblock_alert_action">"Letiltás feloldása"</string>
@ -58,6 +63,7 @@ Ha folytatja, egyes beállítások megváltozhatnak."</string>
<string name="screen_notification_settings_system_notifications_action_required_content_link">"rendszerbeállításokat"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"A rendszerértesítések ki vannak kapcsolva"</string>
<string name="screen_notification_settings_title">"Értesítések"</string>
<string name="troubleshoot_notifications_entry_point_push_history_title">"Leküldéses értesítés előzmények"</string>
<string name="troubleshoot_notifications_entry_point_section">"Hibaelhárítás"</string>
<string name="troubleshoot_notifications_entry_point_title">"Értesítések hibaelhárítása"</string>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Pasirinkite, kaip gauti pranešimus"</string>
<string name="screen_blocked_users_unblock_alert_action">"Atblokuoti"</string>
<string name="screen_blocked_users_unblock_alert_description">"Vėl galėsite matyti visas iš jų gautas žinutes."</string>
<string name="screen_blocked_users_unblock_alert_title">"Atblokuoti vartotoją"</string>
</resources>

View file

@ -5,14 +5,25 @@
<string name="screen_advanced_settings_choose_distributor_dialog_title_android">"Velg hvordan du vil motta varsler"</string>
<string name="screen_advanced_settings_developer_mode">"Utviklermodus"</string>
<string name="screen_advanced_settings_developer_mode_description">"Aktiver for å få tilgang til funksjoner og funksjonalitet for utviklere."</string>
<string name="screen_advanced_settings_element_call_base_url">"Egendefinert Element Call base URL"</string>
<string name="screen_advanced_settings_element_call_base_url_description">"Angi en egendefinert base URL for Element Call."</string>
<string name="screen_advanced_settings_element_call_base_url_validation_error">"Ugyldig URL. Pass på at du inkluderer protokollen (http/https) og riktig adresse."</string>
<string name="screen_advanced_settings_hide_invite_avatars_toggle_title">"Skjul avatarer i invitasjonsforespørsler til rom"</string>
<string name="screen_advanced_settings_hide_timeline_media_toggle_title">"Skjul forhåndsvisninger av medier på tidslinjen"</string>
<string name="screen_advanced_settings_media_compression_description">"Last opp bilder og videoer raskere og reduser databruken"</string>
<string name="screen_advanced_settings_media_compression_title">"Optimaliser mediekvaliteten"</string>
<string name="screen_advanced_settings_moderation_and_safety_section_title">"Moderasjon og sikkerhet"</string>
<string name="screen_advanced_settings_push_provider_android">"Leverandør av pushvarsling"</string>
<string name="screen_advanced_settings_rich_text_editor_description">"Deaktiver rik tekstredigering for å skrive Markdown manuelt."</string>
<string name="screen_advanced_settings_send_read_receipts">"Lesebekreftelser"</string>
<string name="screen_advanced_settings_send_read_receipts_description">"Hvis slått av, sendes ikke lesebekreftelsene dine til noen. Du vil fortsatt motta lesebekreftelser fra andre brukere."</string>
<string name="screen_advanced_settings_share_presence">"Del tilstedeværelse"</string>
<string name="screen_advanced_settings_share_presence_description">"Hvis slått av, kan du ikke sende eller motta lesebekreftelser eller skrivevarsler."</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Skjul alltid"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Vis alltid"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"I private rom"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"Et skjult medium kan alltid vises ved å trykke på det"</string>
<string name="screen_advanced_settings_show_media_timeline_title">"Vis medier i tidslinjen"</string>
<string name="screen_advanced_settings_view_source_description">"Aktiver alternativet for å vise meldingskilden på tidslinjen."</string>
<string name="screen_blocked_users_empty">"Du har ingen blokkerte brukere"</string>
<string name="screen_blocked_users_unblock_alert_action">"Fjern blokkering"</string>
@ -52,6 +63,7 @@ Hvis du fortsetter, kan noen av innstillingene dine endres."</string>
<string name="screen_notification_settings_system_notifications_action_required_content_link">"systeminnstillinger"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Systemvarsler er slått av"</string>
<string name="screen_notification_settings_title">"Varslinger"</string>
<string name="troubleshoot_notifications_entry_point_push_history_title">"Push-historikk"</string>
<string name="troubleshoot_notifications_entry_point_section">"Feilsøk"</string>
<string name="troubleshoot_notifications_entry_point_title">"Feilsøk varsler"</string>
</resources>

View file

@ -55,6 +55,7 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz."</string>
<string name="screen_notification_settings_system_notifications_action_required_content_link">"ustawienia systemowe"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Powiadomienia systemowe wyłączone"</string>
<string name="screen_notification_settings_title">"Powiadomienia"</string>
<string name="troubleshoot_notifications_entry_point_push_history_title">"Historia powiadomień Push"</string>
<string name="troubleshoot_notifications_entry_point_section">"Rozwiązywanie problemów"</string>
<string name="troubleshoot_notifications_entry_point_title">"Rozwiązywanie problemów powiadomień"</string>
</resources>

View file

@ -40,6 +40,7 @@ Se você continuar, algumas de suas configurações poderão mudar."</string>
<string name="screen_notification_settings_failed_fixing_configuration">"A configuração não foi corrigida, tente novamente."</string>
<string name="screen_notification_settings_group_chats">"Bate-papos em grupo"</string>
<string name="screen_notification_settings_invite_for_me_label">"Convites"</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Seu servidor doméstico não suporta esta opção em salas criptografadas. Você pode não ser notificado em algumas salas."</string>
<string name="screen_notification_settings_mentions_section_title">"Menções"</string>
<string name="screen_notification_settings_mode_all">"Todos"</string>
<string name="screen_notification_settings_mode_mentions">"Menções"</string>

View file

@ -8,14 +8,21 @@
<string name="screen_advanced_settings_element_call_base_url">"Базовый URL сервера звонков Element"</string>
<string name="screen_advanced_settings_element_call_base_url_description">"Задайте свой сервер Element Call."</string>
<string name="screen_advanced_settings_element_call_base_url_validation_error">"Адрес указан неверно, удостоверьтесь, что вы указали протокол (http/https) и правильный адрес."</string>
<string name="screen_advanced_settings_hide_invite_avatars_toggle_title">"Скрыть аватары в запросах на приглашение в комнату"</string>
<string name="screen_advanced_settings_hide_timeline_media_toggle_title">"Скрыть предварительный просмотр медиафайлов на временной шкале"</string>
<string name="screen_advanced_settings_media_compression_description">"Загружайте фотографии и видео быстрее и сокращайте потребление трафика"</string>
<string name="screen_advanced_settings_media_compression_title">"Оптимизировать качество мультимедиа"</string>
<string name="screen_advanced_settings_moderation_and_safety_section_title">"Модерация и безопасность"</string>
<string name="screen_advanced_settings_push_provider_android">"Поставщик push-уведомлений"</string>
<string name="screen_advanced_settings_rich_text_editor_description">"Отключить редактор форматированного текста и включить Markdown."</string>
<string name="screen_advanced_settings_send_read_receipts">"Уведомления о прочтении"</string>
<string name="screen_advanced_settings_send_read_receipts_description">"Если этот параметр выключен, ваш статус о прочтении не будет отображаться. Вы по-прежнему будете видеть статус о прочтении от других пользователей."</string>
<string name="screen_advanced_settings_share_presence">"Поделиться присутствием"</string>
<string name="screen_advanced_settings_share_presence_description">"Если выключено, вы не сможете отправлять, получать уведомления о прочтении и наборе текста"</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Всегда скрывать"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Всегда показывать"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"В личных комнатах"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"Скрытый медиафайл всегда можно отобразить, нажав на него."</string>
<string name="screen_advanced_settings_view_source_description">"Включить опцию просмотра источника сообщения в ленте."</string>
<string name="screen_blocked_users_empty">"У вас нет заблокированных пользователей"</string>
<string name="screen_blocked_users_unblock_alert_action">"Разблокировать"</string>

View file

@ -19,6 +19,11 @@
<string name="screen_advanced_settings_send_read_receipts_description">"Ak je táto funkcia vypnutá, vaše potvrdenia o prečítaní sa nebudú nikomu odosielať. Stále budete dostávať potvrdenia o prečítaní od ostatných používateľov."</string>
<string name="screen_advanced_settings_share_presence">"Zdieľať prítomnosť"</string>
<string name="screen_advanced_settings_share_presence_description">"Ak je vypnuté, nebudete môcť odosielať ani prijímať potvrdenia o prečítaní alebo upozornenia o písaní"</string>
<string name="screen_advanced_settings_show_media_timeline_always_hide">"Vždy skryť"</string>
<string name="screen_advanced_settings_show_media_timeline_always_show">"Vždy zobraziť"</string>
<string name="screen_advanced_settings_show_media_timeline_private_rooms">"V súkromných miestnostiach"</string>
<string name="screen_advanced_settings_show_media_timeline_subtitle">"Skryté médium sa dá vždy zobraziť ťuknutím naň"</string>
<string name="screen_advanced_settings_show_media_timeline_title">"Zobraziť médiá na časovej osi"</string>
<string name="screen_advanced_settings_view_source_description">"Povoliť možnosť zobrazenia zdroja správy na časovej osi."</string>
<string name="screen_blocked_users_empty">"Nemáte žiadnych blokovaných používateľov"</string>
<string name="screen_blocked_users_unblock_alert_action">"Odblokovať"</string>
@ -60,6 +65,7 @@ Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť."</stri
<string name="screen_notification_settings_system_notifications_action_required_content_link">"nastavenia systému"</string>
<string name="screen_notification_settings_system_notifications_turned_off">"Systémové oznámenia sú vypnuté"</string>
<string name="screen_notification_settings_title">"Oznámenia"</string>
<string name="troubleshoot_notifications_entry_point_push_history_title">"História push oznámení"</string>
<string name="troubleshoot_notifications_entry_point_section">"Riešenie problémov"</string>
<string name="troubleshoot_notifications_entry_point_title">"Oznámenia riešení problémov"</string>
</resources>

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