Merge branch 'develop' into feature/fga/user_moderation_bottomsheet
This commit is contained in:
commit
03e1926e14
250 changed files with 3339 additions and 2742 deletions
|
|
@ -53,7 +53,7 @@ fun Activity.openUrlInChromeCustomTab(
|
|||
// Disable download button
|
||||
intent.putExtra("org.chromium.chrome.browser.customtabs.EXTRA_DISABLE_DOWNLOAD_BUTTON", true)
|
||||
// Disable bookmark button
|
||||
intent.putExtra("org.chromium.chrome.browser.customtabs.EXTRA_DISABLE_START_BUTTON", true)
|
||||
intent.putExtra("org.chromium.chrome.browser.customtabs.EXTRA_DISABLE_STAR_BUTTON", true)
|
||||
intent.putExtra(Browser.EXTRA_HEADERS, Bundle().apply {
|
||||
putString("Accept-Language", Locale.getDefault().toLanguageTag())
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<string name="state_event_room_ban">"%1$s hat %2$s gesperrt"</string>
|
||||
<string name="state_event_room_ban_by_you">"Du hast %1$s gesperrt"</string>
|
||||
<string name="state_event_room_ban_by_you_with_reason">"Sie haben gesperrt %1$s: %2$s"</string>
|
||||
<string name="state_event_room_ban_with_reason">"%1$sverboten%2$s: %3$s"</string>
|
||||
<string name="state_event_room_ban_with_reason">"%1$s verboten %2$s: %3$s"</string>
|
||||
<string name="state_event_room_created">"%1$s hat den Raum erstellt"</string>
|
||||
<string name="state_event_room_created_by_you">"Du hast den Raum erstellt"</string>
|
||||
<string name="state_event_room_invite">"%1$s hat %2$s eingeladen"</string>
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
<string name="state_event_room_name_changed_by_you">"Du hast den Raum-Namen geändert in: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s hat den Raum-Namen entfernt"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Du hast den Raum-Namen entfernt"</string>
|
||||
<string name="state_event_room_none">"%1$shat keine Änderungen vorgenommen"</string>
|
||||
<string name="state_event_room_none">"%1$s hat keine Änderungen vorgenommen"</string>
|
||||
<string name="state_event_room_none_by_you">"Du hast keine Änderungen vorgenommen"</string>
|
||||
<string name="state_event_room_pinned_events_changed">"%1$s hat die fixierten Nachrichten geändert"</string>
|
||||
<string name="state_event_room_pinned_events_changed_by_you">"Du hast die fixierten Nachrichten geändert"</string>
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
<string name="state_event_room_remove">"%1$s hat %2$s entfernt"</string>
|
||||
<string name="state_event_room_remove_by_you">"Du hast %1$s entfernt"</string>
|
||||
<string name="state_event_room_remove_by_you_with_reason">"Sie haben %1$s entfernt: %2$s"</string>
|
||||
<string name="state_event_room_remove_with_reason">"%1$sentfernt%2$s: %3$s"</string>
|
||||
<string name="state_event_room_remove_with_reason">"%1$s entfernt %2$s: %3$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s hat eine Einladung an %2$s gesendet, dem Raum beizutreten"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Du hast eine Einladung an %1$s gesendet, dem Raum beizutreten"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s hat die Einladung an %2$s zum Betreten des Raums zurückgezogen"</string>
|
||||
|
|
|
|||
|
|
@ -147,13 +147,6 @@ enum class FeatureFlags(
|
|||
defaultValue = { true },
|
||||
isFinished = false,
|
||||
),
|
||||
EventCache(
|
||||
key = "feature.event_cache",
|
||||
title = "Use SDK Event cache",
|
||||
description = "Warning: you must kill and restart the app for the change to take effect.",
|
||||
defaultValue = { true },
|
||||
isFinished = false,
|
||||
),
|
||||
PrintLogsToLogcat(
|
||||
key = "feature.print_logs_to_logcat",
|
||||
title = "Print logs to logcat",
|
||||
|
|
|
|||
|
|
@ -162,6 +162,11 @@ interface MatrixClient {
|
|||
|
||||
fun canDeactivateAccount(): Boolean
|
||||
suspend fun deactivateAccount(password: String, eraseData: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Check if the user can report a room.
|
||||
*/
|
||||
suspend fun canReportRoom(): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -43,7 +43,10 @@ interface MatrixAuthenticationService {
|
|||
/**
|
||||
* Get the Oidc url to display to the user.
|
||||
*/
|
||||
suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails>
|
||||
suspend fun getOidcUrl(
|
||||
prompt: OidcPrompt,
|
||||
loginHint: String?,
|
||||
): Result<OidcDetails>
|
||||
|
||||
/**
|
||||
* Cancel Oidc login sequence.
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@
|
|||
|
||||
package io.element.android.libraries.matrix.api.auth.qrlogin
|
||||
|
||||
interface MatrixQrCodeLoginData
|
||||
interface MatrixQrCodeLoginData {
|
||||
fun serverName(): String?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,12 @@ interface BaseRoom : Closeable {
|
|||
*/
|
||||
fun info(): RoomInfo = roomInfoFlow.value
|
||||
|
||||
/**
|
||||
* A one-to-one is a room with exactly 2 members.
|
||||
* See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
|
||||
*/
|
||||
val isOneToOne: Boolean get() = info().activeMembersCount == 2L
|
||||
|
||||
/**
|
||||
* Try to load the room members and update the membersFlow.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -9,34 +9,21 @@ package io.element.android.libraries.matrix.api.room
|
|||
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SendHandle
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange
|
||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||
import io.element.android.libraries.matrix.api.media.FileInfo
|
||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.knock.KnockRequest
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.io.File
|
||||
|
||||
interface JoinedRoom : BaseRoom {
|
||||
val syncUpdateFlow: StateFlow<Long>
|
||||
|
|
@ -50,12 +37,6 @@ interface JoinedRoom : BaseRoom {
|
|||
*/
|
||||
val knockRequestsFlow: Flow<List<KnockRequest>>
|
||||
|
||||
/**
|
||||
* A one-to-one is a room with exactly 2 members.
|
||||
* See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
|
||||
*/
|
||||
val isOneToOne: Boolean get() = info().activeMembersCount == 2L
|
||||
|
||||
/**
|
||||
* The live timeline of the room. Must be used to send Event to a room.
|
||||
*/
|
||||
|
|
@ -69,135 +50,14 @@ interface JoinedRoom : BaseRoom {
|
|||
createTimelineParams: CreateTimelineParams,
|
||||
): Result<Timeline>
|
||||
|
||||
suspend fun sendMessage(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit>
|
||||
|
||||
suspend fun editMessage(eventId: EventId, body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit>
|
||||
|
||||
suspend fun sendImage(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
imageInfo: ImageInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendVideo(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
videoInfo: VideoInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendAudio(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendFile(
|
||||
file: File,
|
||||
fileInfo: FileInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendVoiceMessage(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
/**
|
||||
* Share a location message in the room.
|
||||
*
|
||||
* @param body A human readable textual representation of the location.
|
||||
* @param geoUri A geo URI (RFC 5870) representing the location e.g. `geo:51.5008,0.1247;u=35`.
|
||||
* Respectively: latitude, longitude, and (optional) uncertainty.
|
||||
* @param description Optional description of the location to display to the user.
|
||||
* @param zoomLevel Optional zoom level to display the map at.
|
||||
* @param assetType Optional type of the location asset.
|
||||
* Set to SENDER if sharing own location. Set to PIN if sharing any location.
|
||||
*/
|
||||
suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
description: String? = null,
|
||||
zoomLevel: Int? = null,
|
||||
assetType: AssetType? = null,
|
||||
): Result<Unit>
|
||||
|
||||
/**
|
||||
* Create a poll in the room.
|
||||
*
|
||||
* @param question The question to ask.
|
||||
* @param answers The list of answers.
|
||||
* @param maxSelections The maximum number of answers that can be selected.
|
||||
* @param pollKind The kind of poll to create.
|
||||
*/
|
||||
suspend fun createPoll(
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit>
|
||||
|
||||
/**
|
||||
* Edit a poll in the room.
|
||||
*
|
||||
* @param pollStartId The event ID of the poll start event.
|
||||
* @param question The question to ask.
|
||||
* @param answers The list of answers.
|
||||
* @param maxSelections The maximum number of answers that can be selected.
|
||||
* @param pollKind The kind of poll to create.
|
||||
*/
|
||||
suspend fun editPoll(
|
||||
pollStartId: EventId,
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit>
|
||||
|
||||
/**
|
||||
* Send a response to a poll.
|
||||
*
|
||||
* @param pollStartId The event ID of the poll start event.
|
||||
* @param answers The list of answer ids to send.
|
||||
*/
|
||||
suspend fun sendPollResponse(pollStartId: EventId, answers: List<String>): Result<Unit>
|
||||
|
||||
/**
|
||||
* Ends a poll in the room.
|
||||
*
|
||||
* @param pollStartId The event ID of the poll start event.
|
||||
* @param text Fallback text of the poll end event.
|
||||
*/
|
||||
suspend fun endPoll(pollStartId: EventId, text: String): Result<Unit>
|
||||
|
||||
/**
|
||||
* Send a typing notification.
|
||||
* @param isTyping True if the user is typing, false otherwise.
|
||||
*/
|
||||
suspend fun typingNotice(isTyping: Boolean): Result<Unit>
|
||||
|
||||
suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit>
|
||||
|
||||
suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit>
|
||||
|
||||
suspend fun cancelSend(transactionId: TransactionId): Result<Unit>
|
||||
|
||||
suspend fun inviteUserById(id: UserId): Result<Unit>
|
||||
|
||||
suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit>
|
||||
|
|
|
|||
|
|
@ -666,6 +666,12 @@ class RustMatrixClient(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun canReportRoom(): Boolean = withContext(sessionDispatcher) {
|
||||
runCatching {
|
||||
innerClient.isReportRoomApiSupported()
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
private suspend fun File.getCacheSize(
|
||||
includeCryptoDb: Boolean = false,
|
||||
): Long = withContext(sessionDispatcher) {
|
||||
|
|
|
|||
|
|
@ -187,14 +187,17 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
|
||||
private var pendingOAuthAuthorizationData: OAuthAuthorizationData? = null
|
||||
|
||||
override suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails> {
|
||||
override suspend fun getOidcUrl(
|
||||
prompt: OidcPrompt,
|
||||
loginHint: String?,
|
||||
): Result<OidcDetails> {
|
||||
return withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||
val oAuthAuthorizationData = client.urlForOidc(
|
||||
oidcConfiguration = oidcConfigurationProvider.get(),
|
||||
prompt = prompt.toRustPrompt(),
|
||||
loginHint = null,
|
||||
loginHint = loginHint,
|
||||
)
|
||||
val url = oAuthAuthorizationData.loginUrl()
|
||||
pendingOAuthAuthorizationData = oAuthAuthorizationData
|
||||
|
|
|
|||
|
|
@ -12,4 +12,8 @@ import org.matrix.rustcomponents.sdk.QrCodeData as RustQrCodeData
|
|||
|
||||
class SdkQrCodeLoginData(
|
||||
internal val rustQrCodeData: RustQrCodeData,
|
||||
) : MatrixQrCodeLoginData
|
||||
) : MatrixQrCodeLoginData {
|
||||
override fun serverName(): String? {
|
||||
return rustQrCodeData.serverName()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,20 +13,11 @@ import io.element.android.libraries.core.extensions.mapFailure
|
|||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SendHandle
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange
|
||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||
import io.element.android.libraries.matrix.api.media.FileInfo
|
||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
|
|
@ -35,14 +26,11 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationSettingsStat
|
|||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.knock.KnockRequest
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
|
||||
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import io.element.android.libraries.matrix.impl.core.RustSendHandle
|
||||
|
|
@ -59,11 +47,8 @@ import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
|||
import io.element.android.libraries.matrix.impl.widget.RustWidgetDriver
|
||||
import io.element.android.libraries.matrix.impl.widget.generateWidgetWebViewUrl
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.drop
|
||||
|
|
@ -71,12 +56,10 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.DateDividerMode
|
||||
import org.matrix.rustcomponents.sdk.IdentityStatusChangeListener
|
||||
import org.matrix.rustcomponents.sdk.KnockRequestsListener
|
||||
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
||||
import org.matrix.rustcomponents.sdk.RoomMessageEventMessageType
|
||||
import org.matrix.rustcomponents.sdk.TimelineConfiguration
|
||||
import org.matrix.rustcomponents.sdk.TimelineFilter
|
||||
|
|
@ -89,11 +72,9 @@ import org.matrix.rustcomponents.sdk.getElementCallRequiredPermissions
|
|||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import uniffi.matrix_sdk.RoomPowerLevelChanges
|
||||
import java.io.File
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
import org.matrix.rustcomponents.sdk.IdentityStatusChange as RustIdentityStateChange
|
||||
import org.matrix.rustcomponents.sdk.KnockRequest as InnerKnockRequest
|
||||
import org.matrix.rustcomponents.sdk.RoomInfo as InnerRoomInfo
|
||||
import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline
|
||||
|
||||
class JoinedRustRoom(
|
||||
|
|
@ -101,7 +82,6 @@ class JoinedRustRoom(
|
|||
private val liveInnerTimeline: InnerTimeline,
|
||||
private val notificationSettingsService: NotificationSettingsService,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val roomInfoMapper: RoomInfoMapper,
|
||||
private val systemClock: SystemClock,
|
||||
private val roomContentForwarder: RoomContentForwarder,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
|
|
@ -112,14 +92,6 @@ class JoinedRustRoom(
|
|||
|
||||
override val syncUpdateFlow = MutableStateFlow(0L)
|
||||
|
||||
override val roomInfoFlow: StateFlow<io.element.android.libraries.matrix.api.room.RoomInfo> = mxCallbackFlow {
|
||||
innerRoom.subscribeToRoomInfoUpdates(object : RoomInfoListener {
|
||||
override fun call(roomInfo: InnerRoomInfo) {
|
||||
channel.trySend(roomInfoMapper.map(roomInfo))
|
||||
}
|
||||
})
|
||||
}.stateIn(roomCoroutineScope, started = SharingStarted.Lazily, initialValue = baseRoom.info())
|
||||
|
||||
override val roomTypingMembersFlow: Flow<List<UserId>> = mxCallbackFlow {
|
||||
val initial = emptyList<UserId>()
|
||||
channel.trySend(initial)
|
||||
|
|
@ -264,10 +236,6 @@ class JoinedRustRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit> {
|
||||
return liveTimeline.sendMessage(body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
override suspend fun editMessage(
|
||||
eventId: EventId,
|
||||
body: String,
|
||||
|
|
@ -281,159 +249,12 @@ class JoinedRustRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun sendImage(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
imageInfo: ImageInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> {
|
||||
return liveTimeline.sendImage(
|
||||
file = file,
|
||||
thumbnailFile = thumbnailFile,
|
||||
imageInfo = imageInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
progressCallback = progressCallback,
|
||||
replyParameters = replyParameters
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendVideo(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
videoInfo: VideoInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> {
|
||||
return liveTimeline.sendVideo(
|
||||
file = file,
|
||||
thumbnailFile = thumbnailFile,
|
||||
videoInfo = videoInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
progressCallback = progressCallback,
|
||||
replyParameters = replyParameters
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendAudio(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> {
|
||||
return liveTimeline.sendAudio(
|
||||
file = file,
|
||||
audioInfo = audioInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
progressCallback = progressCallback,
|
||||
replyParameters = replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendFile(
|
||||
file: File,
|
||||
fileInfo: FileInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> {
|
||||
return liveTimeline.sendFile(
|
||||
file = file,
|
||||
fileInfo = fileInfo,
|
||||
caption = caption,
|
||||
formattedCaption = formattedCaption,
|
||||
progressCallback = progressCallback,
|
||||
replyParameters = replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendVoiceMessage(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> {
|
||||
return liveTimeline.sendVoiceMessage(
|
||||
file = file,
|
||||
audioInfo = audioInfo,
|
||||
waveform = waveform,
|
||||
progressCallback = progressCallback,
|
||||
replyParameters = replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
description: String?,
|
||||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
): Result<Unit> {
|
||||
return liveTimeline.sendLocation(body, geoUri, description, zoomLevel, assetType)
|
||||
}
|
||||
|
||||
override suspend fun createPoll(
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit> {
|
||||
return liveTimeline.createPoll(question, answers, maxSelections, pollKind)
|
||||
}
|
||||
|
||||
override suspend fun editPoll(
|
||||
pollStartId: EventId,
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit> {
|
||||
return liveTimeline.editPoll(pollStartId, question, answers, maxSelections, pollKind)
|
||||
}
|
||||
|
||||
override suspend fun sendPollResponse(
|
||||
pollStartId: EventId,
|
||||
answers: List<String>
|
||||
): Result<Unit> {
|
||||
return liveTimeline.sendPollResponse(pollStartId, answers)
|
||||
}
|
||||
|
||||
override suspend fun endPoll(
|
||||
pollStartId: EventId,
|
||||
text: String
|
||||
): Result<Unit> {
|
||||
return liveTimeline.endPoll(pollStartId, text)
|
||||
}
|
||||
|
||||
override suspend fun typingNotice(isTyping: Boolean) = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
innerRoom.typingNotice(isTyping)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> {
|
||||
return liveTimeline.toggleReaction(emoji, eventOrTransactionId)
|
||||
}
|
||||
|
||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> {
|
||||
return liveTimeline.forwardEvent(eventId, roomIds)
|
||||
}
|
||||
|
||||
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> {
|
||||
return liveTimeline.cancelSend(transactionId)
|
||||
}
|
||||
|
||||
override suspend fun inviteUserById(id: UserId): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
innerRoom.inviteUserById(id.value)
|
||||
|
|
@ -645,7 +466,6 @@ class JoinedRustRoom(
|
|||
override fun destroy() {
|
||||
baseRoom.destroy()
|
||||
liveInnerTimeline.destroy()
|
||||
roomCoroutineScope.cancel()
|
||||
}
|
||||
|
||||
private fun InnerTimeline.map(
|
||||
|
|
|
|||
|
|
@ -31,10 +31,14 @@ import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
|
|||
import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsMapper
|
||||
import io.element.android.libraries.matrix.impl.roomdirectory.map
|
||||
import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType
|
||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.RoomInfoListener
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import uniffi.matrix_sdk_base.EncryptionState
|
||||
|
|
@ -48,6 +52,7 @@ class RustBaseRoom(
|
|||
private val roomSyncSubscriber: RoomSyncSubscriber,
|
||||
private val roomMembershipObserver: RoomMembershipObserver,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
roomInfoMapper: RoomInfoMapper,
|
||||
initialRoomInfo: RoomInfo,
|
||||
) : BaseRoom {
|
||||
override val roomId = RoomId(innerRoom.id())
|
||||
|
|
@ -62,10 +67,16 @@ class RustBaseRoom(
|
|||
|
||||
override val membersStateFlow: StateFlow<RoomMembersState> = roomMemberListFetcher.membersFlow
|
||||
|
||||
override val roomInfoFlow: StateFlow<RoomInfo> = MutableStateFlow(initialRoomInfo)
|
||||
|
||||
override val roomCoroutineScope = sessionCoroutineScope.childScope(coroutineDispatchers.main, "RoomScope-$roomId")
|
||||
|
||||
override val roomInfoFlow: StateFlow<RoomInfo> = mxCallbackFlow {
|
||||
innerRoom.subscribeToRoomInfoUpdates(object : RoomInfoListener {
|
||||
override fun call(roomInfo: org.matrix.rustcomponents.sdk.RoomInfo) {
|
||||
channel.trySend(roomInfoMapper.map(roomInfo))
|
||||
}
|
||||
})
|
||||
}.stateIn(roomCoroutineScope, started = SharingStarted.Lazily, initialValue = initialRoomInfo)
|
||||
|
||||
override suspend fun subscribeToSync() = roomSyncSubscriber.subscribe(roomId)
|
||||
|
||||
override suspend fun updateMembers() {
|
||||
|
|
@ -98,6 +109,7 @@ class RustBaseRoom(
|
|||
|
||||
override fun destroy() {
|
||||
innerRoom.destroy()
|
||||
roomCoroutineScope.cancel()
|
||||
}
|
||||
|
||||
override suspend fun userDisplayName(userId: UserId): Result<String?> = withContext(roomDispatcher) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ package io.element.android.libraries.matrix.impl.room
|
|||
|
||||
import io.element.android.appconfig.TimelineConfig
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.core.DeviceId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
|
@ -21,6 +20,7 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
|||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
|
||||
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewInfoMapper
|
||||
import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline
|
||||
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -30,10 +30,10 @@ import kotlinx.coroutines.sync.withLock
|
|||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.Membership
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import org.matrix.rustcomponents.sdk.Room as SdkRoom
|
||||
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService
|
||||
|
||||
class RustRoomFactory(
|
||||
|
|
@ -56,11 +56,6 @@ class RustRoomFactory(
|
|||
private val mutex = Mutex()
|
||||
private val isDestroyed: AtomicBoolean = AtomicBoolean(false)
|
||||
|
||||
private data class RustRoomReferences(
|
||||
val roomListItem: RoomListItem,
|
||||
val room: SdkRoom,
|
||||
)
|
||||
|
||||
private val roomInfoMapper = RoomInfoMapper()
|
||||
|
||||
private val eventFilters = TimelineConfig.excludedEvents
|
||||
|
|
@ -84,20 +79,26 @@ class RustRoomFactory(
|
|||
Timber.d("Room factory is destroyed, returning null for $roomId")
|
||||
return@withContext null
|
||||
}
|
||||
val roomReferences = awaitRoomReferences(roomId) ?: return@withContext null
|
||||
getBaseRoom(roomReferences)
|
||||
val roomListItem = awaitRoomListItem(roomId) ?: return@withContext null
|
||||
getBaseRoom(roomListItem)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getBaseRoom(roomReferences: RustRoomReferences): RustBaseRoom? {
|
||||
val initialRoomInfo = roomReferences.room.roomInfo()
|
||||
private suspend fun getBaseRoom(roomListItem: RoomListItem): RustBaseRoom? {
|
||||
val sdkRoom = innerClient.getRoom(roomListItem.id()) ?: return null
|
||||
return getBaseRoom(sdkRoom)
|
||||
}
|
||||
|
||||
private suspend fun getBaseRoom(sdkRoom: Room): RustBaseRoom {
|
||||
val initialRoomInfo = sdkRoom.roomInfo()
|
||||
return RustBaseRoom(
|
||||
sessionId = sessionId,
|
||||
deviceId = deviceId,
|
||||
innerRoom = roomReferences.room,
|
||||
innerRoom = sdkRoom,
|
||||
coroutineDispatchers = dispatchers,
|
||||
roomSyncSubscriber = roomSyncSubscriber,
|
||||
roomMembershipObserver = roomMembershipObserver,
|
||||
roomInfoMapper = roomInfoMapper,
|
||||
initialRoomInfo = roomInfoMapper.map(initialRoomInfo),
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
)
|
||||
|
|
@ -109,31 +110,26 @@ class RustRoomFactory(
|
|||
Timber.d("Room factory is destroyed, returning null for $roomId")
|
||||
return@withContext null
|
||||
}
|
||||
val roomReferences = awaitRoomReferences(roomId) ?: return@withContext null
|
||||
|
||||
if (roomReferences.room.membership() == Membership.JOINED) {
|
||||
val baseRoom = getBaseRoom(roomReferences) ?: return@withContext null
|
||||
val roomListItem = awaitRoomListItem(roomId) ?: return@withContext null
|
||||
|
||||
if (roomListItem.membership() == Membership.JOINED) {
|
||||
// Init the live timeline in the SDK from the RoomListItem
|
||||
if (!roomReferences.roomListItem.isTimelineInitialized()) {
|
||||
roomReferences.roomListItem.initTimeline(eventFilters, "LIVE")
|
||||
}
|
||||
val sdkRoom = roomListItem.fullRoomWithTimeline(eventFilters)
|
||||
|
||||
GetRoomResult.Joined(
|
||||
JoinedRustRoom(
|
||||
baseRoom = baseRoom,
|
||||
baseRoom = getBaseRoom(sdkRoom),
|
||||
notificationSettingsService = notificationSettingsService,
|
||||
roomContentForwarder = roomContentForwarder,
|
||||
liveInnerTimeline = roomReferences.room.timeline(),
|
||||
liveInnerTimeline = sdkRoom.timeline(),
|
||||
coroutineDispatchers = dispatchers,
|
||||
systemClock = systemClock,
|
||||
roomInfoMapper = roomInfoMapper,
|
||||
featureFlagService = featureFlagService,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val preview = try {
|
||||
roomReferences.roomListItem.previewRoom(via = emptyList())
|
||||
roomListItem.previewRoom(via = emptyList())
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to get room preview for $roomId")
|
||||
return@withContext null
|
||||
|
|
@ -142,7 +138,7 @@ class RustRoomFactory(
|
|||
GetRoomResult.NotJoined(
|
||||
NotJoinedRustRoom(
|
||||
sessionId = sessionId,
|
||||
localRoom = getBaseRoom(roomReferences),
|
||||
localRoom = getBaseRoom(roomListItem),
|
||||
previewInfo = RoomPreviewInfoMapper.map(preview.info()),
|
||||
)
|
||||
)
|
||||
|
|
@ -150,36 +146,23 @@ class RustRoomFactory(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getRoomReferences(roomId: RoomId): RustRoomReferences? {
|
||||
val roomListItem = innerRoomListService.roomOrNull(roomId.value)
|
||||
/**
|
||||
* Get the Rust room list item for a room, retrying after the room list is loaded if necessary.
|
||||
*/
|
||||
private suspend fun awaitRoomListItem(roomId: RoomId): RoomListItem? {
|
||||
var roomListItem = innerRoomListService.roomOrNull(roomId.value)
|
||||
if (roomListItem == null) {
|
||||
// ... otherwise, lets wait for the SS to load all rooms and check again.
|
||||
roomListService.allRooms.awaitLoaded()
|
||||
roomListItem = innerRoomListService.roomOrNull(roomId.value)
|
||||
}
|
||||
|
||||
if (roomListItem == null) {
|
||||
Timber.d("Room not found for $roomId")
|
||||
return null
|
||||
}
|
||||
val room = tryOrNull {
|
||||
innerClient.getRoom(roomId.value)
|
||||
} ?: error("Failed to get room for room id: $roomId")
|
||||
|
||||
Timber.d("Got room for $roomId")
|
||||
return RustRoomReferences(
|
||||
roomListItem = roomListItem,
|
||||
room = room,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Rust room references for a room, retrying after the room list is loaded if necessary.
|
||||
*/
|
||||
private suspend fun awaitRoomReferences(roomId: RoomId): RustRoomReferences? {
|
||||
var roomReferences = getRoomReferences(roomId)
|
||||
|
||||
if (roomReferences == null) {
|
||||
// ... otherwise, lets wait for the SS to load all rooms and check again.
|
||||
roomListService.allRooms.awaitLoaded()
|
||||
roomReferences = getRoomReferences(roomId)
|
||||
}
|
||||
|
||||
return roomReferences
|
||||
return roomListItem
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class DefaultCallWidgetSettingsProvider @Inject constructor(
|
|||
sentryEnvironment = if (buildMeta.buildType == BuildType.RELEASE) "RELEASE" else "DEBUG",
|
||||
parentUrl = null,
|
||||
hideHeader = true,
|
||||
controlledMediaDevices = false,
|
||||
)
|
||||
val rustWidgetSettings = newVirtualElementCallWidget(options)
|
||||
return MatrixWidgetSettings.fromRustWidgetSettings(rustWidgetSettings)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.auth.qrlogin
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeQrCodeData
|
||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||
import org.junit.Test
|
||||
|
||||
class SdkQrCodeLoginDataTest {
|
||||
@Test
|
||||
fun `getServer reads the value from the Rust side, null case`() {
|
||||
val sut = SdkQrCodeLoginData(
|
||||
rustQrCodeData = FakeQrCodeData(
|
||||
serverNameResult = { null },
|
||||
),
|
||||
)
|
||||
assertThat(sut.serverName()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getServer reads the value from the Rust side`() {
|
||||
val sut = SdkQrCodeLoginData(
|
||||
rustQrCodeData = FakeQrCodeData(
|
||||
serverNameResult = { A_HOMESERVER_URL },
|
||||
),
|
||||
)
|
||||
assertThat(sut.serverName()).isEqualTo(A_HOMESERVER_URL)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.fixtures.fakes
|
||||
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import org.matrix.rustcomponents.sdk.NoPointer
|
||||
import org.matrix.rustcomponents.sdk.QrCodeData
|
||||
|
||||
class FakeQrCodeData(
|
||||
private val serverNameResult: () -> String? = { lambdaError() },
|
||||
) : QrCodeData(NoPointer) {
|
||||
override fun serverName(): String? {
|
||||
return serverNameResult()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.room
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoom
|
||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeRustRoomListService
|
||||
import io.element.android.libraries.matrix.test.A_DEVICE_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class RustBaseRoomTest {
|
||||
@Test
|
||||
fun `RustBaseRoom should cancel the room coroutine scope when it is destroyed`() = runTest {
|
||||
val rustBaseRoom = createRustBaseRoom(
|
||||
// Not using backgroundScope here, but the test scope
|
||||
sessionCoroutineScope = this
|
||||
)
|
||||
assertThat(rustBaseRoom.roomCoroutineScope.isActive).isTrue()
|
||||
rustBaseRoom.destroy()
|
||||
assertThat(rustBaseRoom.roomCoroutineScope.isActive).isFalse()
|
||||
}
|
||||
|
||||
private fun TestScope.createRustBaseRoom(
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
): RustBaseRoom {
|
||||
val dispatchers = testCoroutineDispatchers()
|
||||
return RustBaseRoom(
|
||||
sessionId = A_SESSION_ID,
|
||||
deviceId = A_DEVICE_ID,
|
||||
innerRoom = FakeRustRoom(),
|
||||
coroutineDispatchers = dispatchers,
|
||||
roomSyncSubscriber = RoomSyncSubscriber(
|
||||
roomListService = FakeRustRoomListService(),
|
||||
dispatchers = dispatchers,
|
||||
),
|
||||
roomMembershipObserver = RoomMembershipObserver(),
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
roomInfoMapper = RoomInfoMapper(),
|
||||
initialRoomInfo = aRoomInfo(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -88,6 +88,7 @@ class FakeMatrixClient(
|
|||
private val availableSlidingSyncVersionsLambda: () -> Result<List<SlidingSyncVersion>> = { lambdaError() },
|
||||
private val ignoreUserResult: (UserId) -> Result<Unit> = { lambdaError() },
|
||||
private var unIgnoreUserResult: (UserId) -> Result<Unit> = { Result.success(Unit) },
|
||||
private val canReportRoomLambda: () -> Boolean = { false },
|
||||
override val ignoredUsersFlow: StateFlow<ImmutableList<UserId>> = MutableStateFlow(persistentListOf()),
|
||||
) : MatrixClient {
|
||||
var setDisplayNameCalled: Boolean = false
|
||||
|
|
@ -329,4 +330,8 @@ class FakeMatrixClient(
|
|||
override suspend fun availableSlidingSyncVersions(): Result<List<SlidingSyncVersion>> {
|
||||
return availableSlidingSyncVersionsLambda()
|
||||
}
|
||||
|
||||
override suspend fun canReportRoom(): Boolean {
|
||||
return canReportRoomLambda()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import io.element.android.libraries.matrix.api.core.TransactionId
|
|||
import io.element.android.libraries.matrix.api.core.UniqueId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||
|
||||
const val A_USER_NAME = "alice"
|
||||
const val A_USER_NAME_2 = "Bob"
|
||||
|
|
@ -73,7 +72,6 @@ const val A_HOMESERVER_URL_2 = "matrix-client.org"
|
|||
val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = true, supportsOidcLogin = false)
|
||||
val A_HOMESERVER_OIDC = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = false, supportsOidcLogin = true)
|
||||
val A_ROOM_NOTIFICATION_MODE = RoomNotificationMode.MUTE
|
||||
val A_ROOM_NOTIFICATION_SETTINGS = RoomNotificationSettings(mode = A_ROOM_NOTIFICATION_MODE, isDefault = false)
|
||||
|
||||
const val AN_AVATAR_URL = "mxc://data"
|
||||
|
||||
|
|
@ -88,3 +86,5 @@ val A_SERVER_LIST = listOf("server1", "server2")
|
|||
|
||||
const val A_TIMESTAMP = 567L
|
||||
const val A_FORMATTED_DATE = "April 6, 1980 at 6:35 PM"
|
||||
|
||||
const val A_LOGIN_HINT = "mxid:@alice:example.org"
|
||||
|
|
|
|||
|
|
@ -87,7 +87,10 @@ class FakeMatrixAuthenticationService(
|
|||
return importCreatedSessionLambda(externalSession)
|
||||
}
|
||||
|
||||
override suspend fun getOidcUrl(prompt: OidcPrompt): Result<OidcDetails> = simulateLongTask {
|
||||
override suspend fun getOidcUrl(
|
||||
prompt: OidcPrompt,
|
||||
loginHint: String?,
|
||||
): Result<OidcDetails> = simulateLongTask {
|
||||
oidcError?.let { Result.failure(it) } ?: Result.success(A_OIDC_DATA)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.test.auth.qrlogin
|
|||
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData
|
||||
import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginDataFactory
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
|
||||
class FakeMatrixQrCodeLoginDataFactory(
|
||||
|
|
@ -20,4 +21,8 @@ class FakeMatrixQrCodeLoginDataFactory(
|
|||
}
|
||||
}
|
||||
|
||||
class FakeMatrixQrCodeLoginData : MatrixQrCodeLoginData
|
||||
class FakeMatrixQrCodeLoginData(
|
||||
private val serverNameResult: () -> String? = { lambdaError() },
|
||||
) : MatrixQrCodeLoginData {
|
||||
override fun serverName() = serverNameResult()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,17 +12,9 @@ import io.element.android.libraries.matrix.api.core.DeviceId
|
|||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SendHandle
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange
|
||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||
import io.element.android.libraries.matrix.api.media.FileInfo
|
||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
|
|
@ -33,16 +25,12 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationSettingsStat
|
|||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.room.knock.KnockRequest
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
|
@ -53,7 +41,6 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import java.io.File
|
||||
|
||||
class FakeJoinedRoom(
|
||||
val baseRoom: FakeBaseRoom = FakeBaseRoom(),
|
||||
|
|
@ -63,35 +50,16 @@ class FakeJoinedRoom(
|
|||
override val roomTypingMembersFlow: Flow<List<UserId>> = MutableStateFlow(emptyList()),
|
||||
override val identityStateChangesFlow: Flow<List<IdentityStateChange>> = MutableStateFlow(emptyList()),
|
||||
override val roomNotificationSettingsStateFlow: StateFlow<RoomNotificationSettingsState> =
|
||||
MutableStateFlow(RoomNotificationSettingsState.Unknown),
|
||||
MutableStateFlow(RoomNotificationSettingsState.Unknown),
|
||||
override val knockRequestsFlow: Flow<List<KnockRequest>> = MutableStateFlow(emptyList()),
|
||||
private val roomNotificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||
private var createTimelineResult: (CreateTimelineParams) -> Result<Timeline> = { lambdaError() },
|
||||
private val sendMessageResult: (String, String?, List<IntentionalMention>) -> Result<Unit> = { _, _, _ -> lambdaError() },
|
||||
private val editMessageLambda: (EventId, String, String?, List<IntentionalMention>) -> Result<Unit> = { _, _, _, _ -> lambdaError() },
|
||||
private val sendImageResult: (File, File?, ImageInfo, String?, String?, ProgressCallback?, ReplyParameters?) -> Result<FakeMediaUploadHandler> =
|
||||
{ _, _, _, _, _, _, _ -> lambdaError() },
|
||||
private val sendVideoResult: (File, File?, VideoInfo, String?, String?, ProgressCallback?, ReplyParameters?) -> Result<FakeMediaUploadHandler> =
|
||||
{ _, _, _, _, _, _, _ -> lambdaError() },
|
||||
private val sendFileResult: (File, FileInfo, String?, String?, ProgressCallback?, ReplyParameters?) -> Result<FakeMediaUploadHandler> =
|
||||
{ _, _, _, _, _, _ -> lambdaError() },
|
||||
private val sendAudioResult: (File, AudioInfo, String?, String?, ProgressCallback?, ReplyParameters?) -> Result<FakeMediaUploadHandler> =
|
||||
{ _, _, _, _, _, _ -> lambdaError() },
|
||||
private val sendVoiceMessageResult: (File, AudioInfo, List<Float>, ProgressCallback?, ReplyParameters?) -> Result<FakeMediaUploadHandler> =
|
||||
{ _, _, _, _, _ -> lambdaError() },
|
||||
private val sendLocationResult: (String, String, String?, Int?, AssetType?) -> Result<Unit> = { _, _, _, _, _ -> lambdaError() },
|
||||
private val sendCallNotificationIfNeededResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val progressCallbackValues: List<Pair<Long, Long>> = emptyList(),
|
||||
private val createPollResult: (String, List<String>, Int, PollKind) -> Result<Unit> = { _, _, _, _ -> lambdaError() },
|
||||
private val editPollResult: (EventId, String, List<String>, Int, PollKind) -> Result<Unit> = { _, _, _, _, _ -> lambdaError() },
|
||||
private val sendPollResponseResult: (EventId, List<String>) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val endPollResult: (EventId, String) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val generateWidgetWebViewUrlResult: (MatrixWidgetSettings, String, String?, String?) -> Result<String> = { _, _, _, _ -> lambdaError() },
|
||||
private val getWidgetDriverResult: (MatrixWidgetSettings) -> Result<MatrixWidgetDriver> = { lambdaError() },
|
||||
private val typingNoticeResult: (Boolean) -> Result<Unit> = { lambdaError() },
|
||||
private val toggleReactionResult: (String, EventOrTransactionId) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val forwardEventResult: (EventId, List<RoomId>) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val cancelSendResult: (TransactionId) -> Result<Unit> = { lambdaError() },
|
||||
private val inviteUserResult: (UserId) -> Result<Unit> = { lambdaError() },
|
||||
private val setNameResult: (String) -> Result<Unit> = { lambdaError() },
|
||||
private val setTopicResult: (String) -> Result<Unit> = { lambdaError() },
|
||||
|
|
@ -127,10 +95,6 @@ class FakeJoinedRoom(
|
|||
createTimelineResult(createTimelineParams)
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit> = simulateLongTask {
|
||||
sendMessageResult(body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
override suspend fun editMessage(
|
||||
eventId: EventId,
|
||||
body: String,
|
||||
|
|
@ -140,174 +104,10 @@ class FakeJoinedRoom(
|
|||
editMessageLambda(eventId, body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
override suspend fun sendImage(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
imageInfo: ImageInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendImageResult(
|
||||
file,
|
||||
thumbnailFile,
|
||||
imageInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendVideo(
|
||||
file: File,
|
||||
thumbnailFile: File?,
|
||||
videoInfo: VideoInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendVideoResult(
|
||||
file,
|
||||
thumbnailFile,
|
||||
videoInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendAudio(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendAudioResult(
|
||||
file,
|
||||
audioInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendFile(
|
||||
file: File,
|
||||
fileInfo: FileInfo,
|
||||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendFileResult(
|
||||
file,
|
||||
fileInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendVoiceMessage(
|
||||
file: File,
|
||||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendVoiceMessageResult(
|
||||
file,
|
||||
audioInfo,
|
||||
waveform,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
body: String,
|
||||
geoUri: String,
|
||||
description: String?,
|
||||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
): Result<Unit> = simulateLongTask {
|
||||
return sendLocationResult(
|
||||
body,
|
||||
geoUri,
|
||||
description,
|
||||
zoomLevel,
|
||||
assetType,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun createPoll(question: String, answers: List<String>, maxSelections: Int, pollKind: PollKind): Result<Unit> = simulateLongTask {
|
||||
return createPollResult(
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun editPoll(
|
||||
pollStartId: EventId,
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind
|
||||
): Result<Unit> = simulateLongTask {
|
||||
return editPollResult(
|
||||
pollStartId,
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun sendPollResponse(pollStartId: EventId, answers: List<String>): Result<Unit> = simulateLongTask {
|
||||
return sendPollResponseResult(
|
||||
pollStartId,
|
||||
answers,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun endPoll(pollStartId: EventId, text: String): Result<Unit> = simulateLongTask {
|
||||
endPollResult(
|
||||
pollStartId,
|
||||
text,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun typingNotice(isTyping: Boolean): Result<Unit> = simulateLongTask {
|
||||
typingNoticeResult(isTyping)
|
||||
}
|
||||
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> = simulateLongTask {
|
||||
toggleReactionResult(emoji, eventOrTransactionId)
|
||||
}
|
||||
|
||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = simulateLongTask {
|
||||
forwardEventResult(eventId, roomIds)
|
||||
}
|
||||
|
||||
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = simulateLongTask {
|
||||
cancelSendResult(transactionId)
|
||||
}
|
||||
|
||||
override suspend fun inviteUserById(id: UserId): Result<Unit> = simulateLongTask {
|
||||
inviteUserResult(id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.test.timeline
|
|||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||
import io.element.android.libraries.matrix.api.media.FileInfo
|
||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||
|
|
@ -26,6 +27,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransa
|
|||
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
import io.element.android.tests.testutils.simulateLongTask
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -47,23 +50,31 @@ class FakeTimeline(
|
|||
)
|
||||
),
|
||||
override val membershipChangeEventReceived: Flow<Unit> = MutableSharedFlow(),
|
||||
private val progressCallbackValues: List<Pair<Long, Long>> = emptyList(),
|
||||
private val cancelSendResult: (TransactionId) -> Result<Unit> = { lambdaError() },
|
||||
) : Timeline {
|
||||
var sendMessageLambda: (
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
) -> Result<Unit> = { _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = simulateLongTask {
|
||||
cancelSendResult(transactionId)
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit> = sendMessageLambda(body, htmlBody, intentionalMentions)
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendMessageLambda(body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
var redactEventLambda: (eventOrTransactionId: EventOrTransactionId, reason: String?) -> Result<Unit> = { _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun redactEvent(
|
||||
|
|
@ -77,7 +88,7 @@ class FakeTimeline(
|
|||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
) -> Result<Unit> = { _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun editMessage(
|
||||
|
|
@ -117,7 +128,7 @@ class FakeTimeline(
|
|||
intentionalMentions: List<IntentionalMention>,
|
||||
fromNotification: Boolean,
|
||||
) -> Result<Unit> = { _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun replyMessage(
|
||||
|
|
@ -154,15 +165,18 @@ class FakeTimeline(
|
|||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = sendImageLambda(
|
||||
file,
|
||||
thumbnailFile,
|
||||
imageInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendImageLambda(
|
||||
file,
|
||||
thumbnailFile,
|
||||
imageInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
var sendVideoLambda: (
|
||||
file: File,
|
||||
|
|
@ -184,15 +198,18 @@ class FakeTimeline(
|
|||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = sendVideoLambda(
|
||||
file,
|
||||
thumbnailFile,
|
||||
videoInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendVideoLambda(
|
||||
file,
|
||||
thumbnailFile,
|
||||
videoInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
var sendAudioLambda: (
|
||||
file: File,
|
||||
|
|
@ -212,14 +229,17 @@ class FakeTimeline(
|
|||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = sendAudioLambda(
|
||||
file,
|
||||
audioInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendAudioLambda(
|
||||
file,
|
||||
audioInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
var sendFileLambda: (
|
||||
file: File,
|
||||
|
|
@ -239,14 +259,17 @@ class FakeTimeline(
|
|||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = sendFileLambda(
|
||||
file,
|
||||
fileInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendFileLambda(
|
||||
file,
|
||||
fileInfo,
|
||||
caption,
|
||||
formattedCaption,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
var sendVoiceMessageLambda: (
|
||||
file: File,
|
||||
|
|
@ -264,13 +287,16 @@ class FakeTimeline(
|
|||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<MediaUploadHandler> = sendVoiceMessageLambda(
|
||||
file,
|
||||
audioInfo,
|
||||
waveform,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
): Result<MediaUploadHandler> = simulateLongTask {
|
||||
simulateSendMediaProgress(progressCallback)
|
||||
sendVoiceMessageLambda(
|
||||
file,
|
||||
audioInfo,
|
||||
waveform,
|
||||
progressCallback,
|
||||
replyParameters,
|
||||
)
|
||||
}
|
||||
|
||||
var sendLocationLambda: (
|
||||
body: String,
|
||||
|
|
@ -279,7 +305,7 @@ class FakeTimeline(
|
|||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
) -> Result<Unit> = { _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun sendLocation(
|
||||
|
|
@ -288,24 +314,30 @@ class FakeTimeline(
|
|||
description: String?,
|
||||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
): Result<Unit> = sendLocationLambda(
|
||||
body,
|
||||
geoUri,
|
||||
description,
|
||||
zoomLevel,
|
||||
assetType
|
||||
)
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendLocationLambda(
|
||||
body,
|
||||
geoUri,
|
||||
description,
|
||||
zoomLevel,
|
||||
assetType,
|
||||
)
|
||||
}
|
||||
|
||||
var toggleReactionLambda: (emoji: String, eventOrTransactionId: EventOrTransactionId) -> Result<Unit> = { _, _ -> Result.success(Unit) }
|
||||
var toggleReactionLambda: (emoji: String, eventOrTransactionId: EventOrTransactionId) -> Result<Unit> = { _, _ -> lambdaError() }
|
||||
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> = toggleReactionLambda(
|
||||
emoji,
|
||||
eventOrTransactionId
|
||||
)
|
||||
override suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result<Unit> = simulateLongTask {
|
||||
toggleReactionLambda(
|
||||
emoji,
|
||||
eventOrTransactionId,
|
||||
)
|
||||
}
|
||||
|
||||
var forwardEventLambda: (eventId: EventId, roomIds: List<RoomId>) -> Result<Unit> = { _, _ -> Result.success(Unit) }
|
||||
var forwardEventLambda: (eventId: EventId, roomIds: List<RoomId>) -> Result<Unit> = { _, _ -> lambdaError() }
|
||||
|
||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = forwardEventLambda(eventId, roomIds)
|
||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> = simulateLongTask {
|
||||
forwardEventLambda(eventId, roomIds)
|
||||
}
|
||||
|
||||
var createPollLambda: (
|
||||
question: String,
|
||||
|
|
@ -313,20 +345,17 @@ class FakeTimeline(
|
|||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
) -> Result<Unit> = { _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun createPoll(
|
||||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit> = createPollLambda(
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind
|
||||
)
|
||||
override suspend fun createPoll(question: String, answers: List<String>, maxSelections: Int, pollKind: PollKind): Result<Unit> = simulateLongTask {
|
||||
createPollLambda(
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind,
|
||||
)
|
||||
}
|
||||
|
||||
var editPollLambda: (
|
||||
pollStartId: EventId,
|
||||
|
|
@ -335,7 +364,7 @@ class FakeTimeline(
|
|||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
) -> Result<Unit> = { _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun editPoll(
|
||||
|
|
@ -343,44 +372,56 @@ class FakeTimeline(
|
|||
question: String,
|
||||
answers: List<String>,
|
||||
maxSelections: Int,
|
||||
pollKind: PollKind,
|
||||
): Result<Unit> = editPollLambda(
|
||||
pollStartId,
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind
|
||||
)
|
||||
pollKind: PollKind
|
||||
): Result<Unit> = simulateLongTask {
|
||||
editPollLambda(
|
||||
pollStartId,
|
||||
question,
|
||||
answers,
|
||||
maxSelections,
|
||||
pollKind,
|
||||
)
|
||||
}
|
||||
|
||||
var sendPollResponseLambda: (
|
||||
pollStartId: EventId,
|
||||
answers: List<String>,
|
||||
) -> Result<Unit> = { _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun sendPollResponse(
|
||||
pollStartId: EventId,
|
||||
answers: List<String>,
|
||||
): Result<Unit> = sendPollResponseLambda(pollStartId, answers)
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendPollResponseLambda(
|
||||
pollStartId,
|
||||
answers,
|
||||
)
|
||||
}
|
||||
|
||||
var endPollLambda: (
|
||||
pollStartId: EventId,
|
||||
text: String,
|
||||
) -> Result<Unit> = { _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun endPoll(
|
||||
pollStartId: EventId,
|
||||
text: String,
|
||||
): Result<Unit> = endPollLambda(pollStartId, text)
|
||||
): Result<Unit> = simulateLongTask {
|
||||
endPollLambda(
|
||||
pollStartId,
|
||||
text,
|
||||
)
|
||||
}
|
||||
|
||||
var sendReadReceiptLambda: (
|
||||
eventId: EventId,
|
||||
receiptType: ReceiptType,
|
||||
) -> Result<Unit> = { _, _ ->
|
||||
Result.success(Unit)
|
||||
lambdaError()
|
||||
}
|
||||
|
||||
override suspend fun sendReadReceipt(
|
||||
|
|
@ -417,5 +458,12 @@ class FakeTimeline(
|
|||
closeCounter++
|
||||
}
|
||||
|
||||
private suspend fun simulateSendMediaProgress(progressCallback: ProgressCallback?) {
|
||||
progressCallbackValues.forEach { (current, total) ->
|
||||
progressCallback?.onProgress(current, total)
|
||||
delay(1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() = "FakeTimeline: $name"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.core.ProgressCallback
|
|||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Job
|
||||
|
|
@ -49,7 +50,7 @@ class MediaSender @Inject constructor(
|
|||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
): Result<Unit> {
|
||||
return room.sendMedia(
|
||||
return room.liveTimeline.sendMedia(
|
||||
uploadInfo = mediaUploadInfo,
|
||||
progressCallback = progressCallback,
|
||||
caption = caption,
|
||||
|
|
@ -76,7 +77,7 @@ class MediaSender @Inject constructor(
|
|||
compressIfPossible = compressIfPossible,
|
||||
)
|
||||
.flatMapCatching { info ->
|
||||
room.sendMedia(
|
||||
room.liveTimeline.sendMedia(
|
||||
uploadInfo = info,
|
||||
progressCallback = progressCallback,
|
||||
caption = caption,
|
||||
|
|
@ -108,7 +109,7 @@ class MediaSender @Inject constructor(
|
|||
audioInfo = audioInfo,
|
||||
waveform = waveForm,
|
||||
)
|
||||
room.sendMedia(
|
||||
room.liveTimeline.sendMedia(
|
||||
uploadInfo = newInfo,
|
||||
progressCallback = progressCallback,
|
||||
caption = null,
|
||||
|
|
@ -130,7 +131,7 @@ class MediaSender @Inject constructor(
|
|||
ongoingUploadJobs.remove(Job)
|
||||
}
|
||||
|
||||
private suspend fun JoinedRoom.sendMedia(
|
||||
private suspend fun Timeline.sendMedia(
|
||||
uploadInfo: MediaUploadInfo,
|
||||
progressCallback: ProgressCallback?,
|
||||
caption: String?,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
|
|||
import io.element.android.libraries.matrix.api.room.message.ReplyParameters
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
|
||||
|
|
@ -51,7 +52,9 @@ class MediaSenderTest {
|
|||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
val room = FakeJoinedRoom(
|
||||
sendImageResult = sendImageResult
|
||||
liveTimeline = FakeTimeline().apply {
|
||||
sendImageLambda = sendImageResult
|
||||
},
|
||||
)
|
||||
val sender = createMediaSender(room = room)
|
||||
|
||||
|
|
@ -74,14 +77,22 @@ class MediaSenderTest {
|
|||
|
||||
@Test
|
||||
fun `given a failure in the media upload when sending the whole process fails`() = runTest {
|
||||
val preProcessor = FakeMediaPreProcessor().apply {
|
||||
givenImageResult()
|
||||
}
|
||||
val sendImageResult =
|
||||
lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: ProgressCallback?, _: ReplyParameters? ->
|
||||
Result.failure<FakeMediaUploadHandler>(Exception())
|
||||
}
|
||||
val room = FakeJoinedRoom(
|
||||
sendImageResult = sendImageResult
|
||||
liveTimeline = FakeTimeline().apply {
|
||||
sendImageLambda = sendImageResult
|
||||
},
|
||||
)
|
||||
val sender = createMediaSender(
|
||||
preProcessor = preProcessor,
|
||||
room = room,
|
||||
)
|
||||
val sender = createMediaSender(room = room)
|
||||
|
||||
val uri = Uri.parse("content://image.jpg")
|
||||
val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg)
|
||||
|
|
@ -94,10 +105,12 @@ class MediaSenderTest {
|
|||
fun `given a cancellation in the media upload when sending the job is cancelled`() = runTest(StandardTestDispatcher()) {
|
||||
val sendFileResult =
|
||||
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, ReplyParameters?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
val room = FakeJoinedRoom(
|
||||
sendFileResult = sendFileResult
|
||||
liveTimeline = FakeTimeline().apply {
|
||||
sendFileLambda = sendFileResult
|
||||
},
|
||||
)
|
||||
val sender = createMediaSender(room = room)
|
||||
val sendJob = launch {
|
||||
|
|
|
|||
|
|
@ -5,35 +5,11 @@
|
|||
<string name="notification_channel_noisy">"Hysbysiadau swnllyd"</string>
|
||||
<string name="notification_channel_ringing_calls">"Galwadau\'n canu"</string>
|
||||
<string name="notification_channel_silent">"Hysbysiadau tawel"</string>
|
||||
<plurals name="notification_compat_summary_line_for_room">
|
||||
<item quantity="zero">"%1$s: %2$d negeseuon"</item>
|
||||
<item quantity="one">"%1$s: %2$d neges"</item>
|
||||
<item quantity="two">"%1$s: %2$d neges"</item>
|
||||
<item quantity="few">"%1$s: %2$d neges"</item>
|
||||
<item quantity="many">"%1$s: %2$d neges"</item>
|
||||
<item quantity="other">"%1$s: %2$d neges"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_compat_summary_title">
|
||||
<item quantity="zero">"%d hysbysiadau"</item>
|
||||
<item quantity="one">"%d hysbysiad"</item>
|
||||
<item quantity="two">"%d hysbysiad"</item>
|
||||
<item quantity="few">"%d hysbysiad"</item>
|
||||
<item quantity="many">"%d hysbysiad"</item>
|
||||
<item quantity="other">"%d hysbysiad"</item>
|
||||
</plurals>
|
||||
<string name="notification_fallback_content">"Hysbysiad"</string>
|
||||
<string name="notification_incoming_call">"📹 Galwad i mewn"</string>
|
||||
<string name="notification_inline_reply_failed">"** Wedi methu anfon - agorwch yr ystafell"</string>
|
||||
<string name="notification_invitation_action_join">"Ymuno"</string>
|
||||
<string name="notification_invitation_action_reject">"Gwrthod"</string>
|
||||
<plurals name="notification_invitations">
|
||||
<item quantity="zero">"%d gwahoddiadau"</item>
|
||||
<item quantity="one">"%d gwahoddiadau"</item>
|
||||
<item quantity="two">"%d gwahoddiadau"</item>
|
||||
<item quantity="few">"%d gwahoddiadau"</item>
|
||||
<item quantity="many">"%d gwahoddiadau"</item>
|
||||
<item quantity="other">"%d gwahoddiadau"</item>
|
||||
</plurals>
|
||||
<string name="notification_invite_body">"Wedi eich gwahodd i sgwrsio"</string>
|
||||
<string name="notification_invite_body_with_sender">"Mae %1$s wedi eich gwahodd i sgwrsio"</string>
|
||||
<string name="notification_mentioned_you_body">"Wedi eich crybwyll: %1$s"</string>
|
||||
|
|
@ -56,25 +32,9 @@
|
|||
<string name="notification_test_push_notification_content">"Rydych chi\'n edrych ar yr hysbysiad! Cliciwch fi!"</string>
|
||||
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
|
||||
<string name="notification_ticker_text_group">"%1$s : %2$s %3$s"</string>
|
||||
<plurals name="notification_unread_notified_messages">
|
||||
<item quantity="zero">"%d negeseuon hysbyswyd heb eu darllen"</item>
|
||||
<item quantity="one">"%d neges hysbyswyd heb ei ddarllen"</item>
|
||||
<item quantity="two">"%d neges hysbyswyd heb eu darllen"</item>
|
||||
<item quantity="few">"%d neges hysbyswyd heb eu darllen"</item>
|
||||
<item quantity="many">"%d neges hysbyswyd heb eu darllen"</item>
|
||||
<item quantity="other">"%d neges hysbyswyd heb eu darllen"</item>
|
||||
</plurals>
|
||||
<string name="notification_unread_notified_messages_and_invitation">"%1$s a %2$s"</string>
|
||||
<string name="notification_unread_notified_messages_in_room">"%1$s yn %2$s"</string>
|
||||
<string name="notification_unread_notified_messages_in_room_and_invitation">"%1$s yn %2$s a %3$s"</string>
|
||||
<plurals name="notification_unread_notified_messages_in_room_rooms">
|
||||
<item quantity="zero">"%d ystafelloedd"</item>
|
||||
<item quantity="one">"%d ystafell"</item>
|
||||
<item quantity="two">"%d ystafell"</item>
|
||||
<item quantity="few">"%d ystafell"</item>
|
||||
<item quantity="many">"%d ystafell"</item>
|
||||
<item quantity="other">"%d ystafell"</item>
|
||||
</plurals>
|
||||
<string name="push_distributor_background_sync_android">"Cydweddu\'n y cefndir"</string>
|
||||
<string name="push_distributor_firebase_android">"Gwasanaethau Google"</string>
|
||||
<string name="push_no_valid_google_play_services_apk_android">"Heb ganfod Google Play Services dilys. Efallai fydd hysbysiadau ddim yn gweithio\'n iawn."</string>
|
||||
|
|
@ -84,14 +44,6 @@
|
|||
<string name="troubleshoot_notifications_test_current_push_provider_title">"Darparwr gwthio presennol"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_description">"Gwnewch yn siŵr fod y cais yn cefnogi o leiaf un darparwr gwthio."</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_failure">"Heb ganfod cefnogaeth darparwr gwthio."</string>
|
||||
<plurals name="troubleshoot_notifications_test_detect_push_provider_success">
|
||||
<item quantity="zero">"Wedi canfod %1$d darparwyr gwthio: %2$s"</item>
|
||||
<item quantity="one">"Wedi canfod %1$d darparwr gwthio: %2$s"</item>
|
||||
<item quantity="two">"Wedi canfod %1$d darparwr gwthio: %2$s"</item>
|
||||
<item quantity="few">"Wedi canfod %1$d darparwr gwthio: %2$s"</item>
|
||||
<item quantity="many">"Wedi canfod %1$d darparwr gwthio: %2$s"</item>
|
||||
<item quantity="other">"Wedi canfod %1$d darparwr gwthio: %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_success_2">"Adeiladwyd y rhaglen gyda chefnogaeth ar gyfer: %1$s"</string>
|
||||
<string name="troubleshoot_notifications_test_detect_push_provider_title">"Cefnogaeth darparwr gwthio"</string>
|
||||
<string name="troubleshoot_notifications_test_display_notification_description">"Gwiriwch y gall y cais ddangos hysbysiad."</string>
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@ class NotificationBroadcastReceiverHandlerTest {
|
|||
roomId = A_ROOM_ID,
|
||||
),
|
||||
)
|
||||
runCurrent()
|
||||
advanceUntilIdle()
|
||||
sendMessage.assertions()
|
||||
.isCalledOnce()
|
||||
.with(value(A_MESSAGE), value(null), value(emptyList<IntentionalMention>()))
|
||||
|
|
|
|||
|
|
@ -2,13 +2,5 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="troubleshoot_notifications_test_unified_push_description">"Sicrhewch fod dosbarthwyr UnifiedPush ar gael."</string>
|
||||
<string name="troubleshoot_notifications_test_unified_push_failure">"Heb ganfod dosbarthwyr gwthio."</string>
|
||||
<plurals name="troubleshoot_notifications_test_unified_push_success">
|
||||
<item quantity="zero">"Wedi canfod %1$d dosbarthwyr: %2$s"</item>
|
||||
<item quantity="one">"Wedi canfod %1$d dosbarthwr: %2$s"</item>
|
||||
<item quantity="two">"Wedi canfod %1$d dosbarthwr: %2$s"</item>
|
||||
<item quantity="few">"Wedi canfod %1$d dosbarthwr: %2$s"</item>
|
||||
<item quantity="many">"Wedi canfod %1$d dosbarthwr: %2$s"</item>
|
||||
<item quantity="other">"Wedi canfod %1$d dosbarthwr: %2$s"</item>
|
||||
</plurals>
|
||||
<string name="troubleshoot_notifications_test_unified_push_title">"Gwiriwch UnifiedPush"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ import kotlin.coroutines.suspendCoroutine
|
|||
@Composable
|
||||
fun QrCodeCameraView(
|
||||
onScanQrCode: (ByteArray) -> Unit,
|
||||
renderPreview: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
renderPreview: Boolean = true,
|
||||
) {
|
||||
if (LocalInspectionMode.current) {
|
||||
Box(
|
||||
|
|
@ -62,9 +62,11 @@ fun QrCodeCameraView(
|
|||
var cameraProvider by remember { mutableStateOf<ProcessCameraProvider?>(null) }
|
||||
val previewUseCase = remember { Preview.Builder().build() }
|
||||
var lastFrame by remember { mutableStateOf<Bitmap?>(null) }
|
||||
val imageAnalysis = ImageAnalysis.Builder()
|
||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
.build()
|
||||
val imageAnalysis = remember {
|
||||
ImageAnalysis.Builder()
|
||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
.build()
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
cameraProvider = localContext.getCameraProvider()
|
||||
|
|
|
|||
|
|
@ -172,6 +172,11 @@
|
|||
<string name="common_light">"Светлая"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Спасылка скапіравана ў буфер абмену"</string>
|
||||
<string name="common_loading">"Загрузка…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d іншы"</item>
|
||||
<item quantity="few">"%d іншыя"</item>
|
||||
<item quantity="many">"%d іншых"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d удзельнік"</item>
|
||||
<item quantity="few">"%1$d удзельнікі"</item>
|
||||
|
|
|
|||
|
|
@ -125,6 +125,10 @@
|
|||
<string name="common_light">"Светъл"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Връзката е копирана в клипборда"</string>
|
||||
<string name="common_loading">"Зарежда се…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d друг"</item>
|
||||
<item quantity="other">"%d други"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d член"</item>
|
||||
<item quantity="other">"%1$d членове"</item>
|
||||
|
|
|
|||
|
|
@ -200,6 +200,11 @@ Důvod: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Odkaz zkopírován do schránky"</string>
|
||||
<string name="common_loading">"Načítání…"</string>
|
||||
<string name="common_loading_more">"Načítání dalších…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d další"</item>
|
||||
<item quantity="few">"%d další"</item>
|
||||
<item quantity="other">"%d dalších"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d člen"</item>
|
||||
<item quantity="few">"%1$d členové"</item>
|
||||
|
|
@ -343,6 +348,23 @@ Opravdu chcete pokračovat?"</string>
|
|||
<string name="invite_friends_text">"Ahoj, ozvi se mi na %1$s: %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<string name="preference_rageshake">"Zatřeste zařízením pro nahlášení chyby"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban">"Odebrat a vykázat člena"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Vykázat"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Nebudou se moci znovu připojit k této místnosti, pokud budou pozváni."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Jste si jisti, že chcete vykázat tohoto člena?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Vykazování %1$s"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Odebrat"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Budou moci znovu vstoupit do této místnosti, pokud budou pozváni."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Opravdu chcete tohoto člena odebrat?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Zobrazit profil"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove">"Odebrat z místnosti"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Odebrat člena a zakázat mu připojení v budoucnu?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Odstraňování %1$s…"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban">"Zrušit vykázání z místnosti"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Zrušit vykázání"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Pokud by byli pozváni, mohli by se znovu připojit do místnosti"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Opravdu chcete zrušit vykázání tohoto člena?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Rušení vykázání %1$s"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Výběr média se nezdařil, zkuste to prosím znovu."</string>
|
||||
<string name="screen_media_upload_preview_caption_warning">"Titulky nemusí být viditelné pro lidi, kteří používají starší aplikace."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Nahrání média se nezdařilo, zkuste to prosím znovu."</string>
|
||||
|
|
|
|||
|
|
@ -2,14 +2,6 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="a11y_avatar">"Afatar"</string>
|
||||
<string name="a11y_delete">"Dileu"</string>
|
||||
<plurals name="a11y_digits_entered">
|
||||
<item quantity="zero">"%1$d nodau wedi eu cynnig"</item>
|
||||
<item quantity="one">"%1$d nod wedi ei gynnig"</item>
|
||||
<item quantity="two">"%1$d nod wedi eu cynnig"</item>
|
||||
<item quantity="few">"%1$d nod wedi eu cynnig"</item>
|
||||
<item quantity="many">"%1$d nod wedi eu cynnig"</item>
|
||||
<item quantity="other">"%1$d nod wedi eu cynnig"</item>
|
||||
</plurals>
|
||||
<string name="a11y_hide_password">"Cuddio cyfrinair"</string>
|
||||
<string name="a11y_join_call">"Ymuno â galwad"</string>
|
||||
<string name="a11y_jump_to_bottom">"Symud i\'r gwaelod"</string>
|
||||
|
|
@ -25,14 +17,6 @@
|
|||
<string name="a11y_react_with">"Ymateb gyda %1$s"</string>
|
||||
<string name="a11y_react_with_other_emojis">"Ymateb gydag emojis eraill"</string>
|
||||
<string name="a11y_read_receipts_multiple">"Wedi\'i ddarllen gan %1$s a %2$s"</string>
|
||||
<plurals name="a11y_read_receipts_multiple_with_others">
|
||||
<item quantity="zero">"Wedi\'i ddarllen gan %1$s a %2$d arall"</item>
|
||||
<item quantity="one">"Wedi\'i ddarllen gan %1$s a %2$d arall"</item>
|
||||
<item quantity="two">"Wedi\'i ddarllen gan %1$s a %2$d arall"</item>
|
||||
<item quantity="few">"Wedi\'i ddarllen gan %1$s a %2$d arall"</item>
|
||||
<item quantity="many">"Wedi\'i ddarllen gan %1$s a %2$d arall"</item>
|
||||
<item quantity="other">"Wedi\'i ddarllen gan %1$s a %2$d arall"</item>
|
||||
</plurals>
|
||||
<string name="a11y_read_receipts_single">"Wedi\'i ddarllen gan %1$s"</string>
|
||||
<string name="a11y_read_receipts_tap_to_show_all">"Tapio i ddangos y cyfan"</string>
|
||||
<string name="a11y_remove_reaction_with">"Wedi dileu adwaith gyda %1$s"</string>
|
||||
|
|
@ -206,14 +190,6 @@ Rheswm: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Dolen wedi\'i chopïo i\'r clipfwrdd"</string>
|
||||
<string name="common_loading">"Yn Llwytho…"</string>
|
||||
<string name="common_loading_more">"Wrthi\'n llwytho mwy…"</string>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="zero">"%1$d aelodau"</item>
|
||||
<item quantity="one">"%1$d aelod"</item>
|
||||
<item quantity="two">"%1$d aelod"</item>
|
||||
<item quantity="few">"%1$d aelod"</item>
|
||||
<item quantity="many">"%1$d aelod"</item>
|
||||
<item quantity="other">"%1$d aelod"</item>
|
||||
</plurals>
|
||||
<string name="common_message">"Neges"</string>
|
||||
<string name="common_message_actions">"Gweithredoedd neges"</string>
|
||||
<string name="common_message_layout">"Cynllun y neges"</string>
|
||||
|
|
@ -238,14 +214,6 @@ Rheswm: %1$s."</string>
|
|||
<string name="common_poll_summary">"Pleidlais: %1$s"</string>
|
||||
<string name="common_poll_total_votes">"Cyfanswm y pleidleisiau: %1$s"</string>
|
||||
<string name="common_poll_undisclosed_text">"Bydd y canlyniadau\'n dangos ar ôl i\'r bleidlais ddod i ben"</string>
|
||||
<plurals name="common_poll_votes_count">
|
||||
<item quantity="zero">"%d pleidleisiau"</item>
|
||||
<item quantity="one">"%d pleidlais"</item>
|
||||
<item quantity="two">"%d pleidlais"</item>
|
||||
<item quantity="few">"%d pleidlais"</item>
|
||||
<item quantity="many">"%d pleidlais"</item>
|
||||
<item quantity="other">"%d pleidlais"</item>
|
||||
</plurals>
|
||||
<string name="common_privacy_policy">"Polisi preifatrwydd"</string>
|
||||
<string name="common_reaction">"Adwaith"</string>
|
||||
<string name="common_reactions">"Adweithiau"</string>
|
||||
|
|
@ -355,20 +323,29 @@ Ydych chi\'n siŵr eich bod am barhau?"</string>
|
|||
<string name="invite_friends_text">"Hei, siaradwch â mi ar %1$s: %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"Android %1$s"</string>
|
||||
<string name="preference_rageshake">"Rageshake i adrodd gwall"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban">"Gwahardd o ystafell"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Atal"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Fyddan nhw ddim yn gallu ymuno â\'r ystafell hon eto os cân nhw wahoddiad."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Ydych chi\'n siŵr eich bod am wahardd yr aelod hwn?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Yn gwahardd %1$s"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Tynnu"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Fyddan nhw yn gallu ymuno â\'r ystafell hon eto os cân nhw wahoddiad."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Ydych chi\'n siŵr eich bod am ddileu\'r aelod hwn?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Gweld proffil"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove">"Tynnu o\'r ystafell"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Dileu aelod a\'u gwahardd rhag ymuno yn y dyfodol?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Wrthi\'n dileu %1$s…"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban">"Dad-wahardd o\'r ystafell"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Dad-wahardd"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Bydden nhw\'n gallu ymuno â\'r ystafell eto os fydd rhywun yn eu gwahodd"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Ydych chi\'n siŵr eich bod chi eisiau dadwahardd yr aelod hwn?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Dad-wahardd %1$s"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Wedi methu dewis cyfrwng, ceisiwch eto."</string>
|
||||
<string name="screen_media_upload_preview_caption_warning">"Efallai na fydd capsiynau yn weladwy i bobl sy\'n defnyddio apiau hŷn."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Wedi methu â phrosesu cyfryngau i\'w llwytho, ceisiwch eto."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Wedi methu llwytho cyfryngau, ceisiwch eto."</string>
|
||||
<string name="screen_pinned_timeline_empty_state_description">"Pwyswch ar neges a dewis “%1$s” i\'w cynnwys yma."</string>
|
||||
<string name="screen_pinned_timeline_empty_state_headline">"Pinio negeseuon pwysig fel y mae modd eu darganfod yn hawdd"</string>
|
||||
<plurals name="screen_pinned_timeline_screen_title">
|
||||
<item quantity="zero">"%1$d Negeseuon wedi\'u pinio"</item>
|
||||
<item quantity="one">"%1$d Neges wedi\'i phinio"</item>
|
||||
<item quantity="two">"%1$d Neges wedi\'i phinio"</item>
|
||||
<item quantity="few">"%1$d Neges wedi\'i phinio"</item>
|
||||
<item quantity="many">"%1$d Neges wedi\'i phinio"</item>
|
||||
<item quantity="other">"%1$d Neges wedi\'i phinio"</item>
|
||||
</plurals>
|
||||
<string name="screen_pinned_timeline_screen_title_empty">"Negeseuon wedi\'u pinio"</string>
|
||||
<string name="screen_reset_identity_confirmation_subtitle">"Rydych chi ar fin mynd i\'ch cyfrif %1$s i ailosod eich hunaniaeth. Wedi hynny byddwch yn cael eich tywys yn ôl i\'r ap."</string>
|
||||
<string name="screen_reset_identity_confirmation_title">"Methu cadarnhau? Ewch i\'ch cyfrif i ailosod eich hunaniaeth."</string>
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@ Grund: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Link in die Zwischenablage kopiert"</string>
|
||||
<string name="common_loading">"Laden…"</string>
|
||||
<string name="common_loading_more">"Mehr wird geladen…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d weitere"</item>
|
||||
<item quantity="other">"%d weitere"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d Mitglied"</item>
|
||||
<item quantity="other">"%1$d Mitglieder"</item>
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@
|
|||
<string name="common_link_copied_to_clipboard">"Ο σύνδεσμος αντιγράφηκε στο πρόχειρο"</string>
|
||||
<string name="common_loading">"Φόρτωση…"</string>
|
||||
<string name="common_loading_more">"Φόρτωση περισσότερων…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d ακόμη"</item>
|
||||
<item quantity="other">"%d ακόμη"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d μέλος"</item>
|
||||
<item quantity="other">"%1$d μέλη"</item>
|
||||
|
|
|
|||
|
|
@ -185,6 +185,10 @@ Motivo: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Enlace copiado al portapapeles"</string>
|
||||
<string name="common_loading">"Cargando…"</string>
|
||||
<string name="common_loading_more">"Cargando más…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d otro"</item>
|
||||
<item quantity="other">"%d otros"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d miembro"</item>
|
||||
<item quantity="other">"%1$d miembros"</item>
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@ Põhjus: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Link on kopeeritud lõikelauale"</string>
|
||||
<string name="common_loading">"Laadime…"</string>
|
||||
<string name="common_loading_more">"Laadime veel…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d muu"</item>
|
||||
<item quantity="other">"%d muud"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d liige"</item>
|
||||
<item quantity="other">"%1$d liiget"</item>
|
||||
|
|
@ -339,6 +343,23 @@ Kas sa oled kindel, et soovid jätkata?"</string>
|
|||
<string name="invite_friends_text">"Hei, suhtle minuga %1$s võrgus: %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<string name="preference_rageshake">"Veast teatamiseks raputa nutiseadet ägedalt"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban">"Eemalda ja sea suhtluskeeld"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Sea suhtluskeeld"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Ta ei saa selle jututoaga liituda isegi kutse olemasolul."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Kas sa oled kindel, et soovid sellele kasutajale seada suhtluskeelu?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Seame kasutajale %1$s suhtluskeelu"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Eemalda"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Uue kutse saamisel on tal võimalik selle jututoaga uuesti liituda."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Kas sa oled kindel, et soovid selle osaleja eemaldada?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Vaata profiili"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove">"Eemalda kasutaja jututoast"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Kas eemaldama kasutaja ja seame talle tulevikuks suhtluskeelu?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Eemaldame kasutajat %1$s…"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban">"Eemalda suhtluskeeld jututoas"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Eemalda suhtluskeeld"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Ta võib kutse saamisel liituda jututoaga uuesti"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Kas oled kindel, et soovid selle liikme suhtluskeelu eemaldada?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Eemaldame suhtluskeelu kasutajalt %1$s"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Meediafaili valimine ei õnnestunud. Palun proovi uuesti."</string>
|
||||
<string name="screen_media_upload_preview_caption_warning">"Selgitused ja alapealkirjad ei pruugi olla nähtavad vanemate rakenduste kasutajatele."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Meediafaili töötlemine enne üleslaadimist ei õnnestunud. Palun proovi uuesti."</string>
|
||||
|
|
|
|||
|
|
@ -188,6 +188,10 @@ Arrazoia: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Esteka arbelean kopiatu da"</string>
|
||||
<string name="common_loading">"Kargatzen…"</string>
|
||||
<string name="common_loading_more">"Gehiago kargatzen…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"beste %d"</item>
|
||||
<item quantity="other">"beste %d"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"Kide %1$d"</item>
|
||||
<item quantity="other">"%1$d kide"</item>
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@ Syy: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Linkki kopioitu leikepöydälle"</string>
|
||||
<string name="common_loading">"Ladataan…"</string>
|
||||
<string name="common_loading_more">"Ladataan lisää…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d muu"</item>
|
||||
<item quantity="other">"%d muuta"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d jäsen"</item>
|
||||
<item quantity="other">"%1$d jäsentä"</item>
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@ Raison : %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Lien copié dans le presse-papiers"</string>
|
||||
<string name="common_loading">"Chargement…"</string>
|
||||
<string name="common_loading_more">"Chargement…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d autre"</item>
|
||||
<item quantity="other">"%d autres"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d membre"</item>
|
||||
<item quantity="other">"%1$d membres"</item>
|
||||
|
|
@ -339,6 +343,23 @@ Raison : %1$s."</string>
|
|||
<string name="invite_friends_text">"Salut, parle-moi sur %1$s : %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<string name="preference_rageshake">"Rageshake pour signaler un problème"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban">"Retirer et bannir ce membre"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Bannir"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Il ne pourra pas rejoindre le salon à nouveau, même si il est invité."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Êtes-vous certain de vouloir bannir ce membre ?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Bannissement de %1$s"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Retirer"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Cet utilisateur pourra rejoindre le salon à nouveau si il est invité."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Voulez-vous vraiment supprimer ce membre ?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Voir le profil"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove">"Retirer le membre du salon"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Retirer le membre et interdire l’adhésion à l’avenir ?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Enlever %1$s…"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban">"Débannir du salon"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Débannir"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"L’utilisateur pourra à nouveau rejoindre le salon s’il est invité."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Êtes-vous sûr de vouloir débannir cet utilisateur?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Débannissement de %1$s"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Échec de la sélection du média, veuillez réessayer."</string>
|
||||
<string name="screen_media_upload_preview_caption_warning">"Les légendes peuvent ne pas être visibles pour les utilisateurs d’anciennes applications."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Échec du traitement des médias à télécharger, veuillez réessayer."</string>
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@ Ok: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Hivatkozás a vágólapra másolva"</string>
|
||||
<string name="common_loading">"Betöltés…"</string>
|
||||
<string name="common_loading_more">"Továbbiak betöltése…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d további felhasználó"</item>
|
||||
<item quantity="other">"%d további felhasználó"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d tag"</item>
|
||||
<item quantity="other">"%1$d tag"</item>
|
||||
|
|
@ -339,6 +343,23 @@ Biztos, hogy folytatja?"</string>
|
|||
<string name="invite_friends_text">"Beszélgessünk itt: %1$s, %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<string name="preference_rageshake">"Az eszköz rázása a hibajelentéshez"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban">"Eltávolítás és a tag kitiltása"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Kitiltás"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Többé nem csatlakozhat ehhez a szobához, akkor sem, ha meghívják."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Biztos, hogy kitiltja ezt a tagot?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_banning_user">"%1$s kitiltása"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Eltávolítás"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"Ehhez a szobához is csatlakozhat, ha meghívják."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Biztos, hogy eltávolítja ezt a tagot?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Profil megtekintése"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove">"Eltávolítás a szobából"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Eltávolítja a tagot, és megtiltja a jövőbeni csatlakozást?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_removing_user">"%1$s eltávolítása…"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban">"Visszaengedés a szobába"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Kitiltás visszavonása"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"Újra beléphetnek a szobába, ha meghívják őket."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Biztos, hogy feloldja a felhasználó kitiltását?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"%1$s kitiltásának feloldása"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Nem sikerült kiválasztani a médiát, próbálja újra."</string>
|
||||
<string name="screen_media_upload_preview_caption_warning">"Előfordulhat, hogy a feliratok nem láthatók a régebbi alkalmazásokat használók számára."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra."</string>
|
||||
|
|
|
|||
|
|
@ -181,6 +181,9 @@ Alasan: %1$s."</string>
|
|||
<string name="common_light">"Terang"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Tautan disalin ke papan klip"</string>
|
||||
<string name="common_loading">"Memuat…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="other">"%d lainnya"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="other">"%1$d anggota"</item>
|
||||
</plurals>
|
||||
|
|
|
|||
|
|
@ -190,6 +190,10 @@ Motivo:. %1$s"</string>
|
|||
<string name="common_link_copied_to_clipboard">"Collegamento copiato negli appunti"</string>
|
||||
<string name="common_loading">"Caricamento…"</string>
|
||||
<string name="common_loading_more">"Caricamento in corso…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d altro"</item>
|
||||
<item quantity="other">"altri %d"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d membro"</item>
|
||||
<item quantity="other">"%1$d membri"</item>
|
||||
|
|
|
|||
|
|
@ -147,6 +147,10 @@
|
|||
<string name="common_light">"ღია"</string>
|
||||
<string name="common_link_copied_to_clipboard">"ბმული კოპირებულია გაცვლის ბუფერში"</string>
|
||||
<string name="common_loading">"იტვირთება…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d სხვა"</item>
|
||||
<item quantity="other">"%d სხვა"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d წევრი"</item>
|
||||
<item quantity="other">"%1$d წევრები"</item>
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@
|
|||
<string name="common_link_copied_to_clipboard">"Lenke kopiert til utklippstavlen"</string>
|
||||
<string name="common_loading">"Laster inn…"</string>
|
||||
<string name="common_loading_more">"Laster inn mer…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d andre"</item>
|
||||
<item quantity="other">"%d andre"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d medlem"</item>
|
||||
<item quantity="other">"%1$d medlemmer"</item>
|
||||
|
|
@ -339,6 +343,23 @@ Er du sikker på at du vil fortsette?"</string>
|
|||
<string name="invite_friends_text">"Hei, snakk med meg på %1$s: %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<string name="preference_rageshake">"Rageshake for å rapportere feil"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban">"Fjern og utesteng medlem"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Utesteng"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"De vil ikke kunne bli med i dette rommet igjen hvis de blir invitert."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Er du sikker på at du vil utestenge dette medlemmet?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Utestenger %1$s"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Fjern"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"De vil kunne bli med i dette rommet igjen hvis de blir invitert."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Er du sikker på at du vil fjerne dette medlemmet?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Vis profil"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove">"Fjern fra rommet"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Fjerne medlem og utestenge fra å bli med i fremtiden?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Fjerner %1$s…"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban">"Fjern utestengelsen fra rommet"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Opphev utestengelsen"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"De vil kunne bli med i rommet igjen hvis de blir invitert"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Er du sikker på at du vil oppheve utestengelsen av dette medlemmet?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Oppheve utestengelsen av %1$s"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Kunne ikke velge medium, prøv igjen."</string>
|
||||
<string name="screen_media_upload_preview_caption_warning">"Teksting er kanskje ikke synlig for personer som bruker eldre apper."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Kunne ikke behandle medier for opplasting, vennligst prøv igjen."</string>
|
||||
|
|
|
|||
|
|
@ -174,6 +174,10 @@ Reden: %1$s."</string>
|
|||
<string name="common_light">"Licht"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Link gekopieerd naar klembord"</string>
|
||||
<string name="common_loading">"Laden…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d andere"</item>
|
||||
<item quantity="other">"%d anderen"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d lid"</item>
|
||||
<item quantity="other">"%1$d leden"</item>
|
||||
|
|
|
|||
|
|
@ -200,6 +200,11 @@ Powód: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Link został skopiowany do schowka"</string>
|
||||
<string name="common_loading">"Ładowanie…"</string>
|
||||
<string name="common_loading_more">"Ładuję więcej…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d inny"</item>
|
||||
<item quantity="few">"%d innych"</item>
|
||||
<item quantity="many">"%d innych"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d członek"</item>
|
||||
<item quantity="few">"%1$d członki"</item>
|
||||
|
|
|
|||
|
|
@ -178,6 +178,10 @@
|
|||
<string name="common_light">"Claro"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Link copiado para área de transferência"</string>
|
||||
<string name="common_loading">"Carregando…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d outro"</item>
|
||||
<item quantity="other">"%d outros"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d membro"</item>
|
||||
<item quantity="other">"%1$d membros"</item>
|
||||
|
|
|
|||
|
|
@ -189,6 +189,10 @@ Razão: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Ligação copiada para a área de transferência"</string>
|
||||
<string name="common_loading">"A carregar…"</string>
|
||||
<string name="common_loading_more">"A carregar mais…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d outro"</item>
|
||||
<item quantity="other">"%d outros"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d membro"</item>
|
||||
<item quantity="other">"%1$d membros"</item>
|
||||
|
|
|
|||
|
|
@ -171,6 +171,11 @@ Motiv:%1$s."</string>
|
|||
<string name="common_light">"Deschis"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Linkul a fost copiat în clipboard"</string>
|
||||
<string name="common_loading">"Se încarcă…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d altul"</item>
|
||||
<item quantity="few">"%d alții"</item>
|
||||
<item quantity="other">"%d alții"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d membru"</item>
|
||||
<item quantity="few">"%1$d membri"</item>
|
||||
|
|
|
|||
|
|
@ -200,6 +200,11 @@
|
|||
<string name="common_link_copied_to_clipboard">"Ссылка скопирована в буфер обмена"</string>
|
||||
<string name="common_loading">"Загрузка…"</string>
|
||||
<string name="common_loading_more">"Загрузить больше…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d"</item>
|
||||
<item quantity="few">"%d другие"</item>
|
||||
<item quantity="many">"%d другие"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d участник"</item>
|
||||
<item quantity="few">"%1$d участников"</item>
|
||||
|
|
|
|||
|
|
@ -200,6 +200,11 @@ Dôvod: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Odkaz bol skopírovaný do schránky"</string>
|
||||
<string name="common_loading">"Načítava sa…"</string>
|
||||
<string name="common_loading_more">"Načítava sa viac…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d ďalší"</item>
|
||||
<item quantity="few">"%d ďalší"</item>
|
||||
<item quantity="other">"%d ďalších"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d člen"</item>
|
||||
<item quantity="few">"%1$d členovia"</item>
|
||||
|
|
@ -343,6 +348,23 @@ Naozaj chcete pokračovať?"</string>
|
|||
<string name="invite_friends_text">"Ahoj, porozprávajte sa so mnou na %1$s: %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<string name="preference_rageshake">"Zúrivo potriasť pre nahlásenie chyby"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban">"Odstrániť a zakázať člena"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Zakázať"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"Nebudú sa môcť pripojiť k tejto miestnosti znova ani ak budú pozvaní."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Ste si istý, že chcete zakázať tohto člena?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Zakazuje sa %1$s"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Odstrániť"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"V prípade pozvania sa budú môcť znova pripojiť k tejto miestnosti."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Ste si istý, že chcete odstrániť tohto člena?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"Zobraziť profil"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove">"Odstrániť z miestnosti"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Odstrániť člena a zakázať vstup v budúcnosti?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Odstraňuje sa %1$s…"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban">"Zrušiť zákaz prístupu do miestnosti"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Zrušiť zákaz"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"V prípade pozvania by sa mohli opäť pripojiť k miestnosti"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Naozaj chcete zrušiť zablokovanie tohto člena?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Zrušenie zákazu pre %1$s"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Nepodarilo sa vybrať médium, skúste to prosím znova."</string>
|
||||
<string name="screen_media_upload_preview_caption_warning">"Titulky nemusia byť viditeľné pre ľudí používajúcich staršie aplikácie."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova."</string>
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@ Anledning:%1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Länk kopierad till klippbordet"</string>
|
||||
<string name="common_loading">"Laddar …"</string>
|
||||
<string name="common_loading_more">"Laddar mer …"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d annan"</item>
|
||||
<item quantity="other">"%d andra"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d medlem"</item>
|
||||
<item quantity="other">"%1$d medlemmar"</item>
|
||||
|
|
|
|||
|
|
@ -185,6 +185,10 @@ Neden: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Bağlantı panoya kopyalandı"</string>
|
||||
<string name="common_loading">"Yükleniyor…"</string>
|
||||
<string name="common_loading_more">"Daha fazla yükleniyor…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d diğer"</item>
|
||||
<item quantity="other">"%d diğer"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d üye"</item>
|
||||
<item quantity="other">"%1$d üye"</item>
|
||||
|
|
|
|||
|
|
@ -200,6 +200,11 @@
|
|||
<string name="common_link_copied_to_clipboard">"Посилання скопійовано в буфер обміну"</string>
|
||||
<string name="common_loading">"Завантаження"</string>
|
||||
<string name="common_loading_more">"Завантаження наступних…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d інший"</item>
|
||||
<item quantity="few">"%d інші"</item>
|
||||
<item quantity="many">"%d інші"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d учасник"</item>
|
||||
<item quantity="few">"%1$d учасники"</item>
|
||||
|
|
|
|||
|
|
@ -195,6 +195,9 @@
|
|||
<string name="common_link_copied_to_clipboard">"連結已複製到剪貼簿"</string>
|
||||
<string name="common_loading">"載入中…"</string>
|
||||
<string name="common_loading_more">"載入更多……"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="other">"其他 %d 個人"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="other">"%1$d 位成員"</item>
|
||||
</plurals>
|
||||
|
|
|
|||
|
|
@ -192,6 +192,9 @@
|
|||
<string name="common_link_copied_to_clipboard">"链接已复制到剪贴板"</string>
|
||||
<string name="common_loading">"正在加载…"</string>
|
||||
<string name="common_loading_more">"正在加载更多……"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="other">"其他 %d 人"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="other">"%1$d个成员"</item>
|
||||
</plurals>
|
||||
|
|
|
|||
|
|
@ -198,6 +198,10 @@ Reason: %1$s."</string>
|
|||
<string name="common_link_copied_to_clipboard">"Link copied to clipboard"</string>
|
||||
<string name="common_loading">"Loading…"</string>
|
||||
<string name="common_loading_more">"Loading more…"</string>
|
||||
<plurals name="common_many_members">
|
||||
<item quantity="one">"%d other"</item>
|
||||
<item quantity="other">"%d others"</item>
|
||||
</plurals>
|
||||
<plurals name="common_member_count">
|
||||
<item quantity="one">"%1$d member"</item>
|
||||
<item quantity="other">"%1$d members"</item>
|
||||
|
|
@ -339,6 +343,23 @@ Are you sure you want to continue?"</string>
|
|||
<string name="invite_friends_text">"Hey, talk to me on %1$s: %2$s"</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<string name="preference_rageshake">"Rageshake to report bug"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban">"Ban from room"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_action">"Ban"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_description">"They won’t be able to join this room again if invited."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_ban_member_confirmation_title">"Are you sure you want to ban this member?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_banning_user">"Banning %1$s"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_action">"Remove"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_description">"They will be able to join this room again if invited."</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_kick_member_confirmation_title">"Are you sure you want to remove this member?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_member_user_info">"View profile"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove">"Remove from room"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_remove_confirmation_title">"Remove member and ban from joining in the future?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_removing_user">"Removing %1$s…"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban">"Unban from room"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_action">"Unban"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_description">"They would be able to join the room again if invited"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unban_member_confirmation_title">"Are you sure you want to unban this member?"</string>
|
||||
<string name="screen_bottom_sheet_manage_room_member_unbanning_user">"Unbanning %1$s"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Failed selecting media, please try again."</string>
|
||||
<string name="screen_media_upload_preview_caption_warning">"Captions might not be visible to people using older apps."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Failed processing media to upload, please try again."</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue