mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Merge branch 'develop' into feature/bma/scroll-to-unread-messages
This commit is contained in:
+31
@@ -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)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
+45
@@ -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,
|
||||
|
||||
+2
@@ -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. */
|
||||
|
||||
+1
@@ -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,
|
||||
|
||||
+1
@@ -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,
|
||||
|
||||
+1
@@ -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,
|
||||
|
||||
+1
@@ -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>
|
||||
+8
-2
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+26
-10
@@ -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
|
||||
|
||||
+7
-7
@@ -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,
|
||||
),
|
||||
}
|
||||
|
||||
+7
@@ -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>
|
||||
}
|
||||
|
||||
+6
@@ -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() {
|
||||
|
||||
+5
@@ -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)
|
||||
|
||||
+3
-4
@@ -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>
|
||||
|
||||
+16
@@ -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>
|
||||
|
||||
+3
-2
@@ -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 -> {
|
||||
|
||||
+2
-2
@@ -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(
|
||||
|
||||
+1
-1
@@ -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",
|
||||
|
||||
+3
-3
@@ -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> {
|
||||
|
||||
+12
-5
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user