Updated on 2026-08-14

This commit is contained in:
Tangem 2023-12-04 16:46:41 +03:00
commit 512ab6d54d
206 changed files with 8933 additions and 630 deletions

View file

@ -1,19 +1,18 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.jvm)
alias(deps.plugins.kotlin.kapt)
alias(deps.plugins.hilt.android)
id("configuration")
}
dependencies {
/** DI */
implementation(deps.hilt.android)
implementation(deps.hilt.core)
kapt(deps.hilt.kapt)
/** Core shouldn't depends on core, but in case with utils and logging its necessary */
implementation(projects.core.utils)
implementation(projects.core.analytics.models)
implementation(deps.kotlin.coroutines)
}

View file

@ -8,4 +8,7 @@ open class AnalyticsEvent(
val event: String,
var params: Map<String, String> = mapOf(),
val error: Throwable? = null,
)
) {
val id: String = "[$category] $event"
}

View file

@ -0,0 +1,6 @@
package com.tangem.core.analytics.models
interface OneTimeAnalyticsEvent {
val oneTimeEventId: String
}

View file

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.tangem.core.analytics" />

View file

@ -16,7 +16,7 @@ interface AnalyticsEventFilter {
* An internal filter check that, on external or internal conditions, recognizes the possibility of
* sending an event.
*/
fun canBeSent(event: AnalyticsEvent): Boolean
suspend fun canBeSent(event: AnalyticsEvent): Boolean
/**
* Performs a check to see if the event can be dispatched by a specific handler

View file

@ -12,13 +12,11 @@ interface AnalyticsEventHandler {
interface AnalyticsHandler : AnalyticsEventHandler {
fun id(): String
fun send(event: String, params: Map<String, String> = emptyMap())
fun send(eventId: String, params: Map<String, String> = emptyMap())
override fun send(event: AnalyticsEvent) {
send(prepareEventString(event), event.params)
send(event.id, event.params)
}
fun prepareEventString(event: AnalyticsEvent): String = "[${event.category}] ${event.event}"
}
interface ErrorEventHandler {

View file

@ -2,6 +2,8 @@ package com.tangem.core.analytics.di
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.filter.OneTimeEventFilter
import com.tangem.core.analytics.repository.AnalyticsRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -10,11 +12,16 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
class AnalyticsModule {
internal object AnalyticsModule {
@Singleton
@Provides
fun provideAnalyticsHandler(): AnalyticsEventHandler {
return Analytics // todo replace after refactoring calling Analytics in whole project
}
@Provides
fun provideOneTimeEventFilter(analyticsRepository: AnalyticsRepository): OneTimeEventFilter {
return OneTimeEventFilter(analyticsRepository)
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.core.analytics.filter
import com.tangem.core.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.OneTimeAnalyticsEvent
import com.tangem.core.analytics.repository.AnalyticsRepository
class OneTimeEventFilter(
private val analyticsRepository: AnalyticsRepository,
) : AnalyticsEventFilter {
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = event is OneTimeAnalyticsEvent
override suspend fun canBeSent(event: AnalyticsEvent): Boolean {
if (event !is OneTimeAnalyticsEvent) return true
val isSent = analyticsRepository.checkIsEventSent(event.oneTimeEventId)
if (!isSent) {
analyticsRepository.setIsEventSent(event.oneTimeEventId)
}
return !isSent
}
override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean {
return canBeAppliedTo(event)
}
}

View file

@ -0,0 +1,8 @@
package com.tangem.core.analytics.repository
interface AnalyticsRepository {
suspend fun checkIsEventSent(eventId: String): Boolean
suspend fun setIsEventSent(eventId: String)
}

View file

@ -32,6 +32,10 @@ object PreferencesKeys {
val SELECTED_APP_CURRENCY_KEY by lazy { stringPreferencesKey(name = "selectedAppCurrency") }
val BALANCE_HIDING_SETTINGS_KEY by lazy { stringPreferencesKey(name = "balanceHidingSettings") }
val WALLETS_SCROLL_PREVIEW_KEY by lazy { booleanPreferencesKey(name = "walletsScrollPreview") }
val SENT_ONE_TIME_EVENTS_KEY by lazy { stringPreferencesKey(name = "sentOneTimeEvents") }
}
/** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore<Preferences> */

View file

@ -34,5 +34,9 @@
{
"name": "REDESIGNED_SEND_SCREEN_ENABLED",
"version": "undefined"
},
{
"name": "WALLETS_SCROLLING_PREVIEW_ENABLED",
"version": "5.4.0"
}
]

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="common_fee_error">Erhalt der Gebühr fehlgeschlagen</string>
<string name="no_account_generic">Laden Sie %1$s+ %2$s auf um ein Konto zu erstellen</string>
<string name="send_error_dust_amount_format">Minimaler Betrag ist %s</string>
<string name="send_error_dust_change">Restbestand zu klein</string>
<string name="send_error_invalid_fee_value">Falsche Gebühr</string>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="common_fee_error">Échec de réception des commissions</string>
<string name="no_account_generic">Pour créer un compte, téléchargez %1$s+ %2$s</string>
<string name="send_error_dust_amount_format">Le montant minimal est de %s</string>
<string name="send_error_dust_change">Le reste est trop petit</string>
<string name="send_error_invalid_fee_value">Commission non valide</string>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="common_fee_error">Impossibile ottenere la commissione</string>
<string name="no_account_generic">Scarica %1$s+ %2$s per creare un account</string>
<string name="send_error_dust_amount_format">L\'importo minimo è di %s</string>
<string name="send_error_dust_change">L\'importo residuo è molto basso</string>
<string name="send_error_invalid_fee_value">Commissione non valida</string>

