Release 0.2.0 (#150)

Release Notes

* Bumped haze, agp, and guava dependencies
* Implemented ordering functionality for friends list
* Added scroll to top feature in friends and conversations screens
* Improved messages handling
* Fixed coloring issues
* Cache improvements
* Implemented logout functionality
* Implemented new authorization flow (no auto-token re-request)
* Added support for sticker pack preview attachments
* Bump LongPoll to version 19
* Markdown support for messages bubbles
* Adjust app name font size based on screen width

---------

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
2025-04-04 21:47:05 +03:00
committed by GitHub
parent 0eb3146428
commit 82fb78e9ea
279 changed files with 9171 additions and 4517 deletions
@@ -1,135 +0,0 @@
package dev.meloda.fast.ui.basic
import android.os.Build
import android.view.autofill.AutofillManager
import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.Autofill
import androidx.compose.ui.autofill.AutofillNode
import androidx.compose.ui.autofill.AutofillType
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalAutofill
import androidx.compose.ui.platform.LocalAutofillTree
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import kotlin.math.roundToInt
fun Modifier.connectNode(handler: AutoFillHandler): Modifier {
return with(handler) { fillBounds() }
}
fun Modifier.defaultFocusChangeAutoFill(handler: AutoFillHandler): Modifier {
return this.then(
Modifier.onFocusChanged {
if (it.isFocused) {
handler.request()
} else {
handler.cancel()
}
}
)
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun autoFillRequestHandler(
autofillTypes: List<AutofillType> = listOf(),
onFill: (String) -> Unit,
): AutoFillHandler {
val view = LocalView.current
val context = LocalContext.current
var isFillRecently = remember { false }
val autoFillNode = remember {
AutofillNode(
autofillTypes = autofillTypes,
onFill = {
isFillRecently = true
onFill(it)
}
)
}
val autofill = LocalAutofill.current
LocalAutofillTree.current += autoFillNode
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return EmptyAutoFillHandler
return remember {
@RequiresApi(Build.VERSION_CODES.O)
object : AutoFillHandler {
val autofillManager = context.getSystemService(AutofillManager::class.java)
override fun requestManual() {
autofillManager.requestAutofill(
view,
autoFillNode.id,
autoFillNode.boundingBox?.toAndroidRect() ?: error("BoundingBox is not provided yet")
)
}
override fun requestVerifyManual() {
if (isFillRecently) {
isFillRecently = false
requestManual()
}
}
override val autoFill: Autofill?
get() = autofill
override val autoFillNode: AutofillNode
get() = autoFillNode
override fun request() {
autofill?.requestAutofillForNode(autofillNode = autoFillNode)
}
override fun cancel() {
autofill?.cancelAutofillForNode(autofillNode = autoFillNode)
}
override fun Modifier.fillBounds(): Modifier {
return this.then(
Modifier.onGloballyPositioned {
autoFillNode.boundingBox = it.boundsInWindow()
})
}
}
}
}
fun Rect.toAndroidRect(): android.graphics.Rect {
return android.graphics.Rect(
left.roundToInt(),
top.roundToInt(),
right.roundToInt(),
bottom.roundToInt()
)
}
@OptIn(ExperimentalComposeUiApi::class)
interface AutoFillHandler {
val autoFill: Autofill?
val autoFillNode: AutofillNode?
fun requestVerifyManual()
fun requestManual()
fun request()
fun cancel()
fun Modifier.fillBounds(): Modifier
}
@ExperimentalComposeUiApi
data object EmptyAutoFillHandler : AutoFillHandler {
override val autoFill: Autofill? = null
override val autoFillNode: AutofillNode? = null
override fun requestVerifyManual() {}
override fun requestManual() {}
override fun request() {}
override fun cancel() {}
override fun Modifier.fillBounds(): Modifier = this.then(Modifier)
}
@@ -6,19 +6,26 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import dev.meloda.fast.ui.R as UiR
@Composable
fun ErrorView(
modifier: Modifier = Modifier,
iconResId: Int? = UiR.drawable.round_error_24,
text: String,
buttonText: String? = null,
onButtonClick: (() -> Unit)? = null,
@@ -30,6 +37,16 @@ fun ErrorView(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
iconResId?.let {
Icon(
modifier = Modifier.size(120.dp),
painter = painterResource(iconResId),
contentDescription = null,
tint = MaterialTheme.colorScheme.primaryContainer
)
Spacer(modifier = Modifier.height(12.dp))
}
Text(
text = text,
style = MaterialTheme.typography.titleLarge,
@@ -37,9 +54,10 @@ fun ErrorView(
)
buttonText?.let {
Spacer(modifier = Modifier.height(12.dp))
Spacer(modifier = Modifier.height(24.dp))
Button(
onClick = { onButtonClick?.invoke() }
onClick = { onButtonClick?.invoke() },
shape = RoundedCornerShape(6.dp)
) {
Text(text = buttonText)
}
@@ -110,13 +110,12 @@ fun MaterialDialog(
.verticalScroll(scrollState)
.onPlaced { isPlaced = true }
) {
Spacer(modifier = Modifier.height(8.dp))
if (text != null && title == null) {
Spacer(modifier = Modifier.height(20.dp))
}
if (text != null) {
Spacer(modifier = Modifier.height(8.dp))
Row {
Spacer(modifier = Modifier.width(24.dp))
Text(
@@ -137,8 +136,6 @@ fun MaterialDialog(
selectionType = selectionType,
items = alertItems,
onItemClick = { index ->
onItemClick?.invoke(index)
if (selectionType == SelectionType.None) {
onDismissRequest.invoke()
} else {
@@ -149,6 +146,8 @@ fun MaterialDialog(
alertItems = newItems
}
onItemClick?.invoke(index)
},
onItemCheckedChanged = { index ->
val newItems = alertItems.toMutableList()
@@ -161,11 +160,7 @@ fun MaterialDialog(
)
Spacer(modifier = Modifier.height(10.dp))
} else {
if (customContent != null) {
Spacer(modifier = Modifier.height(4.dp))
customContent.invoke(this)
Spacer(modifier = Modifier.height(10.dp))
}
customContent?.invoke(this)
}
}
@@ -0,0 +1,70 @@
package dev.meloda.fast.ui.components
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import dev.meloda.fast.model.BaseError
import dev.meloda.fast.ui.R
@Composable
fun VkErrorView(
modifier: Modifier = Modifier,
baseError: BaseError,
onButtonClick: () -> Unit = {}
) {
when (baseError) {
is BaseError.SessionExpired -> {
ErrorView(
modifier = modifier,
text = stringResource(R.string.session_expired),
buttonText = stringResource(R.string.action_log_out),
onButtonClick = onButtonClick
)
}
is BaseError.SimpleError -> {
ErrorView(
modifier = modifier,
text = baseError.message,
buttonText = stringResource(R.string.try_again),
onButtonClick = onButtonClick
)
}
BaseError.AccountBlocked -> {
ErrorView(
modifier = modifier,
text = "Account blocked",
buttonText = stringResource(R.string.action_log_out),
onButtonClick = onButtonClick
)
}
BaseError.ConnectionError -> {
ErrorView(
modifier = modifier,
text = "Connection error",
buttonText = stringResource(R.string.try_again),
onButtonClick = onButtonClick
)
}
BaseError.InternalError -> {
ErrorView(
modifier = modifier,
text = "Internal error",
buttonText = stringResource(R.string.try_again),
onButtonClick = onButtonClick
)
}
BaseError.UnknownError -> {
ErrorView(
modifier = modifier,
text = "Unknown error",
buttonText = stringResource(R.string.try_again),
onButtonClick = onButtonClick
)
}
}
}
@@ -6,13 +6,27 @@ import androidx.lifecycle.ViewModel
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import org.koin.androidx.compose.koinViewModel
import org.koin.androidx.compose.navigation.koinNavViewModel
import org.koin.core.parameter.ParametersDefinition
import org.koin.core.qualifier.Qualifier
@Composable
inline fun <reified T : ViewModel> NavBackStackEntry.sharedViewModel(navController: NavController): T {
val navGraphRoute = destination.parent?.route ?: return koinViewModel()
inline fun <reified T : ViewModel> NavBackStackEntry.sharedViewModel(
navController: NavController,
route: String? = null,
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null,
): T {
val navGraphRoute = route ?: destination.parent?.route ?: return koinViewModel(
qualifier = qualifier,
parameters = parameters
)
val parentEntry = remember(this) {
navController.getBackStackEntry(navGraphRoute)
}
return koinNavViewModel(viewModelStoreOwner = parentEntry)
return koinViewModel(
viewModelStoreOwner = parentEntry,
qualifier = qualifier,
parameters = parameters
)
}
@@ -7,5 +7,6 @@ data class ThemeConfig(
val amoledDark: Boolean,
val enableBlur: Boolean,
val enableMultiline: Boolean,
val useSystemFont: Boolean
val useSystemFont: Boolean,
val enableAnimations: Boolean
)
@@ -28,4 +28,14 @@ sealed class ConversationOption(
title = UiText.Resource(R.string.action_delete),
icon = UiImage.Resource(R.drawable.round_delete_outline_24)
)
data object Archive : ConversationOption(
title = UiText.Resource(R.string.conversation_context_action_archive),
icon = UiImage.Resource(R.drawable.outline_archive_24)
)
data object Unarchive : ConversationOption(
title = UiText.Resource(R.string.conversation_context_action_unarchive),
icon = UiImage.Resource(R.drawable.outline_unarchive_24)
)
}
@@ -1,14 +0,0 @@
package dev.meloda.fast.ui.model.api
data class ConversationsShowOptions(
val showDeleteDialog: Int?,
val showPinDialog: UiConversation?
) {
companion object {
val EMPTY: ConversationsShowOptions = ConversationsShowOptions(
showDeleteDialog = null,
showPinDialog = null
)
}
}
@@ -9,8 +9,8 @@ import dev.meloda.fast.ui.util.ImmutableList
@Immutable
data class UiConversation(
val id: Int,
val lastMessageId: Int?,
val id: Long,
val lastMessageId: Long?,
val avatar: UiImage?,
val title: String,
val unreadCount: String?,
@@ -27,5 +27,6 @@ data class UiConversation(
val peerType: PeerType,
val interactionText: String?,
val isExpanded: Boolean,
val isArchived: Boolean,
val options: ImmutableList<ConversationOption>,
)
@@ -6,7 +6,7 @@ import dev.meloda.fast.model.api.domain.OnlineStatus
@Immutable
data class UiFriend(
val userId: Int,
val userId: Long,
val avatar: UiImage?,
val firstName: String,
val lastName: String,
@@ -2,6 +2,7 @@ package dev.meloda.fast.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.animation.animateColorAsState
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
@@ -9,8 +10,10 @@ import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
@@ -20,7 +23,9 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat
import androidx.navigation.NavController
import dev.chrisbanes.haze.HazeState
import dev.meloda.fast.model.api.domain.VkUser
import dev.meloda.fast.ui.R
import dev.meloda.fast.ui.model.DeviceSize
import dev.meloda.fast.ui.model.SizeConfig
@@ -113,7 +118,8 @@ val LocalThemeConfig = compositionLocalOf {
amoledDark = false,
enableBlur = false,
enableMultiline = false,
useSystemFont = false
useSystemFont = false,
enableAnimations = false
)
}
@@ -124,12 +130,16 @@ val LocalSizeConfig = compositionLocalOf {
)
}
val LocalHazeState = compositionLocalOf {
HazeState()
}
val LocalHazeState = compositionLocalOf { HazeState() }
val LocalBottomPadding = compositionLocalOf { 0.dp }
val LocalUser = compositionLocalOf<VkUser?> { null }
val LocalReselectedTab = compositionLocalOf { mapOf<Any, Boolean>() }
val LocalNavRootController = compositionLocalOf<NavController?> { null }
val LocalNavController = compositionLocalOf<NavController?> { null }
val LocalBottomPadding = compositionLocalOf {
0.dp
@Composable
fun <T: NavController> ProvidableCompositionLocal<T?>.getOrThrow(): T {
return requireNotNull(current)
}
@Composable
@@ -142,9 +152,10 @@ fun AppTheme(
selectedColorScheme: Int = 0,
content: @Composable () -> Unit
) {
val context = LocalContext.current
val colorScheme: ColorScheme = when {
useDynamicColors && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (useDarkTheme) dynamicDarkColorScheme(context)
else dynamicLightColorScheme(context)
}
@@ -167,6 +178,10 @@ fun AppTheme(
}
}
val colorPrimary by animateColorAsState(colorScheme.primary)
val colorSurface by animateColorAsState(colorScheme.surface)
val colorBackground by animateColorAsState(colorScheme.background)
val typography = if (useSystemFont) {
MaterialTheme.typography
} else {
@@ -185,7 +200,7 @@ fun AppTheme(
bodySmall = MaterialTheme.typography.bodySmall.copy(fontFamily = robotoFonts),
labelLarge = MaterialTheme.typography.labelLarge.copy(fontFamily = robotoFonts),
labelMedium = MaterialTheme.typography.labelMedium.copy(fontFamily = robotoFonts),
labelSmall = MaterialTheme.typography.labelSmall.copy(fontFamily = robotoFonts)
labelSmall = MaterialTheme.typography.labelSmall.copy(fontFamily = robotoFonts),
)
}
@@ -199,7 +214,12 @@ fun AppTheme(
}
MaterialTheme(
colorScheme = predefinedColorScheme ?: colorScheme,
colorScheme = (predefinedColorScheme ?: colorScheme)
.copy(
primary = colorPrimary,
background = colorBackground,
surface = colorSurface
),
typography = typography,
content = content
)
@@ -65,3 +65,5 @@ class ImmutableList<T>(val values: List<T>) : Iterable<T> {
override fun iterator(): Iterator<T> = values.listIterator()
}
fun <T> emptyImmutableList(): ImmutableList<T> = ImmutableList(emptyList())
@@ -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:fillColor="#ffffff"
android:pathData="M9 13V5C9 3.9 9.9 3 11 3H20C21.1 3 22 3.9 22 5V11H18.57L17.29 9.26C17.23 9.17 17.11 9.17 17.05 9.26L15.06 12C15 12.06 14.88 12.07 14.82 12L13.39 10.25C13.33 10.18 13.22 10.18 13.16 10.25L11.05 12.91C10.97 13 11.04 13.15 11.16 13.15H17.5V15H11C9.89 15 9 14.11 9 13M6 22V21H4V22H2V2H4V3H6V2H8.39C7.54 2.74 7 3.8 7 5V13C7 15.21 8.79 17 11 17H15.7C14.67 17.83 14 19.08 14 20.5C14 21.03 14.11 21.53 14.28 22H6M4 7H6V5H4V7M4 11H6V9H4V11M4 15H6V13H4V15M6 19V17H4V19H6M23 13V15H21V20.5C21 21.88 19.88 23 18.5 23S16 21.88 16 20.5 17.12 18 18.5 18C18.86 18 19.19 18.07 19.5 18.21V13H23Z" />
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3L6,3c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5L3,19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM6.24,5h11.52l0.81,0.97L5.44,5.97l0.8,-0.97zM5,19L5,8h14v11L5,19zM13.45,10h-2.9v3L8,13l4,4 4,-4h-2.55z" />
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3L6,3c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5L3,19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM6.24,5h11.52l0.83,1L5.42,6l0.82,-1zM5,19L5,8h14v11L5,19zM8,14h2.55v3h2.9v-3L16,14l-4,-4z" />
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M15,20H5V7c0,-0.55 -0.45,-1 -1,-1h0C3.45,6 3,6.45 3,7v13c0,1.1 0.9,2 2,2h10c0.55,0 1,-0.45 1,-1v0C16,20.45 15.55,20 15,20zM20,16V4c0,-1.1 -0.9,-2 -2,-2H9C7.9,2 7,2.9 7,4v12c0,1.1 0.9,2 2,2h9C19.1,18 20,17.1 20,16zM18,16H9V4h9V16z" />
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M3,17.46v3.04c0,0.28 0.22,0.5 0.5,0.5h3.04c0.13,0 0.26,-0.05 0.35,-0.15L17.81,9.94l-3.75,-3.75L3.15,17.1c-0.1,0.1 -0.15,0.22 -0.15,0.36zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,13c-0.55,0 -1,-0.45 -1,-1L11,8c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v4c0,0.55 -0.45,1 -1,1zM13,17h-2v-2h2v2z" />
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M11,18h2c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1h-2c-0.55,0 -1,0.45 -1,1s0.45,1 1,1zM3,7c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L4,6c-0.55,0 -1,0.45 -1,1zM7,13h10c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L7,11c-0.55,0 -1,0.45 -1,1s0.45,1 1,1z" />
</vector>
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,8V6.41c0,-0.89 1.08,-1.34 1.71,-0.71l5.59,5.59c0.39,0.39 0.39,1.02 0,1.41l-5.59,5.59c-0.63,0.63 -1.71,0.19 -1.71,-0.7V16H5c-0.55,0 -1,-0.45 -1,-1V9c0,-0.55 0.45,-1 1,-1h7z" />
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M18.05,21.29c-0.39,0.39 -1.02,0.39 -1.41,0l-2.12,-2.12c-0.39,-0.39 -0.39,-1.02 0,-1.41h0c0.39,-0.39 1.02,-0.39 1.41,0l1.41,1.41l3.54,-3.54c0.39,-0.39 1.02,-0.39 1.41,0l0,0c0.39,0.39 0.39,1.02 0,1.41L18.05,21.29zM12.08,20H4c-1.1,0 -2,-0.9 -2,-2V6c0,-1.1 0.9,-2 2,-2h16c1.1,0 2,0.9 2,2v6.68C21.09,12.25 20.08,12 19,12c-3.87,0 -7,3.13 -7,7C12,19.34 12.03,19.67 12.08,20zM11.47,12.67c0.32,0.2 0.74,0.2 1.06,0l7.07,-4.42C19.85,8.09 20,7.82 20,7.53c0,-0.67 -0.73,-1.07 -1.3,-0.72L12,11L5.3,6.81C4.73,6.46 4,6.86 4,7.53c0,0.29 0.15,0.56 0.4,0.72L11.47,12.67z" />
</vector>
@@ -3,7 +3,9 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
android:pathData="M8,6.82v10.36c0,0.79 0.87,1.27 1.54,0.84l8.14,-5.18c0.62,-0.39 0.62,-1.29 0,-1.69L9.54,5.98C8.87,5.55 8,6.03 8,6.82z" />
</vector>
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M10,9V7.41c0,-0.89 -1.08,-1.34 -1.71,-0.71L3.7,11.29c-0.39,0.39 -0.39,1.02 0,1.41l4.59,4.59c0.63,0.63 1.71,0.19 1.71,-0.7V14.9c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z" />
</vector>
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M7,7.56c0,-0.94 -1.14,-1.42 -1.81,-0.75L0.71,11.29c-0.39,0.39 -0.39,1.02 0,1.41l4.48,4.48c0.67,0.68 1.81,0.2 1.81,-0.74 0,-0.28 -0.11,-0.55 -0.31,-0.75L3,12l3.69,-3.69c0.2,-0.2 0.31,-0.47 0.31,-0.75zM13,9V7.41c0,-0.89 -1.08,-1.34 -1.71,-0.71L6.7,11.29c-0.39,0.39 -0.39,1.02 0,1.41l4.59,4.59c0.63,0.63 1.71,0.18 1.71,-0.71V14.9c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z" />
</vector>
@@ -0,0 +1,19 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20.71,7.98L16.03,3.3c-0.19,-0.19 -0.45,-0.3 -0.71,-0.3H8.68c-0.26,0 -0.52,0.11 -0.7,0.29L3.29,7.98c-0.18,0.18 -0.29,0.44 -0.29,0.7v6.63c0,0.27 0.11,0.52 0.29,0.71l4.68,4.68c0.19,0.19 0.45,0.3 0.71,0.3h6.63c0.27,0 0.52,-0.11 0.71,-0.29l4.68,-4.68c0.19,-0.19 0.29,-0.44 0.29,-0.71V8.68c0.01,-0.26 -0.1,-0.52 -0.28,-0.7zM19,14.9L14.9,19H9.1L5,14.9V9.1L9.1,5h5.8L19,9.1v5.8z" />
<path
android:fillColor="@android:color/white"
android:pathData="M12,16m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" />
<path
android:fillColor="@android:color/white"
android:pathData="M12,7c-0.55,0 -1,0.45 -1,1v5c0,0.55 0.45,1 1,1s1,-0.45 1,-1V8c0,-0.55 -0.45,-1 -1,-1z" />
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,7c0.55,0 1,0.45 1,1v1.33l7.2,7.2 0.51,-0.51c0.19,-0.19 0.29,-0.44 0.29,-0.71V8.68c0,-0.27 -0.11,-0.52 -0.29,-0.71l-4.68,-4.68c-0.19,-0.18 -0.45,-0.29 -0.71,-0.29H8.68c-0.26,0 -0.52,0.11 -0.7,0.29l-0.51,0.51 3.69,3.69c0.17,-0.29 0.48,-0.49 0.84,-0.49zM2.41,1.58L1,2.99l3.64,3.64 -1.35,1.35c-0.18,0.18 -0.29,0.44 -0.29,0.7v6.63c0,0.27 0.11,0.52 0.29,0.71l4.68,4.68c0.19,0.19 0.45,0.3 0.71,0.3h6.63c0.27,0 0.52,-0.11 0.71,-0.29l1.35,-1.35L21.01,23l1.41,-1.41L2.41,1.58zM12,17.3c-0.72,0 -1.3,-0.58 -1.3,-1.3 0,-0.72 0.58,-1.3 1.3,-1.3s1.3,0.58 1.3,1.3c0,0.72 -0.58,1.3 -1.3,1.3z" />
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,17.27l4.15,2.51c0.76,0.46 1.69,-0.22 1.49,-1.08l-1.1,-4.72l3.67,-3.18c0.67,-0.58 0.31,-1.68 -0.57,-1.75l-4.83,-0.41l-1.89,-4.46c-0.34,-0.81 -1.5,-0.81 -1.84,0L9.19,8.63L4.36,9.04c-0.88,0.07 -1.24,1.17 -0.57,1.75l3.67,3.18l-1.1,4.72c-0.2,0.86 0.73,1.54 1.49,1.08L12,17.27z" />
</vector>
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19.65,9.04l-4.84,-0.42 -1.89,-4.45c-0.34,-0.81 -1.5,-0.81 -1.84,0L9.19,8.63l-4.83,0.41c-0.88,0.07 -1.24,1.17 -0.57,1.75l3.67,3.18 -1.1,4.72c-0.2,0.86 0.73,1.54 1.49,1.08l4.15,-2.5 4.15,2.51c0.76,0.46 1.69,-0.22 1.49,-1.08l-1.1,-4.73 3.67,-3.18c0.67,-0.58 0.32,-1.68 -0.56,-1.75zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z" />
</vector>
+47 -2
View File
@@ -5,19 +5,26 @@
<string name="sign_out_confirm">При выходе из учётной записи с устройства будут удалены все связанные с ней данные. Продолжить?</string>
<string name="yes">Да</string>
<string name="no">Нет</string>
<string name="message_context_action_retry">Повторить</string>
<string name="message_context_action_reply">Ответить</string>
<string name="message_context_action_forward_here">Переслать сюда</string>
<string name="message_context_action_forward">Переслать</string>
<string name="message_context_action_mark_as_important">Пометить как важное</string>
<string name="message_context_action_unmark_as_important">Помететить как не важное</string>
<string name="message_context_action_unmark_as_important">Пометить как не важное</string>
<string name="time_format">Время: %1$s</string>
<string name="message_context_action_unmark_as_spam">Помеьиьб как не спам</string>
<string name="message_context_action_pin">Закрепить</string>
<string name="message_context_action_unpin">Открепить</string>
<string name="message_context_action_edit">Изменить</string>
<string name="message_context_action_delete">Удалить</string>
<string name="message_context_action_read">Прочитать</string>
<string name="message_context_action_copy">Скопировать</string>
<string name="confirm_delete_message">Удалить сообщение?</string>
<string name="message_delete_for_all">Для всех</string>
<string name="message_mark_as_spam">Пометить как спам</string>
<string name="action_mark_as_read">Прочитать</string>
<string name="action_delete">Удалить</string>
<string name="conversation_context_action_unarchive">Из архива</string>
<string name="conversation_context_action_delete">Удалить</string>
<string name="confirm_delete_conversation">Удалить чат?</string>
<string name="action_sign_out">Выйти</string>
@@ -26,8 +33,12 @@
<string name="conversation_context_action_pin">Закрепить</string>
<string name="confirm_unpin_conversation">Открепить чат?</string>
<string name="confirm_pin_conversation">Закрепить чат?</string>
<string name="confirm_unarchive_conversation">Разархивировать чат?</string>
<string name="action_pin">Закрепить</string>
<string name="action_unpin">Открепить</string>
<string name="action_mark">Пометить</string>
<string name="action_unmark">Убрать пометку</string>
<string name="action_unarchive">Из архива</string>
<string name="message_call_type_outgoing">Исходящий вызов</string>
<string name="message_call_type_incoming">Входящий вызов</string>
<string name="message_call_state_ended">Закончился</string>
@@ -117,10 +128,13 @@
<string name="message_attachments_podcast">Подкаст</string>
<string name="message_attachments_narrative">Момент</string>
<string name="message_attachments_article">Статья</string>
<string name="message_attachments_video_message">Видеосообщение</string>
<string name="message_attachments_group_sticker">Стикер группы</string>
<string name="message_attachments_sticker_pack_preview">Превью стикерпака</string>
<string name="chat_interaction_uploading_file">Загрузка файла</string>
<string name="chat_interaction_uploading_photo">Загрузка фото</string>
<string name="chat_interaction_uploading_video">Загрузка видео</string>
<string name="chat_interaction_typing">Печатает</string>
<string name="chat_interaction_typing">печатает</string>
<string name="chat_interaction_recording_audio_message">Записывает</string>
<string name="chat_interaction_chat_typing">%1$s печатают</string>
<string name="chat_interaction_chat_single_typing">%1$s печатает</string>
@@ -167,6 +181,7 @@
<string name="members_count">Участники: %1$d</string>
<string name="title_loading">Загрузка&#8230;</string>
<string name="title_conversations">Чаты</string>
<string name="title_archive">Архив</string>
<string name="title_friends">Друзья</string>
<string name="title_profile">Профиль</string>
<string name="title_friends_all">Все</string>
@@ -218,4 +233,34 @@
<string name="title_create_chat">Создать чат</string>
<string name="action_create">Создать</string>
<string name="create_chat_title">Название</string>
<string name="chat_materials_title">Вложения чата</string>
<string name="chat_materials_action_title">Вложения</string>
<string name="friends_order_priority">Приоритет</string>
<string name="friends_order_name">Имя</string>
<string name="friends_order_random">Случайно</string>
<string name="friends_order_by_title">Упорядочить по</string>
<string name="chat_attachment_photos">Фото</string>
<string name="chat_attachment_videos">Видео</string>
<string name="chat_attachment_music">Музыка</string>
<string name="chat_attachment_files">Файлы</string>
<string name="chat_attachment_links">Ссылки</string>
<string name="message_context_action_mark_as_spam">Пометить как спам</string>
<string name="pin_message_text">Вы уверены, что хотите закрепить это сообщение? Это изменение увидят все участники чата.</string>
<string name="unpin_message_title">Открепить сообщение</string>
<string name="unpin_message_text">Вы уверены, что хотите открепить это сообщение? Все участники чата увидят это изменение.</string>
<string name="delete_message_title">Удалить сообщение?</string>
<string name="delete_message_for_everyone">Для всех</string>
<string name="important_message_title">Пометить как важное</string>
<string name="important_message_text">Вы уверены, что хотите пометить это сообщение как важное?</string>
<string name="unimportant_message_text">Вы уверены, что хотите убрать пометку избранного у этого сообщения?</string>
<string name="spam_message_title">Пометить как спам</string>
<string name="spam_message_text">Вы уверены, что хотите пометить это сообщение как спам?</string>
<string name="unimportant_message_title">Убрать пометку избранного</string>
<string name="unspam_message_title">Убрать пометку спама</string>
<string name="unspam_message_text">Вы уверены, что хотите убрать пометку спама у этого сообщения?</string>
<string name="pin_message_title">Закрепить сообщение</string>
<string name="copied_to_clipboard">Скопировано в буфер обмена</string>
<string name="conversation_context_action_archive">В архив</string>
<string name="confirm_archive_conversation">Архивировать чат?</string>
<string name="action_archive">В архив</string>
</resources>
+57 -2
View File
@@ -104,11 +104,14 @@
<string name="message_attachments_podcast">Podcast</string>
<string name="message_attachments_narrative">Narrative</string>
<string name="message_attachments_article">Article</string>
<string name="message_attachments_video_message">Video message</string>
<string name="message_attachments_group_sticker">Group sticker</string>
<string name="message_attachments_sticker_pack_preview">Sticker pack preview</string>
<string name="chat_interaction_uploading_file">Uploading file</string>
<string name="chat_interaction_uploading_photo">Uploading photo</string>
<string name="chat_interaction_uploading_video">Uploading video</string>
<string name="chat_interaction_typing">Typing</string>
<string name="chat_interaction_typing">typing</string>
<string name="chat_interaction_recording_audio_message">Recording</string>
<string name="chat_interaction_chat_typing">%1$s are typing</string>
@@ -124,14 +127,22 @@
<string name="sign_out_confirm">Signing out will delete all data related to this account from this device. Continue?</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="time_format">Time: %1$s</string>
<string name="message_context_action_retry">Retry</string>
<string name="message_context_action_reply">Reply</string>
<string name="message_context_action_forward_here">Forward here</string>
<string name="message_context_action_forward">Forward</string>
<string name="message_context_action_mark_as_important">Mark as important</string>
<string name="message_context_action_unmark_as_important">Unmark as important</string>
<string name="time_format">Time: %1$s</string>
<string name="message_context_action_mark_as_spam">Mark as spam</string>
<string name="message_context_action_unmark_as_spam">Unmark as spam</string>
<string name="message_context_action_pin">Pin</string>
<string name="message_context_action_unpin">Unpin</string>
<string name="message_context_action_edit">Edit</string>
<string name="message_context_action_delete">Delete</string>
<string name="message_context_action_read">Read</string>
<string name="message_context_action_copy">Copy</string>
<string name="confirm_delete_message">Delete the message?</string>
@@ -141,6 +152,8 @@
<string name="action_mark_as_read">Read</string>
<string name="action_delete">Delete</string>
<string name="conversation_context_action_archive">Archive</string>
<string name="conversation_context_action_unarchive">Unarchive</string>
<string name="conversation_context_action_delete">Delete</string>
<string name="confirm_delete_conversation">Delete the conversation?</string>
<string name="action_sign_out">Sign out</string>
@@ -149,8 +162,14 @@
<string name="conversation_context_action_pin">Pin</string>
<string name="confirm_unpin_conversation">Unpin the conversation?</string>
<string name="confirm_pin_conversation">Pin the conversation?</string>
<string name="confirm_archive_conversation">Archive the conversation?</string>
<string name="confirm_unarchive_conversation">Unarchive the conversation?</string>
<string name="action_pin">Pin</string>
<string name="action_unpin">Unpin</string>
<string name="action_mark">Mark</string>
<string name="action_unmark">Unmark</string>
<string name="action_archive">Archive</string>
<string name="action_unarchive">Unarchive</string>
<string name="message_call_type_outgoing">Outgoing call</string>
<string name="message_call_type_incoming">Incoming call</string>
<string name="message_call_state_ended">Ended</string>
@@ -226,6 +245,7 @@
<string name="members_count">Members: %1$d</string>
<string name="title_loading">Loading&#8230;</string>
<string name="title_conversations">Conversations</string>
<string name="title_archive">Archive</string>
<string name="title_friends">Friends</string>
<string name="title_profile">Profile</string>
<string name="title_friends_all">All</string>
@@ -283,4 +303,39 @@
<string name="title_create_chat">Create chat</string>
<string name="action_create">Create</string>
<string name="create_chat_title">Title</string>
<string name="chat_materials_title">Chat materials</string>
<string name="chat_materials_action_title">Materials</string>
<string name="friends_order_priority">Priority</string>
<string name="friends_order_name">Name</string>
<string name="friends_order_random">Random</string>
<string name="friends_order_mobile" translatable="false">Mobile</string>
<string name="friends_order_smart" translatable="false">Smart</string>
<string name="friends_order_by_title">Order by</string>
<string name="chat_attachment_photos">Photos</string>
<string name="chat_attachment_videos">Videos</string>
<string name="chat_attachment_music">Music</string>
<string name="chat_attachment_files">Files</string>
<string name="chat_attachment_links">Links</string>
<string name="pin_message_title">Pin message</string>
<string name="pin_message_text">Are you sure you want to pin this message? All chat members will see this change.</string>
<string name="unpin_message_title">Unpin message</string>
<string name="unpin_message_text">Are you sure you want to unpin this message? All chat members will see this change.</string>
<string name="delete_message_title">Delete the message?</string>
<string name="delete_message_for_everyone">For everyone</string>
<string name="important_message_title">Mark as important</string>
<string name="important_message_text">Are you sure you want to mark this message as important?</string>
<string name="unimportant_message_title">Unmark as important</string>
<string name="unimportant_message_text">Are you sure you want to unmark this message as important?</string>
<string name="spam_message_title">Mark as spam</string>
<string name="spam_message_text">Are you sure you want to mark this message as spam?</string>
<string name="unspam_message_title">Unmark as spam</string>
<string name="unspam_message_text">Are you sure you want to unmark this message as spam?</string>
<string name="copied_to_clipboard">Copied to clipboard</string>
</resources>