Merge branch 'develop' into feature/bma/scroll-to-unread-messages

This commit is contained in:
Benoit Marty
2026-06-08 17:30:11 +02:00
278 changed files with 2880 additions and 1792 deletions
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.androidutils.system
import android.content.Context
import dev.zacsweers.metro.Inject
import io.element.android.libraries.di.annotations.ApplicationContext
private const val VULKAN_VERSION_1_0 = 0x400003
/**
* Checks if the device supports Vulkan 1.0.
*
* This is needed for the location screens that contain maps using MapLibre UI components.
*
* Needed until https://github.com/maplibre/maplibre-native/issues/3079 is resolved and we can automatically choose between OpenGL and Vulkan renderers,
* or no devices support OpenGL anymore.
*/
@Inject
class DeviceHasVulkanSupport(
@ApplicationContext private val context: Context,
) {
operator fun invoke(): Boolean {
return context.packageManager.hasSystemFeature("android.hardware.vulkan.version", VULKAN_VERSION_1_0)
}
}
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7901fea2f578c8ed796160c9c08f417c61f4fb21580f958844fdf0cb794adf8a
size 239731
oid sha256:78386a58e91be3213485f60909ec8d83cd535aabcec775aadf7b36e77d8542f0
size 245853
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:245f012d419817f6557d92a71729b3b70092f24f0eba37f2f1fc431ad27592be
size 252969
oid sha256:477ab18ffa1ca81ca5e4c9f30f4466b19e14fcaddf641fc860c6be19a0e33d4f
size 259185
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2d15c52b21cc279d306fa187cd0c318820109b5ec66270e6447e1b02e800eeba
size 254206
oid sha256:7edeebf9876aecd262fceb7d55a4bfd30a859a69b708af8dd4538ffc0f9be419
size 260400
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:85ef188fa3a27e42f4beafc899c1f3e7e8bcfad980ed76af6a03f76d70d6a511
size 93807
oid sha256:cc6cc2d2e89c7460217c2075046e41bef727407ee422dcdea23f161f48441904
size 97346
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f6e38386e95dc0c50384f06fca122ce14851ceff8ffc7865e394c1b4fccc5db6
size 100555
oid sha256:4df16aee4a3a8b10c33e9724904c72b8dc502725204d2f86cf62cc381b35f4a1
size 103870
File diff suppressed because one or more lines are too long
@@ -130,6 +130,9 @@ object CompoundIcons {
@Composable fun Collapse(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_collapse)
}
@Composable fun CollapseAll(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_collapse_all)
}
@Composable fun Company(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_company)
}
@@ -142,6 +145,9 @@ object CompoundIcons {
@Composable fun Copy(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_copy)
}
@Composable fun Crop(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_crop)
}
@Composable fun DarkMode(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_dark_mode)
}
@@ -202,6 +208,9 @@ object CompoundIcons {
@Composable fun Expand(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_expand)
}
@Composable fun ExpandAll(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_expand_all)
}
@Composable fun Explore(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_explore)
}
@@ -229,6 +238,15 @@ object CompoundIcons {
@Composable fun Filter(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_filter)
}
@Composable fun FlipHorizontal(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_flip_horizontal)
}
@Composable fun FlipVertical(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_flip_vertical)
}
@Composable fun Folder(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_folder)
}
@Composable fun Forward(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_forward)
}
@@ -496,6 +514,12 @@ object CompoundIcons {
@Composable fun RotateRight(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_rotate_right)
}
@Composable fun Save(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_save)
}
@Composable fun SaveSolid(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_save_solid)
}
@Composable fun Search(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_search)
}
@@ -607,6 +631,9 @@ object CompoundIcons {
@Composable fun Unpin(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_unpin)
}
@Composable fun Unsave(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_unsave)
}
@Composable fun User(): ImageVector {
return ImageVector.vectorResource(R.drawable.ic_compound_user)
}
@@ -735,10 +762,12 @@ object CompoundIcons {
CloudSolid(),
Code(),
Collapse(),
CollapseAll(),
Company(),
Compose(),
Computer(),
Copy(),
Crop(),
DarkMode(),
Delete(),
DevicePasskey(),
@@ -759,6 +788,7 @@ object CompoundIcons {
ErrorSolid(),
ExitFullScreen(),
Expand(),
ExpandAll(),
Explore(),
ExportArchive(),
Extensions(),
@@ -768,6 +798,9 @@ object CompoundIcons {
FileError(),
Files(),
Filter(),
FlipHorizontal(),
FlipVertical(),
Folder(),
Forward(),
FullScreen(),
Grid(),
@@ -857,6 +890,8 @@ object CompoundIcons {
Room(),
RotateLeft(),
RotateRight(),
Save(),
SaveSolid(),
Search(),
Section(),
Send(),
@@ -894,6 +929,7 @@ object CompoundIcons {
Unknown(),
UnknownSolid(),
Unpin(),
Unsave(),
User(),
UserAdd(),
UserAddSolid(),
@@ -963,10 +999,12 @@ object CompoundIcons {
R.drawable.ic_compound_cloud_solid,
R.drawable.ic_compound_code,
R.drawable.ic_compound_collapse,
R.drawable.ic_compound_collapse_all,
R.drawable.ic_compound_company,
R.drawable.ic_compound_compose,
R.drawable.ic_compound_computer,
R.drawable.ic_compound_copy,
R.drawable.ic_compound_crop,
R.drawable.ic_compound_dark_mode,
R.drawable.ic_compound_delete,
R.drawable.ic_compound_device_passkey,
@@ -987,6 +1025,7 @@ object CompoundIcons {
R.drawable.ic_compound_error_solid,
R.drawable.ic_compound_exit_full_screen,
R.drawable.ic_compound_expand,
R.drawable.ic_compound_expand_all,
R.drawable.ic_compound_explore,
R.drawable.ic_compound_export_archive,
R.drawable.ic_compound_extensions,
@@ -996,6 +1035,9 @@ object CompoundIcons {
R.drawable.ic_compound_file_error,
R.drawable.ic_compound_files,
R.drawable.ic_compound_filter,
R.drawable.ic_compound_flip_horizontal,
R.drawable.ic_compound_flip_vertical,
R.drawable.ic_compound_folder,
R.drawable.ic_compound_forward,
R.drawable.ic_compound_full_screen,
R.drawable.ic_compound_grid,
@@ -1085,6 +1127,8 @@ object CompoundIcons {
R.drawable.ic_compound_room,
R.drawable.ic_compound_rotate_left,
R.drawable.ic_compound_rotate_right,
R.drawable.ic_compound_save,
R.drawable.ic_compound_save_solid,
R.drawable.ic_compound_search,
R.drawable.ic_compound_section,
R.drawable.ic_compound_send,
@@ -1122,6 +1166,7 @@ object CompoundIcons {
R.drawable.ic_compound_unknown,
R.drawable.ic_compound_unknown_solid,
R.drawable.ic_compound_unpin,
R.drawable.ic_compound_unsave,
R.drawable.ic_compound_user,
R.drawable.ic_compound_user_add,
R.drawable.ic_compound_user_add_solid,
@@ -175,6 +175,8 @@ data class SemanticColors(
val iconTertiaryAlpha: Color,
/** Used to separate core sections of the UI as well as containers */
val separatorPrimary: Color,
/** Secondary shade for separating sections of components or list items */
val separatorSecondary: Color,
/** Accent text colour for plain actions. */
val textActionAccent: Color,
/** Default text colour for plain actions. */
@@ -103,6 +103,7 @@ val compoundColorsDark = SemanticColors(
iconTertiary = DarkColorTokens.colorGray800,
iconTertiaryAlpha = DarkColorTokens.colorAlphaGray800,
separatorPrimary = DarkColorTokens.colorGray400,
separatorSecondary = DarkColorTokens.colorGray300,
textActionAccent = DarkColorTokens.colorGreen900,
textActionPrimary = DarkColorTokens.colorGray1400,
textBadgeAccent = DarkColorTokens.colorGreen1100,
@@ -103,6 +103,7 @@ val compoundColorsHcDark = SemanticColors(
iconTertiary = DarkHcColorTokens.colorGray800,
iconTertiaryAlpha = DarkHcColorTokens.colorAlphaGray800,
separatorPrimary = DarkHcColorTokens.colorGray400,
separatorSecondary = DarkHcColorTokens.colorGray300,
textActionAccent = DarkHcColorTokens.colorGreen900,
textActionPrimary = DarkHcColorTokens.colorGray1400,
textBadgeAccent = DarkHcColorTokens.colorGreen1100,
@@ -103,6 +103,7 @@ val compoundColorsLight = SemanticColors(
iconTertiary = LightColorTokens.colorGray800,
iconTertiaryAlpha = LightColorTokens.colorAlphaGray800,
separatorPrimary = LightColorTokens.colorGray400,
separatorSecondary = LightColorTokens.colorGray300,
textActionAccent = LightColorTokens.colorGreen900,
textActionPrimary = LightColorTokens.colorGray1400,
textBadgeAccent = LightColorTokens.colorGreen1100,
@@ -103,6 +103,7 @@ val compoundColorsHcLight = SemanticColors(
iconTertiary = LightHcColorTokens.colorGray800,
iconTertiaryAlpha = LightHcColorTokens.colorAlphaGray800,
separatorPrimary = LightHcColorTokens.colorGray400,
separatorSecondary = LightHcColorTokens.colorGray300,
textActionAccent = LightHcColorTokens.colorGreen900,
textActionPrimary = LightHcColorTokens.colorGray1400,
textBadgeAccent = LightHcColorTokens.colorGreen1100,
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="m12,16.975 l-3.9,3.9a0.95,0.95 0,0 1,-0.7 0.275,0.95 0.95,0 0,1 -0.7,-0.275 0.95,0.95 0,0 1,-0.275 -0.7q0,-0.425 0.275,-0.7l3.875,-3.875q0.575,-0.575 1.425,-0.575t1.425,0.575l3.875,3.875a0.95,0.95 0,0 1,0.275 0.7,0.95 0.95,0 0,1 -0.275,0.7 0.95,0.95 0,0 1,-0.7 0.275,0.95 0.95,0 0,1 -0.7,-0.275zM12,7.175 L15.9,3.275A0.95,0.95 0,0 1,16.6 3q0.425,0 0.7,0.275a0.95,0.95 0,0 1,0.275 0.7,0.95 0.95,0 0,1 -0.275,0.7L13.425,8.55q-0.575,0.575 -1.425,0.575t-1.425,-0.575L6.7,4.675a0.95,0.95 0,0 1,-0.275 -0.7q0,-0.425 0.275,-0.7A0.95,0.95 0,0 1,7.4 3q0.425,0 0.7,0.275z"
android:fillColor="#FF000000"/>
</vector>
@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,4a1,1 0,0 0,-1 1v2H3a1,1 0,1 0,0 2h2v8a3,3 0,0 0,3 3h8v2a1,1 0,0 0,2 0v-2h2a1,1 0,1 0,0 -2h-2v-8a3,3 0,0 0,-3 -3H7V5a1,1 0,0 0,-1 -1m9,5a1,1 0,0 1,1 1v8H8a1,1 0,0 1,-1 -1V9z"
android:fillColor="#FF000000"
android:fillType="evenOdd"/>
<path
android:pathData="M15.707,0.293a1,1 0,0 0,-1.414 0l-2,2a1,1 0,0 0,0 1.414l2,2a1,1 0,1 0,1.414 -1.414l-0.275,-0.276A6,6 0,0 1,21 10a1,1 0,1 0,2 0,8 8,0 0,0 -7.596,-7.99l0.303,-0.303a1,1 0,0 0,0 -1.414"
android:fillColor="#FF000000"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="m12,19.2 l3.9,-3.9a0.95,0.95 0,0 1,0.7 -0.275q0.425,0 0.7,0.275a0.95,0.95 0,0 1,0.275 0.7,0.95 0.95,0 0,1 -0.275,0.7l-3.875,3.875q-0.575,0.575 -1.425,0.575t-1.425,-0.575L6.7,16.7a0.95,0.95 0,0 1,-0.275 -0.7q0,-0.425 0.275,-0.7a0.95,0.95 0,0 1,0.7 -0.275q0.425,0 0.7,0.275zM12,4.95 L8.1,8.85a0.95,0.95 0,0 1,-0.7 0.275,0.95 0.95,0 0,1 -0.7,-0.275 0.95,0.95 0,0 1,-0.275 -0.7q0,-0.425 0.275,-0.7l3.875,-3.875Q11.15,3 12,3t1.425,0.575L17.3,7.45a0.95,0.95 0,0 1,0.275 0.7,0.95 0.95,0 0,1 -0.275,0.7 0.95,0.95 0,0 1,-0.7 0.275,0.95 0.95,0 0,1 -0.7,-0.275z"
android:fillColor="#FF000000"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2a1,1 0,0 1,1 1v18a1,1 0,1 1,-2 0L11,3a1,1 0,0 1,1 -1M2,8a1,1 0,0 1,1.6 -0.8l5.334,4a1,1 0,0 1,0 1.6l-5.334,4A1,1 0,0 1,2 16zM20.4,7.2A1,1 0,0 1,22 8v8a1,1 0,0 1,-1.6 0.8l-5.334,-4a1,1 0,0 1,0 -1.6z"
android:fillColor="#FF000000"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M11.2,15.066a1,1 0,0 1,1.6 0l4,5.334A1,1 0,0 1,16 22L8,22a1,1 0,0 1,-0.8 -1.6zM21,11a1,1 0,1 1,0 2L3,13a1,1 0,1 1,0 -2zM16,2a1,1 0,0 1,0.8 1.6l-4,5.334a1,1 0,0 1,-1.6 0L7.2,3.6A1,1 0,0 1,8 2z"
android:fillColor="#FF000000"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M9.837,17.869a4,4 0,0 1,4.326 0L18,20.336V4H6v16.336zM20,20.336c0,1.582 -1.751,2.538 -3.082,1.682l-3.836,-2.466a2,2 0,0 0,-2.164 0l-3.836,2.466 -0.126,0.076C5.643,22.814 4,21.869 4,20.336V4a2,2 0,0 1,2 -2h12a2,2 0,0 1,2 2z"
android:fillColor="#FF000000"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M18,2H6a2,2 0,0 0,-2 2v16.336c0,1.583 1.75,2.538 3.082,1.683l3.836,-2.467a2,2 0,0 1,2.164 0l3.836,2.467c1.331,0.855 3.082,-0.1 3.082,-1.683V4a2,2 0,0 0,-2 -2"
android:fillColor="#FF000000"/>
</vector>
@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3.503,2.083a1,1 0,0 1,1.414 0l17,17a1,1 0,1 1,-1.414 1.414L20,19.994v0.342c0,1.582 -1.75,2.538 -3.082,1.683l-3.836,-2.467a2,2 0,0 0,-2.164 0l-3.836,2.467 -0.126,0.075C5.643,22.814 4,21.869 4,20.336V3.994l-0.497,-0.497a1,1 0,0 1,0 -1.414M6,20.336l3.837,-2.467a4,4 0,0 1,4.326 0L18,20.336v-2.342l-12,-12z"
android:fillColor="#FF000000"
android:fillType="evenOdd"/>
<path
android:pathData="M18,2a2,2 0,0 1,2 2v10.35l-2,-2V4H9.65l-2,-2z"
android:fillColor="#FF000000"/>
</vector>
@@ -18,6 +18,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.dp
@@ -37,13 +38,18 @@ private const val MAX_COUNT_STRING = "+$MAX_COUNT"
* @param count The number to display. If the number is greater than [MAX_COUNT], the counter will display [MAX_COUNT_STRING].
* If the number is less than 1, the counter will not be displayed.
* @param modifier The modifier to apply to this layout.
* @param containerColor The background color of the counter. When null, uses [isCritical] to pick a default.
* @param contentColor The text color inside the counter. When null, uses [textOnSolidPrimary].
* @param textStyle The style to apply to the text inside the counter.
* @param isCritical If true, the counter will use a critical color scheme, otherwise it will use an accent color scheme.
* Only used when [containerColor] is null.
*/
@Composable
fun CounterAtom(
count: Int,
modifier: Modifier = Modifier,
containerColor: Color? = null,
contentColor: Color? = null,
textStyle: TextStyle = CounterAtomDefaults.textStyle,
isCritical: Boolean = false,
) {
@@ -65,7 +71,7 @@ fun CounterAtom(
.size(squareSize.toDp() + 1.dp)
.clip(CircleShape)
.background(
if (isCritical) {
containerColor ?: if (isCritical) {
ElementTheme.colors.iconCriticalPrimary
} else {
ElementTheme.colors.iconAccentPrimary
@@ -76,7 +82,7 @@ fun CounterAtom(
modifier = Modifier.align(Alignment.Center),
text = countAsText,
style = textStyle,
color = ElementTheme.colors.textOnSolidPrimary,
color = contentColor ?: ElementTheme.colors.textOnSolidPrimary,
)
}
}
@@ -12,6 +12,7 @@ import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
@@ -31,21 +32,36 @@ import io.element.android.libraries.designsystem.theme.unreadIndicator
fun UnreadIndicatorAtom(
modifier: Modifier = Modifier,
size: Dp = 12.dp,
count: Long? = null,
color: Color = ElementTheme.colors.unreadIndicator,
isVisible: Boolean = true,
contentDescription: String? = null,
border: BorderStroke? = null,
) {
Box(
modifier = modifier
.semantics {
contentDescription?.let { this.contentDescription = it }
}
.size(size)
.clip(CircleShape)
.background(if (isVisible) color else Color.Transparent)
.then(if (border != null) Modifier.border(border, CircleShape) else Modifier)
)
when {
!isVisible -> Spacer(modifier = modifier.size(size))
count != null && count >= 1 -> CounterAtom(
count = count.toInt(),
modifier = modifier
.semantics {
contentDescription?.let { this.contentDescription = it }
}
.then(if (border != null) Modifier.border(border, CircleShape) else Modifier),
containerColor = color,
contentColor = ElementTheme.colors.bgCanvasDefault,
textStyle = ElementTheme.typography.fontBodySmMedium,
)
else -> Box(
modifier = modifier
.semantics {
contentDescription?.let { this.contentDescription = it }
}
.size(size)
.clip(CircleShape)
.background(color)
.then(if (border != null) Modifier.border(border, CircleShape) else Modifier),
)
}
}
@PreviewsDayNight
@@ -108,13 +108,6 @@ enum class FeatureFlags(
defaultValue = { true },
isFinished = false,
),
FloatingDateBadge(
key = "feature.floating_date_badge",
title = "Display sticky date headers in the timeline",
description = "When scrolling, a sticky date badge will be displayed so you can easily know on which date the messages you're seeing were sent.",
defaultValue = { false },
isFinished = false,
),
JumpToUnread(
key = "feature.jump_to_unread",
title = "Jump to unread messages",
@@ -145,4 +138,11 @@ enum class FeatureFlags(
defaultValue = { false },
isFinished = false,
),
UnreadIndicatorCount(
key = "feature.unread_indicator_count",
title = "Unread indicator count",
description = "Show the number of unread messages on the unread indicator in the room list.",
defaultValue = { false },
isFinished = false,
),
}
@@ -212,4 +212,11 @@ interface JoinedRoom : BaseRoom {
* @return Result indicating success or failure.
*/
suspend fun sendLiveLocation(geoUri: String): Result<Unit>
/**
* Sets the display name of the current user within this room.
* This is different from the global setDisplayName which updates
* the user's display name across all of their rooms.
*/
suspend fun setOwnMemberDisplayName(displayName: String): Result<Unit>
}
@@ -548,6 +548,12 @@ class JoinedRustRoom(
}
}
override suspend fun setOwnMemberDisplayName(displayName: String): Result<Unit> = withContext(roomDispatcher) {
runCatchingExceptions {
innerRoom.setOwnMemberDisplayName(displayName)
}
}
override fun close() = destroy()
override fun destroy() {
@@ -92,6 +92,7 @@ class FakeJoinedRoom(
private val startLiveLocationShareResult: (Long) -> Result<EventId> = { lambdaError() },
private val stopLiveLocationShareResult: () -> Result<Unit> = { lambdaError() },
private val sendLiveLocationResult: (String) -> Result<Unit> = { lambdaError() },
private val setOwnMemberDisplayNameResult: (String) -> Result<Unit> = { lambdaError() },
) : JoinedRoom, BaseRoom by baseRoom {
private val sendQueueUpdates = MutableSharedFlow<SendQueueUpdate>(extraBufferCapacity = 10)
@@ -255,6 +256,10 @@ class FakeJoinedRoom(
sendLiveLocationResult(geoUri)
}
override suspend fun setOwnMemberDisplayName(displayName: String): Result<Unit> = simulateLongTask {
setOwnMemberDisplayNameResult(displayName)
}
private suspend fun simulateSendMediaProgress(progressCallback: ProgressCallback?) {
progressCallbackValues.forEach { (current, total) ->
progressCallback?.onProgress(current, total)
@@ -13,6 +13,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.room.BaseRoom
@@ -42,10 +43,8 @@ fun getRoomMemberAsState(roomMembersState: RoomMembersState, userId: UserId): St
@Composable
fun BaseRoom.getDirectRoomMember(roomMembersState: RoomMembersState): State<RoomMember?> {
val roomInfo by roomInfoFlow.collectAsState()
return remember {
derivedStateOf {
roomMembersState.getDirectRoomMember(roomInfo, sessionId)
}
return remember(roomInfo.isDm) {
mutableStateOf(roomMembersState.getDirectRoomMember(roomInfo, sessionId))
}
}
@@ -3,5 +3,7 @@
<string name="screen_bottom_sheet_create_dm_confirmation_button_title">"초대장 보내기"</string>
<string name="screen_bottom_sheet_create_dm_message">"%1$s 와 채팅을 시작하시겠습니까?"</string>
<string name="screen_bottom_sheet_create_dm_title">"초대장을 보내시겠습니까?"</string>
<string name="screen_bottom_sheet_create_dm_unknown_user_content">"이 사용자와의 대화 내역이 없습니다. 계속하려면 먼저 초대를 확인해 주세요."</string>
<string name="screen_bottom_sheet_create_dm_unknown_user_title">"이 사용자와 대화를 시작하시겠습니까?"</string>
<string name="screen_invites_invited_you">"%1$s (%2$s) 당신을 초대했습니다"</string>
</resources>
@@ -33,10 +33,13 @@ import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
import androidx.media3.common.MediaItem
import androidx.media3.common.ParserException
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.Player.STATE_READY
import androidx.media3.common.Timeline
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlaybackException
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
@@ -158,6 +161,19 @@ private fun ExoPlayerMediaVideoView(
isReady = playbackState == STATE_READY,
)
}
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
Timber.w(error, "Player error")
if (error is ExoPlaybackException && error.cause is ParserException) {
// The cause can be:
// androidx.media3.common.ParserException: Invalid NAL length {contentIsMalformed=true, dataType=1}
// This has been observed when the user wants to play a second time a recorded video.
// Workaround the issue #6956, start the playback again
exoPlayer.prepare()
exoPlayer.play()
}
}
}
}
@@ -16,6 +16,7 @@
<string name="screen_media_details_filename">"파일 명"</string>
<string name="screen_media_details_no_more_files_to_show">"더 이상 표시할 파일이 없습니다"</string>
<string name="screen_media_details_no_more_media_to_show">"더 이상 보여줄 미디어가 없습니다"</string>
<string name="screen_media_details_title">"파일 정보"</string>
<string name="screen_media_details_uploaded_by">"에 의해 업로드됨"</string>
<string name="screen_media_details_uploaded_on">"에 업로드됨"</string>
</resources>
@@ -29,8 +29,9 @@ class DefaultUnifiedPushGatewayUrlResolver(
): String {
return when (gatewayResult) {
is UnifiedPushGatewayResolverResult.Error -> {
// Use previous gateway if any, or the provided one
unifiedPushStore.getPushGateway(instance) ?: gatewayResult.gateway
// Use previous gateway if any, or the default one
unifiedPushStore.getPushGateway(instance)
?: defaultPushGatewayHttpUrlProvider.provide()
}
UnifiedPushGatewayResolverResult.ErrorInvalidUrl,
UnifiedPushGatewayResolverResult.NoMatrixGateway -> {
@@ -60,7 +60,7 @@ class DefaultUnifiedPushGatewayUrlResolverTest {
}
@Test
fun `resolve Error returns the url if no current url is available`() {
fun `resolve Error returns the default gateway url if no current url is available`() {
val sut = createDefaultUnifiedPushGatewayUrlResolver(
unifiedPushStore = FakeUnifiedPushStore(
getPushGatewayResult = { instance ->
@@ -73,7 +73,7 @@ class DefaultUnifiedPushGatewayUrlResolverTest {
gatewayResult = UnifiedPushGatewayResolverResult.Error("aUrl"),
instance = "instance",
)
assertThat(result).isEqualTo("aUrl")
assertThat(result).isEqualTo(FakeDefaultPushGatewayHttpUrlProvider().provide())
}
private fun createDefaultUnifiedPushGatewayUrlResolver(
@@ -109,7 +109,7 @@ enum class Command(
parameters = "<display-name>",
description = R.string.slash_command_description_nick_for_room,
isAllowedInThread = false,
isSupported = false,
isSupported = true,
),
ROOM_AVATAR(
command = "/roomavatar",
@@ -47,7 +47,7 @@ class CommandExecutor(
is SlashCommand.ChangeAvatar -> changeAvatar()
is SlashCommand.ChangeAvatarForRoom -> changeAvatarForRoom()
is SlashCommand.ChangeDisplayName -> changeDisplayName(slashCommand)
is SlashCommand.ChangeDisplayNameForRoom -> changeDisplayNameForRoom()
is SlashCommand.ChangeDisplayNameForRoom -> changeDisplayNameForRoom(slashCommand)
is SlashCommand.ChangeRoomAvatar -> changeRoomAvatar()
is SlashCommand.ChangeRoomName -> changeRoomName(slashCommand)
is SlashCommand.ChangeTopic -> changeTopic(slashCommand)
@@ -171,8 +171,8 @@ class CommandExecutor(
return Result.failure(Exception("Not yet implemented"))
}
private fun changeDisplayNameForRoom(): Result<Unit> {
return Result.failure(Exception("Not yet implemented"))
private suspend fun changeDisplayNameForRoom(slashCommand: SlashCommand.ChangeDisplayNameForRoom): Result<Unit> {
return joinedRoom.setOwnMemberDisplayName(slashCommand.displayName)
}
private suspend fun changeDisplayName(slashCommand: SlashCommand.ChangeDisplayName): Result<Unit> {
@@ -14,7 +14,6 @@ import io.element.android.libraries.matrix.api.timeline.MsgType
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
import io.element.android.libraries.matrix.test.A_MESSAGE
import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
@@ -185,10 +184,18 @@ class CommandExecutorTest {
}
@Test
fun `change display name for room is not supported`() = runTest {
val sut = createCommandExecutor()
val res = sut.proceedAdmin(SlashCommand.ChangeDisplayNameForRoom(A_USER_NAME))
assertThat(res.isFailure).isTrue()
fun `change display name for room delegates to joined room`() = runTest {
var capturedDisplayName: String? = null
val joinedRoom = FakeJoinedRoom(
setOwnMemberDisplayNameResult = { displayName ->
capturedDisplayName = displayName
Result.success(Unit)
}
)
val sut = createCommandExecutor(joinedRoom = joinedRoom)
val res = sut.proceedAdmin(SlashCommand.ChangeDisplayNameForRoom("room nick"))
assertThat(res.isSuccess).isTrue()
assertThat(capturedDisplayName).isEqualTo("room nick")
}
@Test
@@ -50,12 +50,15 @@
<string name="a11y_room_avatar">"Avatar místnosti"</string>
<string name="a11y_send_files">"Odeslat soubory"</string>
<string name="a11y_sender_location">"Poloha odesílatele"</string>
<string name="a11y_sent_by_sender_at_date">"Odesláno %1$s v %2$s"</string>
<string name="a11y_session_verification_time_limited_action_required">"Vyžaduje se časově omezená akce, na ověření máte jednu minutu"</string>
<string name="a11y_settings_with_required_action">"Nastavení, vyžaduje akci"</string>
<string name="a11y_show_password">"Zobrazit heslo"</string>
<string name="a11y_start_call">"Zahájit hovor"</string>
<string name="a11y_start_video_call">"Zahájit videohovor"</string>
<string name="a11y_start_voice_call">"Zahájit hlasový hovor"</string>
<string name="a11y_thread_in_room">"Vlákno v %1$s"</string>
<string name="a11y_threads_in_room">"Vlákna v %1$s"</string>
<string name="a11y_tombstoned_room">"Místnost s náhrobkem"</string>
<string name="a11y_user_avatar">"Avatar uživatele"</string>
<string name="a11y_user_menu">"Uživatelské menu"</string>
@@ -50,6 +50,7 @@
<string name="a11y_session_verification_time_limited_action_required">"Απαιτείται ενέργεια περιορισμένης χρονικής διάρκειας, έχετε ένα λεπτό για επαλήθευση."</string>
<string name="a11y_show_password">"Εμφάνιση κωδικού πρόσβασης"</string>
<string name="a11y_start_call">"Ξεκίνησε μια κλήση"</string>
<string name="a11y_start_video_call">"Ξεκινήστε μια βιντεοκλήση"</string>
<string name="a11y_start_voice_call">"Έναρξη φωνητικής κλήσης"</string>
<string name="a11y_tombstoned_room">"Θαμένη αίθουσα"</string>
<string name="a11y_user_avatar">"Άβαταρ χρήστη"</string>
@@ -545,4 +545,6 @@ Kas sa oled kindel, et soovid jätkata?"</string>
<string name="timeline_decryption_failure_historical_event_user_not_joined">"Sul pole ligipääsu antud sõnumile"</string>
<string name="timeline_decryption_failure_unable_to_decrypt">"Sõnumi dekrüptimine ei õnnestu"</string>
<string name="timeline_decryption_failure_withheld_unverified">"Kuna seade on verifitseerimata või saatja pole sinu digitaalset identiteeti verifitseerinud, siis sõnumi näitamine on blokeeritud."</string>
<string name="vulkan_not_supported_dialog_content_android">"Sinu nutiseadme tarkvara on liiga vana, vajalik on Android 8 või uuem."</string>
<string name="vulkan_not_supported_dialog_title_android">"Kaartide joonistamine pole toetatud"</string>
</resources>
@@ -99,6 +99,7 @@
<string name="action_decline_and_block">"Hylkää ja estä"</string>
<string name="action_delete">"Poista"</string>
<string name="action_delete_account">"Poista tili"</string>
<string name="action_delete_file">"Poista tiedosto"</string>
<string name="action_delete_poll">"Poista kysely"</string>
<string name="action_deselect_all">"Poista kaikki valinnat"</string>
<string name="action_disable">"Poista käytöstä"</string>
@@ -304,6 +305,7 @@ Syy: %1$s."</string>
<string name="common_please_wait">"Odota hetki…"</string>
<string name="common_poll_end_confirmation">"Haluatko varmasti lopettaa tämän kyselyn?"</string>
<string name="common_poll_summary">"Kysely: %1$s"</string>
<string name="common_poll_summary_prefix">"Kysely"</string>
<string name="common_poll_total_votes">"Ääniä yhteensä: %1$s"</string>
<string name="common_poll_undisclosed_text">"Tulokset näkyvät kyselyn päätyttyä"</string>
<plurals name="common_poll_votes_count">
@@ -543,4 +545,6 @@ Haluatko varmasti jatkaa?"</string>
<string name="timeline_decryption_failure_historical_event_user_not_joined">"Sinulla ei ole oikeutta lukea tätä viestiä"</string>
<string name="timeline_decryption_failure_unable_to_decrypt">"Viestin salauksen purkaminen ei onnistu"</string>
<string name="timeline_decryption_failure_withheld_unverified">"Tämä viesti estettiin, koska et ole vahvistanut laitettasi tai koska lähettäjän on vahvistettava diigitaalinen identiteettisi."</string>
<string name="vulkan_not_supported_dialog_content_android">"Laitteesi on liian vanha, tarvitset laitteen, jossa on Android 8 tai uudempi."</string>
<string name="vulkan_not_supported_dialog_title_android">"Karttojen renderöintiä ei tueta"</string>
</resources>
@@ -18,6 +18,7 @@
<string name="a11y_info">"Információ"</string>
<string name="a11y_join_call">"Csatlakozás a híváshoz"</string>
<string name="a11y_jump_to_bottom">"Ugrás az aljára"</string>
<string name="a11y_jump_to_unread">"Ugrás az olvasatlanhoz"</string>
<string name="a11y_move_the_map_to_my_location">"Térkép áthelyezése a jelenlegi helyre"</string>
<string name="a11y_notifications_mentions_only">"Csak megemlítések"</string>
<string name="a11y_notifications_muted">"Némítva"</string>
@@ -98,6 +99,7 @@
<string name="action_decline_and_block">"Elutasítás és letiltás"</string>
<string name="action_delete">"Törlés"</string>
<string name="action_delete_account">"Fiók törlése"</string>
<string name="action_delete_file">"Fájl törlése"</string>
<string name="action_delete_poll">"Szavazás törlése"</string>
<string name="action_deselect_all">"Kijelölés megszüntetése"</string>
<string name="action_disable">"Letiltás"</string>
@@ -303,6 +305,7 @@ Ok: %1$s."</string>
<string name="common_please_wait">"Kis türelmet…"</string>
<string name="common_poll_end_confirmation">"Biztos, hogy befejezi ezt a szavazást?"</string>
<string name="common_poll_summary">"Szavazás: %1$s"</string>
<string name="common_poll_summary_prefix">"Szavazás"</string>
<string name="common_poll_total_votes">"Összes szavazat: %1$s"</string>
<string name="common_poll_undisclosed_text">"Az eredmények a szavazás befejezése után jelennek meg"</string>
<plurals name="common_poll_votes_count">
@@ -541,4 +544,6 @@ Biztos, hogy folytatja?"</string>
<string name="timeline_decryption_failure_historical_event_user_not_joined">"Nincs hozzáférése ehhez az üzenethez"</string>
<string name="timeline_decryption_failure_unable_to_decrypt">"Nem sikerült visszafejteni az üzenetet"</string>
<string name="timeline_decryption_failure_withheld_unverified">"Ez az üzenet azért lett blokkolva, mert vagy nem ellenőrizte az eszközt, vagy a feladónak ellenőriznie kell az Ön digitális személyazonosságát."</string>
<string name="vulkan_not_supported_dialog_content_android">"Az Ön eszköze túl régi, Android 8 vagy újabb verziójú eszközre van szükség."</string>
<string name="vulkan_not_supported_dialog_title_android">"A térképek megjelenítése nem támogatott"</string>
</resources>
@@ -97,6 +97,7 @@
<string name="action_decline_and_block">"拒否してブロック"</string>
<string name="action_delete">"削除"</string>
<string name="action_delete_account">"アカウントを削除"</string>
<string name="action_delete_file">"ファイルを削除"</string>
<string name="action_delete_poll">"投票を削除"</string>
<string name="action_deselect_all">"全ての選択を解除"</string>
<string name="action_disable">"無効化"</string>
@@ -292,7 +293,7 @@
<string name="common_or">"または"</string>
<string name="common_other_options">"他のオプション"</string>
<string name="common_password">"パスワード"</string>
<string name="common_people">"人々"</string>
<string name="common_people">"ユーザー"</string>
<string name="common_permalink">"固定リンク"</string>
<string name="common_permission">"権限"</string>
<string name="common_pinned">"ピン留め"</string>
@@ -300,6 +301,7 @@
<string name="common_please_wait">"お待ちください…"</string>
<string name="common_poll_end_confirmation">"本当に投票を終了しますか?"</string>
<string name="common_poll_summary">"投票: %1$s"</string>
<string name="common_poll_summary_prefix">"投票"</string>
<string name="common_poll_total_votes">"総投票数:%1$s"</string>
<string name="common_poll_undisclosed_text">"結果は投票終了後に表示されます"</string>
<plurals name="common_poll_votes_count">
@@ -8,13 +8,16 @@
<plurals name="a11y_digits_entered">
<item quantity="other">"%1$d자리 입력됨"</item>
</plurals>
<string name="a11y_duration">"소요 시간: %1$s"</string>
<string name="a11y_edit_avatar">"아바타 편집"</string>
<string name="a11y_edit_room_address_hint">"전체 주소는 다음과 같습니다 %1$s"</string>
<string name="a11y_encryption_details">"암호화 세부 정보"</string>
<string name="a11y_expand_message_text_field">"메시지 텍스트 필드 확장"</string>
<string name="a11y_hide_password">"비밀번호 숨기기"</string>
<string name="a11y_info">"정보"</string>
<string name="a11y_join_call">"통화 참가"</string>
<string name="a11y_jump_to_bottom">"맨 아래로 이동"</string>
<string name="a11y_jump_to_unread">"읽지 않은 항목으로 이동"</string>
<string name="a11y_move_the_map_to_my_location">"지도를 내 위치로 이동"</string>
<string name="a11y_notifications_mentions_only">"멘션만"</string>
<string name="a11y_notifications_muted">"음소거함"</string>
@@ -31,6 +34,7 @@
<string name="a11y_playback_speed">"재생 속도"</string>
<string name="a11y_poll">"투표"</string>
<string name="a11y_poll_end">"종료된 투표"</string>
<string name="a11y_position">"위치: %1$s"</string>
<string name="a11y_qr_code">"QR 코드"</string>
<string name="a11y_react_with">"%1$s에 반응하세요"</string>
<string name="a11y_react_with_other_emojis">"다른 이모지로 반응하세요"</string>
@@ -45,10 +49,15 @@
<string name="a11y_room_avatar">"방 아바타"</string>
<string name="a11y_send_files">"파일 보내기"</string>
<string name="a11y_sender_location">"발신자 위치"</string>
<string name="a11y_sent_by_sender_at_date">"%1$s님이 %2$s에 보냈습니다."</string>
<string name="a11y_session_verification_time_limited_action_required">"제한 시간 내 인증이 필요합니다.1분 안에 확인해 주세요."</string>
<string name="a11y_settings_with_required_action">"설정,조치 필요"</string>
<string name="a11y_show_password">"비밀번호 표시"</string>
<string name="a11y_start_call">"통화 시작"</string>
<string name="a11y_start_video_call">"영상 통화 시작"</string>
<string name="a11y_start_voice_call">"음성 통화 시작"</string>
<string name="a11y_thread_in_room">"%1$s 내 스레드"</string>
<string name="a11y_threads_in_room">"%1$s 내 스레드 목록"</string>
<string name="a11y_tombstoned_room">"묘비 방"</string>
<string name="a11y_user_avatar">"사용자 아바타"</string>
<string name="a11y_user_menu">"사용자 메뉴"</string>
@@ -66,6 +75,7 @@
<string name="action_call">"통화"</string>
<string name="action_cancel">"취소"</string>
<string name="action_cancel_for_now">"현재 취소"</string>
<string name="action_choose_file">"파일 선택"</string>
<string name="action_choose_photo">"사진 선택"</string>
<string name="action_clear">"지우기"</string>
<string name="action_close">"닫기"</string>
@@ -85,12 +95,16 @@
<string name="action_deactivate_account">"계정 비활성화"</string>
<string name="action_decline">"거절"</string>
<string name="action_decline_and_block">"거부 및 차단"</string>
<string name="action_delete">"삭제"</string>
<string name="action_delete_account">"계정 삭제"</string>
<string name="action_delete_file">"파일 삭제"</string>
<string name="action_delete_poll">"투표 삭제"</string>
<string name="action_deselect_all">"전체 선택 해제"</string>
<string name="action_disable">"비활성화"</string>
<string name="action_discard">"취소"</string>
<string name="action_dismiss">"닫기"</string>
<string name="action_done">"완료"</string>
<string name="action_download">"다운로드"</string>
<string name="action_edit">"편집"</string>
<string name="action_edit_caption">"캡션 편집"</string>
<string name="action_edit_poll">"투표 수정"</string>
@@ -197,7 +211,9 @@
<string name="common_beta">"베타"</string>
<string name="common_blocked_users">"차단한 사용자"</string>
<string name="common_bubbles">"버블"</string>
<string name="common_call_declined">"통화 거절"</string>
<string name="common_call_started">"통화 시작"</string>
<string name="common_call_you_declined">"통화를 거절하셨습니다"</string>
<string name="common_chat_backup">"채팅 백업"</string>
<string name="common_copied_to_clipboard">"클립보드에 복사됨"</string>
<string name="common_copyright">"저작권"</string>
@@ -285,6 +301,7 @@
<string name="common_please_wait">"기다려 주세요…"</string>
<string name="common_poll_end_confirmation">"정말로 이 투표를 종료하시겠어요?"</string>
<string name="common_poll_summary">"투표: %1$s"</string>
<string name="common_poll_summary_prefix">"투표"</string>
<string name="common_poll_total_votes">"총 투표수: %1$s"</string>
<string name="common_poll_undisclosed_text">"결과는 투표가 끝난 이후에 공개됨"</string>
<plurals name="common_poll_votes_count">
@@ -448,6 +465,9 @@
<string name="error_unknown">"이런, 오류가 발생했어요"</string>
<string name="invite_friends_rich_title">"🔐️ %1$s에 참여하기"</string>
<string name="invite_friends_text">"%1$s에서 대화해요: %2$s"</string>
<string name="live_location_sharing_foreground_service_channel_title_android">"실시간 위치 공유"</string>
<string name="live_location_sharing_foreground_service_message_android">"위치 공유 중"</string>
<string name="live_location_sharing_foreground_service_title_android">"%1$s님의 실시간 위치"</string>
<string name="login_initial_device_name_android">"%1$s Android"</string>
<string name="preference_rageshake">"강하게 흔들어서 오류 보고하기"</string>
<string name="screen_bug_report_a11y_screenshot">"스크린샷"</string>
@@ -455,6 +475,12 @@
<string name="screen_create_poll_options_section_title">"옵션"</string>
<string name="screen_create_poll_remove_accessibility_label">"%1$s 제거"</string>
<string name="screen_create_poll_settings_section_title">"설정"</string>
<string name="screen_live_location_sheet_nobody_sharing">"위치를 공유 중인 사용자가 없습니다."</string>
<string name="screen_live_location_sheet_sharing_live_location">"실시간 위치 공유 중"</string>
<plurals name="screen_live_location_sheet_subtitle">
<item quantity="other">"%1$d명"</item>
</plurals>
<string name="screen_live_location_sheet_title">"지도 보기"</string>
<string name="screen_media_picker_error_failed_selection">"미디어 선택에 실패했습니다. 다시 시도해 주세요."</string>
<string name="screen_pinned_timeline_empty_state_description">"메시지를 누르고 \"%1$s\" 를 선택하여 여기에 포함합니다."</string>
<string name="screen_pinned_timeline_empty_state_headline">"중요한 메시지를 고정하여 쉽게 찾을 수 있도록 합니다"</string>
@@ -478,6 +504,7 @@
<string name="screen_room_event_pill">"메시지 %1$s"</string>
<string name="screen_room_grouped_state_events_expand">"펼치기"</string>
<string name="screen_room_grouped_state_events_reduce">"줄이다"</string>
<string name="screen_room_live_location_banner">"실시간 위치 공유 중"</string>
<string name="screen_room_permalink_same_room_android">"이 방을 이미 보고 있습니다!"</string>
<string name="screen_room_pinned_banner_indicator">"%2$s 의 %1$s"</string>
<string name="screen_room_pinned_banner_indicator_description">"%1$s 고정된 메시지"</string>
@@ -101,6 +101,7 @@
<string name="action_decline_and_block">"Odrzuć i zablokuj"</string>
<string name="action_delete">"Usuń"</string>
<string name="action_delete_account">"Usuń konto"</string>
<string name="action_delete_file">"Usuń plik"</string>
<string name="action_delete_poll">"Usuń ankietę"</string>
<string name="action_deselect_all">"Odznacz wszystko"</string>
<string name="action_disable">"Wyłącz"</string>
@@ -555,4 +556,6 @@ Czy na pewno chcesz kontynuować?"</string>
<string name="timeline_decryption_failure_historical_event_user_not_joined">"Nie masz uprawnień do tej wiadomości"</string>
<string name="timeline_decryption_failure_unable_to_decrypt">"Nie można odszyfrować wiadomości"</string>
<string name="timeline_decryption_failure_withheld_unverified">"Ta wiadomość została zablokowana, ponieważ urządzenie nie zostało zweryfikowane lub nadawca musi zweryfikować Twoją tożsamość."</string>
<string name="vulkan_not_supported_dialog_content_android">"Twoje urządzenie jest przestarzałe, wymagany system Android 8 lub nowszy."</string>
<string name="vulkan_not_supported_dialog_title_android">"Renderowanie map nie jest obsługiwane"</string>
</resources>
@@ -532,4 +532,6 @@
<string name="timeline_decryption_failure_historical_event_user_not_joined">"无权访问此消息"</string>
<string name="timeline_decryption_failure_unable_to_decrypt">"无法解密消息"</string>
<string name="timeline_decryption_failure_withheld_unverified">"此消息已被阻止,因为你未验证你的设备,或发送者需要验证你的数字身份。"</string>
<string name="vulkan_not_supported_dialog_content_android">"你的设备过旧,需要 Android 8 或更高版本的设备。"</string>
<string name="vulkan_not_supported_dialog_title_android">"地图渲染不受支持。"</string>
</resources>
@@ -546,4 +546,6 @@ Are you sure you want to continue?"</string>
<string name="timeline_decryption_failure_historical_event_user_not_joined">"You don\'t have access to this message"</string>
<string name="timeline_decryption_failure_unable_to_decrypt">"Unable to decrypt message"</string>
<string name="timeline_decryption_failure_withheld_unverified">"This message was blocked either because you did not verify your device or because the sender needs to verify your digital identity."</string>
<string name="vulkan_not_supported_dialog_content_android">"Your device is too old, a device with Android 8 or newer is required."</string>
<string name="vulkan_not_supported_dialog_title_android">"Rendering maps is not supported"</string>
</resources>