View file

@ -4,7 +4,6 @@
<string name="address_type_legacy">遺留資產</string>
<string name="common_fee_error">獲取費用失敗</string>
<string name="kaspa_withdrawal_message_warning">由於 Kaspa 的限制,只有%1$d UTXO 可以放入單次交易中。這意味著您只能發送%2$s或更少數量。您需要減少數量。</string>
<string name="no_account_generic">加載 %1$s+ %2$s 以創建帳戶</string>
<string name="no_account_polkadot">目標帳戶未激活。發送 %s 或更多以激活帳戶</string>
<string name="send_error_dust_amount_format">最小數量是 %s</string>
<string name="send_error_dust_change">更動太小</string>

View file

@ -13,6 +13,7 @@ import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
@ -162,11 +163,12 @@ fun CardWithIcon(
* >Figma component</a>
*/
@Composable
fun IconWithTitleAndDescription(
internal fun IconWithTitleAndDescription(
title: String,
description: String,
description: String?,
icon: @Composable () -> Unit,
additionalContent: @Composable () -> Unit = {},
iconBackground: Color = TangemTheme.colors.background.secondary,
) {
Row(
modifier = Modifier
@ -182,7 +184,7 @@ fun IconWithTitleAndDescription(
Box(
modifier = Modifier
.background(
color = TangemTheme.colors.background.secondary,
color = iconBackground,
shape = CircleShape,
)
.height(TangemTheme.dimens.size40)
@ -205,12 +207,14 @@ fun IconWithTitleAndDescription(
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.subtitle1,
)
SpacerH4()
Text(
text = description,
color = TangemTheme.colors.text.secondary,
style = TangemTheme.typography.body2,
)
if (description != null) {
SpacerH4()
Text(
text = description,
color = TangemTheme.colors.text.secondary,
style = TangemTheme.typography.body2,
)
}
}
additionalContent()

View file

@ -23,7 +23,7 @@ import com.tangem.core.ui.res.TangemTheme
@Composable
fun TangemSwitch(
onCheckedChange: (Boolean) -> Unit,
checkedColor: Color = TangemTheme.colors.icon.accent,
checkedColor: Color = TangemTheme.colors.control.checked,
uncheckedColor: Color = TangemTheme.colors.icon.informative,
size: Dp = 48.dp,
checked: Boolean = false,

View file

@ -9,6 +9,7 @@ import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
@ -36,6 +37,28 @@ fun WarningCard(title: String, description: String, icon: @Composable (() -> Uni
)
}
/**
* A card with a warning icon to the left and title without description shown to the right of it.
*
* @param title title of the warning in bold
*
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=290%3A217&t=yMepJZTRh5bLkOoJ-1"
* >Figma component</a>
*/
@Composable
fun WarningCardTitleOnly(title: String, icon: @Composable (() -> Unit)? = null) {
WarningCardMaterial3Style(
content = {
WarningBody(
title = title,
description = null,
icon = icon,
iconBackground = TangemTheme.colors.button.disabled,
)
},
)
}
/**
* [WarningCard], but clickable (with an 'greater then' icon to the left)
*
@ -105,7 +128,8 @@ fun RefreshableWaringCard(
@Composable
private fun WarningBody(
title: String,
description: String,
description: String?,
iconBackground: Color = TangemTheme.colors.background.secondary,
icon: @Composable (() -> Unit)? = null,
additionalContent: @Composable () -> Unit = {},
) {
@ -119,6 +143,7 @@ private fun WarningBody(
contentDescription = null,
)
},
iconBackground = iconBackground,
)
}
@ -136,6 +161,20 @@ private fun WarningCardSurface(onClick: (() -> Unit)? = null, content: @Composab
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun WarningCardMaterial3Style(onClick: (() -> Unit)? = null, content: @Composable () -> Unit) {
Card(
shape = RoundedCornerShape(TangemTheme.dimens.radius16),
backgroundColor = TangemTheme.colors.button.disabled,
elevation = TangemTheme.dimens.elevation0,
onClick = onClick ?: {},
enabled = onClick != null,
) {
content()
}
}
// endregion elements
// region Preview

View file

@ -4,10 +4,13 @@ import androidx.annotation.DrawableRes
import androidx.compose.animation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@ -39,7 +42,10 @@ fun AppBarWithBackButtonAndIcon(
contentDescription = null,
modifier = Modifier
.size(size = TangemTheme.dimens.size24)
.clickable { onBackClick() },
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false),
) { onBackClick() },
tint = TangemTheme.colors.icon.primary1,
)
AnimatedContent(

View file

@ -6,6 +6,7 @@ import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.SheetState
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import com.tangem.core.ui.res.TangemTheme
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
@ -20,6 +21,7 @@ import kotlinx.coroutines.launch
@Composable
inline fun <reified T : TangemBottomSheetConfigContent> TangemBottomSheet(
config: TangemBottomSheetConfig,
contentColor: Color = TangemTheme.colors.background.primary,
crossinline content: @Composable ColumnScope.(T) -> Unit,
) {
var isVisible by remember { mutableStateOf(value = config.isShow) }
@ -29,7 +31,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> TangemBottomSheet(
ModalBottomSheet(
onDismissRequest = config.onDismissRequest,
sheetState = sheetState,
containerColor = TangemTheme.colors.background.primary,
containerColor = contentColor,
shape = TangemTheme.shapes.bottomSheetLarge,
dragHandle = { TangemBottomSheetDraggableHeader() },
) {

View file

@ -1,4 +1,4 @@
package com.tangem.core.ui.components.fields
package com.tangem.core.ui.components.fields.visualtransformations
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.buildAnnotatedString

View file

@ -0,0 +1,87 @@
package com.tangem.core.ui.components.transactions
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
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.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.toDateFormat
import com.tangem.core.ui.utils.toTimeFormat
/**
* Common transaction done screen title
*
* @param titleRes title resource
* @param date transaction timestamp in millis
*/
@Composable
fun TransactionDoneTitle(@StringRes titleRes: Int, date: Long, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(id = R.drawable.ic_empty_in_process_64),
contentDescription = null,
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing8)
.size(TangemTheme.dimens.size64),
)
Text(
text = stringResource(id = titleRes),
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing32),
)
Text(
text = stringResource(id = R.string.send_date_format, date.toDateFormat(), date.toTimeFormat()),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing4),
)
}
}
// region Previews
@Preview
@Composable
private fun TransactionDoneTitlePreview_Light() {
TangemTheme {
TransactionDoneTitle(
titleRes = R.string.sent_transaction_sent_title,
date = 0,
modifier = Modifier
.background(TangemTheme.colors.background.tertiary)
.padding(TangemTheme.dimens.spacing16),
)
}
}
@Preview
@Composable
private fun TransactionDoneTitlePreview_Dark() {
TangemTheme(isDark = true) {
TransactionDoneTitle(
titleRes = R.string.sent_transaction_sent_title,
date = 0,
modifier = Modifier
.background(TangemTheme.colors.background.tertiary)
.padding(TangemTheme.dimens.spacing16),
)
}
}
// endregion

View file

@ -7,18 +7,34 @@ import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import com.tangem.core.ui.res.TangemTheme
fun Modifier.roundedShapeItemDecoration(currentIndex: Int, lastIndex: Int): Modifier = composed {
val modifierWithHorizontalPadding = this.padding(horizontal = TangemTheme.dimens.spacing16)
fun Modifier.roundedShapeItemDecoration(
currentIndex: Int,
lastIndex: Int,
addDefaultPadding: Boolean = true,
): Modifier = composed {
val modifier = if (addDefaultPadding) this.padding(horizontal = TangemTheme.dimens.spacing16) else this
val isSingleItem = currentIndex == 0 && lastIndex == 0
when {
isSingleItem -> {
modifierWithHorizontalPadding
.padding(top = TangemTheme.dimens.spacing14)
modifier
.then(
if (addDefaultPadding) {
Modifier.padding(top = TangemTheme.dimens.spacing14)
} else {
Modifier
},
)
.clip(shape = TangemTheme.shapes.roundedCornersXMedium)
}
currentIndex == 0 -> {
modifierWithHorizontalPadding
.padding(top = TangemTheme.dimens.spacing14)
modifier
.then(
if (addDefaultPadding) {
Modifier.padding(top = TangemTheme.dimens.spacing14)
} else {
Modifier
},
)
.clip(
shape = RoundedCornerShape(
topStart = TangemTheme.dimens.radius16,
@ -27,7 +43,7 @@ fun Modifier.roundedShapeItemDecoration(currentIndex: Int, lastIndex: Int): Modi
)
}
currentIndex == lastIndex -> {
modifierWithHorizontalPadding
modifier
.clip(
shape = RoundedCornerShape(
bottomStart = TangemTheme.dimens.radius16,
@ -35,6 +51,6 @@ fun Modifier.roundedShapeItemDecoration(currentIndex: Int, lastIndex: Int): Modi
),
)
}
else -> modifierWithHorizontalPadding
else -> modifier
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.core.ui.event
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.NonRestartableComposable
import kotlinx.coroutines.launch
/**
* A Composable function that reacts to a given [StateEvent], executing the provided action only once when the event
@ -17,8 +18,8 @@ import androidx.compose.runtime.NonRestartableComposable
fun <A> EventEffect(event: StateEvent<A>, onTrigger: suspend (data: A) -> Unit) {
LaunchedEffect(event) {
if (event is StateEvent.Triggered<A>) {
onTrigger(event.data)
event.onConsume()
launch { onTrigger(event.data) }
.invokeOnCompletion { event.onConsume() }
}
}
}

View file

@ -32,6 +32,16 @@ fun CryptoCurrency.Token.tryGetBackgroundForTokenIcon(
): Color {
if (isGrayscale) return TangemColorPalette.Dark2
return tryGetBackgroundForTokenIcon(contractAddress = contractAddress, fallbackColor = fallbackColor)
}
/**
* Tries to extract a background color from the contract address of a token.
*
* @param fallbackColor The color to use as a fallback.
* @return The extracted background color or the fallback color if extraction fails or if it is a test network token.
*/
fun tryGetBackgroundForTokenIcon(contractAddress: String, fallbackColor: Color = TangemColorPalette.Black): Color {
return try {
val colorHex = "#" + contractAddress.substring(range = COLOR_HEX_START_INDEX..COLOR_HEX_END_INDEX)
Color(colorHex.toColorInt())

View file

@ -1,5 +1,6 @@
package com.tangem.core.ui.utils
import com.tangem.domain.tokens.model.CryptoCurrency
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.NumberFormat
@ -24,6 +25,10 @@ object BigDecimalFormatter {
return formatter.format(cryptoAmount) + "\u2009$cryptoCurrency"
}
fun formatCryptoAmount(cryptoAmount: BigDecimal?, cryptoCurrency: CryptoCurrency): String {
return formatCryptoAmount(cryptoAmount, cryptoCurrency.symbol, cryptoCurrency.decimals)
}
fun formatFiatAmount(
fiatAmount: BigDecimal?,
fiatCurrencyCode: String,

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<path
android:pathData="M28.384,0.616C27.896,0.128 27.104,0.128 26.616,0.616C26.128,1.104 26.128,1.896 26.616,2.384L31.002,6.769C17.519,7.294 6.75,18.389 6.75,32C6.75,45.945 18.055,57.25 32,57.25C45.945,57.25 57.25,45.945 57.25,32C57.25,31.31 56.69,30.75 56,30.75C55.31,30.75 54.75,31.31 54.75,32C54.75,44.564 44.564,54.75 32,54.75C19.435,54.75 9.25,44.564 9.25,32C9.25,19.785 18.878,9.818 30.959,9.273L26.616,13.616C26.128,14.104 26.128,14.896 26.616,15.384C27.104,15.872 27.896,15.872 28.384,15.384L34.884,8.884C35.372,8.396 35.372,7.604 34.884,7.116L28.384,0.616ZM39,22.75H40.25V24V26V26.518L39.884,26.884L33.768,33L39.884,39.116L40.25,39.482V40V42V43.25H39H25H23.75V42V40V39.482L24.116,39.116L30.232,33L24.116,26.884L23.75,26.518V26V24V22.75H25H39ZM26.25,25.482L32,31.232L37.75,25.482V25.25H26.25V25.482ZM37.75,40.518L32,34.768L26.25,40.518V40.75H37.75V40.518Z"
android:fillColor="#C9C9C9"
android:fillType="evenOdd"/>
</vector>

View file

@ -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">
<group>
<clip-path
android:pathData="M0,0h24v24h-24z"/>
<path
android:pathData="M12,22C10.633,22 9.342,21.737 8.125,21.212C6.908,20.688 5.846,19.971 4.938,19.063C4.029,18.154 3.313,17.092 2.787,15.875C2.263,14.658 2,13.367 2,12C2,10.617 2.263,9.321 2.787,8.113C3.313,6.904 4.029,5.846 4.938,4.938C5.846,4.029 6.908,3.313 8.125,2.787C9.342,2.263 10.633,2 12,2C13.383,2 14.679,2.263 15.887,2.787C17.096,3.313 18.154,4.029 19.063,4.938C19.971,5.846 20.688,6.904 21.212,8.113C21.737,9.321 22,10.617 22,12C22,13.367 21.737,14.658 21.212,15.875C20.688,17.092 19.971,18.154 19.063,19.063C18.154,19.971 17.096,20.688 15.887,21.212C14.679,21.737 13.383,22 12,22ZM12,19.95C12.433,19.35 12.808,18.725 13.125,18.075C13.442,17.425 13.7,16.733 13.9,16H10.1C10.3,16.733 10.558,17.425 10.875,18.075C11.192,18.725 11.567,19.35 12,19.95ZM9.4,19.55C9.1,19 8.837,18.429 8.613,17.837C8.387,17.246 8.2,16.633 8.05,16H5.1C5.583,16.833 6.188,17.558 6.912,18.175C7.637,18.792 8.467,19.25 9.4,19.55ZM14.6,19.55C15.533,19.25 16.362,18.792 17.087,18.175C17.813,17.558 18.417,16.833 18.9,16H15.95C15.8,16.633 15.613,17.246 15.387,17.837C15.163,18.429 14.9,19 14.6,19.55ZM4.25,14H7.65C7.6,13.667 7.563,13.337 7.537,13.012C7.512,12.688 7.5,12.35 7.5,12C7.5,11.65 7.512,11.313 7.537,10.988C7.563,10.663 7.6,10.333 7.65,10H4.25C4.167,10.333 4.104,10.663 4.063,10.988C4.021,11.313 4,11.65 4,12C4,12.35 4.021,12.688 4.063,13.012C4.104,13.337 4.167,13.667 4.25,14ZM9.65,14H14.35C14.4,13.667 14.438,13.337 14.462,13.012C14.488,12.688 14.5,12.35 14.5,12C14.5,11.65 14.488,11.313 14.462,10.988C14.438,10.663 14.4,10.333 14.35,10H9.65C9.6,10.333 9.563,10.663 9.538,10.988C9.512,11.313 9.5,11.65 9.5,12C9.5,12.35 9.512,12.688 9.538,13.012C9.563,13.337 9.6,13.667 9.65,14ZM16.35,14H19.75C19.833,13.667 19.896,13.337 19.938,13.012C19.979,12.688 20,12.35 20,12C20,11.65 19.979,11.313 19.938,10.988C19.896,10.663 19.833,10.333 19.75,10H16.35C16.4,10.333 16.438,10.663 16.462,10.988C16.487,11.313 16.5,11.65 16.5,12C16.5,12.35 16.487,12.688 16.462,13.012C16.438,13.337 16.4,13.667 16.35,14ZM15.95,8H18.9C18.417,7.167 17.813,6.442 17.087,5.825C16.362,5.208 15.533,4.75 14.6,4.45C14.9,5 15.163,5.571 15.387,6.162C15.613,6.754 15.8,7.367 15.95,8ZM10.1,8H13.9C13.7,7.267 13.442,6.575 13.125,5.925C12.808,5.275 12.433,4.65 12,4.05C11.567,4.65 11.192,5.275 10.875,5.925C10.558,6.575 10.3,7.267 10.1,8ZM5.1,8H8.05C8.2,7.367 8.387,6.754 8.613,6.162C8.837,5.571 9.1,5 9.4,4.45C8.467,4.75 7.637,5.208 6.912,5.825C6.188,6.442 5.583,7.167 5.1,8Z"
android:fillColor="#1E1E1E"/>
</group>
</vector>