Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-04 20:09:55 +03:00
commit a33afd3583
684 changed files with 8006 additions and 4239 deletions

View file

@ -32,9 +32,9 @@ fun AppBarWithBackButton(
TangemTopAppBar(
modifier = modifier,
title = text,
startButton = TopAppBarButtonUM(
startButton = TopAppBarButtonUM.Icon(
iconRes = iconRes ?: R.drawable.ic_back_24,
onIconClicked = onBackClick,
onClicked = onBackClick,
),
containerColor = containerColor,
)

View file

@ -27,14 +27,14 @@ fun AppBarWithBackButtonAndIcon(
title = text,
subtitle = subtitle,
containerColor = backgroundColor,
startButton = TopAppBarButtonUM(
startButton = TopAppBarButtonUM.Icon(
iconRes = backIconRes ?: R.drawable.ic_back_24,
onIconClicked = onBackClick,
onClicked = onBackClick,
),
endButton = if (iconRes != null && onIconClick != null) {
TopAppBarButtonUM(
TopAppBarButtonUM.Icon(
iconRes = iconRes,
onIconClicked = onIconClick,
onClicked = onIconClick,
)
} else {
null

View file

@ -10,6 +10,7 @@ import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -20,6 +21,7 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.TopAppBarTestTags
/**
* [TangemTopAppBar] height options.
@ -127,6 +129,7 @@ fun TangemTopAppBar(
TopAppBarButton(
button = endButton,
tint = iconTint,
modifier = Modifier.testTag(TopAppBarTestTags.MORE_BUTTON),
)
}
},
@ -172,6 +175,7 @@ fun TangemTopAppBar(
TopAppBarButton(
button = startButton,
tint = iconTint,
modifier = Modifier.testTag(TopAppBarTestTags.CLOSE_BUTTON),
)
}
}
@ -222,6 +226,7 @@ private fun TopAppBarTitle(
color = textColor,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.testTag(TopAppBarTestTags.TITLE),
)
AnimatedVisibility(
@ -296,25 +301,25 @@ private class BasicTopAppBarPMPreviewProvider : PreviewParameterProvider<BasicTo
height = TangemTopAppBarHeight.BOTTOM_SHEET,
),
BasicTopAppBarPM(
startButton = TopAppBarButtonUM(
startButton = TopAppBarButtonUM.Icon(
iconRes = R.drawable.ic_scan_24,
onIconClicked = {},
onClicked = {},
),
endButton = TopAppBarButtonUM(
endButton = TopAppBarButtonUM.Icon(
iconRes = R.drawable.ic_more_vertical_24,
onIconClicked = {},
onClicked = {},
),
),
BasicTopAppBarPM(
startButton = TopAppBarButtonUM(
startButton = TopAppBarButtonUM.Icon(
iconRes = R.drawable.ic_scan_24,
onIconClicked = {},
onClicked = {},
),
),
BasicTopAppBarPM(
endButton = TopAppBarButtonUM(
endButton = TopAppBarButtonUM.Icon(
iconRes = R.drawable.ic_more_vertical_24,
onIconClicked = {},
onClicked = {},
),
height = TangemTopAppBarHeight.BOTTOM_SHEET,
),
@ -322,13 +327,13 @@ private class BasicTopAppBarPMPreviewProvider : PreviewParameterProvider<BasicTo
title = "1234567891011121314151617181920",
subtitle = "12345678910111213141516171819202122232425",
titleAlignment = Alignment.Start,
startButton = TopAppBarButtonUM(
startButton = TopAppBarButtonUM.Icon(
iconRes = R.drawable.ic_scan_24,
onIconClicked = {},
onClicked = {},
),
endButton = TopAppBarButtonUM(
endButton = TopAppBarButtonUM.Icon(
iconRes = R.drawable.ic_more_vertical_24,
onIconClicked = {},
onClicked = {},
),
),
)

View file

@ -1,27 +1,49 @@
package com.tangem.core.ui.components.appbar
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
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.unit.dp
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
@Composable
fun TopAppBarButton(button: TopAppBarButtonUM, tint: Color, modifier: Modifier = Modifier) {
IconButton(
enabled = button.enabled,
modifier = modifier.size(TangemTheme.dimens.size32),
onClick = button.onIconClicked,
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size24),
painter = painterResource(id = button.iconRes),
tint = tint,
contentDescription = null,
)
when (button) {
is TopAppBarButtonUM.Icon -> {
IconButton(
enabled = button.enabled,
modifier = modifier.size(TangemTheme.dimens.size32),
onClick = button.onClicked,
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size24),
painter = painterResource(id = button.iconRes),
tint = tint,
contentDescription = null,
)
}
}
is TopAppBarButtonUM.Text -> {
Text(
modifier = modifier
.conditional(button.enabled) {
clickable { button.onClicked() }
}
.padding(4.dp),
text = button.text.resolveReference(),
color = tint,
style = TangemTheme.typography.body1,
)
}
}
}

View file

@ -2,21 +2,39 @@ package com.tangem.core.ui.components.appbar.models
import androidx.annotation.DrawableRes
import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.TextReference
data class TopAppBarButtonUM(
@DrawableRes val iconRes: Int,
val onIconClicked: () -> Unit,
val enabled: Boolean = true,
sealed class TopAppBarButtonUM(
open val onClicked: () -> Unit,
open val enabled: Boolean = true,
) {
data class Icon(
@DrawableRes val iconRes: Int,
override val onClicked: () -> Unit,
override val enabled: Boolean = true,
) : TopAppBarButtonUM(onClicked, enabled)
data class Text(
val text: TextReference,
override val onClicked: () -> Unit,
override val enabled: Boolean = true,
) : TopAppBarButtonUM(onClicked, enabled)
@Suppress("FunctionName")
companion object {
fun Back(onBackClicked: () -> Unit) = Back(true, onBackClicked)
fun Back(enabled: Boolean = true, onBackClicked: () -> Unit) = TopAppBarButtonUM(
fun Back(enabled: Boolean = true, onBackClicked: () -> Unit) = Icon(
iconRes = R.drawable.ic_back_24,
onIconClicked = onBackClicked,
onClicked = onBackClicked,
enabled = enabled,
)
fun Text(text: TextReference, onTextClicked: () -> Unit, enabled: Boolean = true) = Text(
text = text,
onClicked = onTextClicked,
enabled = enabled,
)
}

View file

@ -57,9 +57,9 @@ private fun Preview_TangemBottomSheetTitle() {
TangemThemePreview {
TangemBottomSheetTitle(
title = "Title",
endButton = TopAppBarButtonUM(
endButton = TopAppBarButtonUM.Icon(
iconRes = R.drawable.ic_information_24,
onIconClicked = {},
onClicked = {},
),
containerColor = TangemTheme.colors.background.secondary,
)

View file

@ -25,7 +25,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.tangem.core.ui.components.ResizableText
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.DialogTestTags
import com.tangem.core.ui.test.BaseButtonTestTags
import com.tangem.core.ui.utils.MultipleClickPreventer
@Suppress("LongParameterList")
@ -51,7 +51,7 @@ fun TangemButton(
Button(
modifier = modifier
.heightIn(min = size.toHeightDp())
.testTag(DialogTestTags.BUTTON),
.testTag(BaseButtonTestTags.BUTTON),
onClick = {
multipleClickPreventer.processEvent { if (!showProgress) onClick() }
},

View file

@ -5,7 +5,7 @@ import com.tangem.core.ui.extensions.getTintForTokenIcon
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.utils.converter.Converter
/**

View file

@ -21,6 +21,7 @@ import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.SoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
@ -35,6 +36,7 @@ import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.SelectCountryBottomSheetTestTags
@Composable
fun SearchBar(
@ -57,7 +59,8 @@ fun SearchBar(
} else {
state.onActiveChange(false)
}
},
}
.testTag(SelectCountryBottomSheetTestTags.SEARCH_BAR),
enabled = enabled,
value = state.query,
onValueChange = state.onQueryChange,

View file

@ -10,6 +10,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment.Companion.CenterEnd
@ -19,6 +20,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.buttons.small.TangemIconButton
import com.tangem.core.ui.components.fields.SimpleTextField
@ -69,6 +71,7 @@ fun InputRowRecipient(
showDivider: Boolean = false,
isLoading: Boolean = false,
isValuePasted: Boolean = false,
resolvedAddress: String? = null,
) {
val (titleText, color) = if (isError && error != null) {
error to TangemTheme.colors.text.warning
@ -144,11 +147,15 @@ fun InputRowRecipient(
} else {
TangemTheme.colors.text.primary2
},
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing8),
modifier = Modifier.padding(start = TangemTheme.dimens.spacing8),
)
}
}
ResolvedAddressRow(
isLoading = isLoading,
resolvedAddress = resolvedAddress,
)
}
}
}
@ -203,6 +210,38 @@ private fun RowScope.InputIcon(isLoading: Boolean, value: String) {
}
}
@Composable
private fun ResolvedAddressRow(isLoading: Boolean, resolvedAddress: String?) {
AnimatedContent(
targetState = if (resolvedAddress.isNullOrBlank() || isLoading) {
ResolvedState.Hide
} else {
ResolvedState.Show(resolvedAddress)
},
label = "Resolved Address",
) { state ->
if (state is ResolvedState.Show) {
Column {
HorizontalDivider(
thickness = 0.5.dp,
modifier = Modifier.padding(top = 12.dp, bottom = 12.dp),
color = TangemTheme.colors.stroke.primary,
)
Text(
text = state.address,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
}
}
}
private sealed interface ResolvedState {
data object Hide : ResolvedState
data class Show(val address: String) : ResolvedState
}
//region preview
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@ -224,6 +263,7 @@ private fun InputRowRecipientPreview(
onQrCodeClick = {},
modifier = Modifier.background(TangemTheme.colors.background.primary),
isRedesignEnabled = false,
resolvedAddress = value.resolvedAddress,
)
}
}
@ -232,6 +272,7 @@ private data class InputRowRecipientPreviewData(
val value: String,
val isError: Boolean,
val isLoading: Boolean = false,
val resolvedAddress: String? = null,
)
private class InputRowRecipientPreviewDataProvider : PreviewParameterProvider<InputRowRecipientPreviewData> {
@ -250,6 +291,12 @@ private class InputRowRecipientPreviewDataProvider : PreviewParameterProvider<In
isLoading = true,
isError = true,
),
InputRowRecipientPreviewData(
value = "vitalik.eth",
isLoading = false,
isError = true,
resolvedAddress = "0x391316d97a07027a0702c8A002c8A0C25d8470",
),
)
}
//endregion

View file

@ -20,6 +20,7 @@ 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.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
@ -35,6 +36,7 @@ import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.NotificationTestTags
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState as NotificationButtonsState
/**
@ -206,6 +208,7 @@ internal fun TextsBlock(
text = titleText,
color = titleColor,
style = TangemTheme.typography.button,
modifier = Modifier.testTag(NotificationTestTags.TITLE),
)
SpacerH(height = TangemTheme.dimens.spacing2)
@ -217,6 +220,7 @@ internal fun TextsBlock(
text = subtitleText,
color = subtitleColor,
style = TangemTheme.typography.caption2,
modifier = Modifier.testTag(NotificationTestTags.TEXT),
)
}
}

View file

@ -23,6 +23,7 @@ import androidx.constraintlayout.compose.Visibility
import coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
import com.tangem.core.ui.R
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.audits.AuditLabel
import com.tangem.core.ui.components.audits.AuditLabelUM
import com.tangem.core.ui.components.badge.Badge
@ -50,7 +51,6 @@ private const val DISABLED_ICON_ALPHA = 0.4f
fun ProviderChooseCrypto(providerChooseUM: ProviderChooseUM, onClick: () -> Unit, modifier: Modifier = Modifier) {
ConstraintLayout(
modifier = modifier
.background(TangemTheme.colors.background.action)
.clip(RoundedCornerShape(14.dp))
.selectedBorder(isSelected = providerChooseUM.isSelected)
.clickable(
@ -133,13 +133,23 @@ private fun IconContent(iconUrl: String, modifier: Modifier = Modifier) {
SubcomposeAsyncImage(
modifier = modifier
.size(40.dp)
.clip(RoundedCornerShape(8.dp))
.background(TangemColorPalette.Light1),
.clip(RoundedCornerShape(8.dp)),
model = ImageRequest.Builder(context = LocalContext.current)
.data(iconUrl)
.crossfade(enable = true)
.allowHardware(false)
.build(),
loading = {
RectangleShimmer(radius = 8.dp)
},
error = {
Box(
modifier = Modifier.background(
color = TangemColorPalette.Light1,
shape = RoundedCornerShape(8.dp),
),
)
},
contentDescription = null,
)
}

View file

@ -0,0 +1,5 @@
package com.tangem.core.ui.test
object BaseButtonTestTags {
const val BUTTON = "BASE_BUTTON"
}

View file

@ -0,0 +1,16 @@
package com.tangem.core.ui.test
object BuyTokenDetailsScreenTestTags {
const val EXPAND_FIAT_LIST_BUTTON = "BUY_TOKEN_DETAILS_SCREEN_EXPAND_FIAT_LIST_BUTTON"
const val FIAT_CURRENCY_ICON = "BUY_TOKEN_DETAILS_SCREEN_FIAT_CURRENCY_ICON"
const val FIAT_AMOUNT_TEXT_FIELD = "BUY_TOKEN_DETAILS_SCREEN_FIAT_AMOUNT_TEXT_FIELD"
const val TOKEN_AMOUNT = "BUY_TOKEN_DETAILS_SCREEN_TOKEN_AMOUNT"
const val PROVIDER_LOADING_TITLE = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_LOADING_TITLE"
const val PROVIDER_LOADING_TEXT = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_LOADING_TITLE"
const val PROVIDER_TITLE = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_TITLE"
const val PROVIDER_TEXT = "BUY_TOKEN_DETAILS_SCREEN_PROVIDER_TEXT"
const val TOS_BLOCK = "BUY_TOKEN_DETAILS_SCREEN_TOS_BLOCK"
}

View file

@ -0,0 +1,6 @@
package com.tangem.core.ui.test
object BuyTokenFiatListTestTags {
const val LAZY_LIST = "BUY_TOKEN_FIAT_LIST_LAZY_LIST"
const val LAZY_LIST_ITEM = "BUY_TOKEN_FIAT_LIST_LAZY_LIST_ITEM"
}

View file

@ -0,0 +1,6 @@
package com.tangem.core.ui.test
object BuyTokenScreenTestTags {
const val LAZY_LIST = "BUY_TOKEN_SCREEN_LAZY_LIST"
const val LAZY_LIST_ITEM = "BUY_TOKEN_SCREEN_LAZY_LIST_ITEM"
}

View file

@ -2,5 +2,4 @@ package com.tangem.core.ui.test
object DialogTestTags {
const val DIALOG_CONTAINER = "DIALOG_CONTAINER"
const val BUTTON = "DIALOG_BUTTON"
}

View file

@ -3,4 +3,5 @@ package com.tangem.core.ui.test
object DisclaimerScreenTestTags {
const val SCREEN_CONTAINER = "DISCLAIMER_SCREEN_CONTAINER"
const val ACCEPT_BUTTON = "DISCLAIMER_SCREEN_ACCEPT_BUTTON"
const val WEB_VIEW = "DISCLAIMER_SCREEN_WEB_VIEW"
}

View file

@ -8,4 +8,5 @@ object MainScreenTestTags {
const val WALLET_BALANCE = "MAIN_SCREEN_WALLET_BALANCE"
const val WALLET_LIST_ITEM = "MAIN_SCREEN_WALLET_LIST_ITEM"
const val ORGANIZE_TOKENS_BUTTON = "MAIN_SCREEN_ORGANIZE_TOKENS_BUTTON"
const val MULTI_CURRENCY_ACTION_BUTTON = "MAIN_SCREEN_MULTI_CURRENCY_ACTION_BUTTON"
}

View file

@ -0,0 +1,6 @@
package com.tangem.core.ui.test
object NotificationTestTags {
const val TITLE = "NOTIFICATION_TITLE"
const val TEXT = "NOTIFICATION_TEXT"
}

View file

@ -0,0 +1,5 @@
package com.tangem.core.ui.test
object ResidenceSettingsScreenTestTags {
const val COUNTRY_NAME = "RESIDENCE_SETTINGS_SCREEN_COUNTRY_NAME"
}

View file

@ -0,0 +1,12 @@
package com.tangem.core.ui.test
object SelectCountryBottomSheetTestTags {
const val LAZY_LIST = "SELECT_COUNTRY_BOTTOM_SHEET_LAZY_LIST"
const val COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ITEM"
const val UNAVAILABLE_COUNTRY_ITEM = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ITEM"
const val SEARCH_BAR = "SELECT_COUNTRY_BOTTOM_SHEET_SEARCH_BAR"
const val COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_ICON"
const val UNAVAILABLE_COUNTRY_ICON = "SELECT_COUNTRY_BOTTOM_SHEET_UNAVAILABLE_COUNTRY_ICON"
const val COUNTRY_NAME = "SELECT_COUNTRY_BOTTOM_SHEET_COUNTRY_NAME"
}

View file

@ -0,0 +1,7 @@
package com.tangem.core.ui.test
object SelectPaymentMethodBottomSheetTestTags {
const val LAZY_LIST = "SELECT_PAYMENT_METHOD_LAZY_LIST"
const val PAYMENT_METHOD_ICON = "PAYMENT_METHOD_NAME"
}

View file

@ -0,0 +1,18 @@
package com.tangem.core.ui.test
object SelectProviderBottomSheetTestTags {
const val PAYMENT_METHOD_ICON = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_ICON"
const val PAYMENT_METHOD_TITLE = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_TITLE"
const val PAYMENT_METHOD_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_NAME"
const val PAYMENT_METHOD_EXPAND_BUTTON = "SELECT_PROVIDER_BOTTOM_SHEET_PAYMENT_METHOD_EXPAND_BUTTON"
const val TOKEN_AMOUNT = "SELECT_PROVIDER_BOTTOM_SHEET_TOKEN_AMOUNT"
const val AVAILABLE_PROVIDER_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_AVAILABLE_PROVIDER_NAME"
const val AVAILABLE_PROVIDER_ITEM = "SELECT_PROVIDER_BOTTOM_SHEET_AVAILABLE_PROVIDER_ITEM"
const val UNAVAILABLE_PROVIDER_ITEM = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_ITEM"
const val UNAVAILABLE_PROVIDER_NAME = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_NAME"
const val UNAVAILABLE_PROVIDER_SUBTITLE = "SELECT_PROVIDER_BOTTOM_SHEET_UNAVAILABLE_PROVIDER_SUBTITLE"
const val MORE_PROVIDERS_ICON = "SELECT_PROVIDER_BOTTOM_SHEET_MORE_PROVIDERS_ICON"
const val MORE_PROVIDERS_TEXT = "SELECT_PROVIDER_BOTTOM_SHEET_MORE_PROVIDERS_TEXT"
const val BEST_RATE_LABEL = "SELECT_PROVIDER_BOTTOM_SHEET_BEST_RATE_LABEL"
}

View file

@ -0,0 +1,7 @@
package com.tangem.core.ui.test
object TopAppBarTestTags {
const val TITLE = "TOP_APP_BAR_TITLE"
const val MORE_BUTTON = "TOP_APP_BAR_MORE_BUTTON"
const val CLOSE_BUTTON = "TOP_APP_BAR_CLOSE_BUTTON"
}

View file

@ -0,0 +1,54 @@
package com.tangem.core.ui.utils
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
/**
[REDACTED_AUTHOR]
*/
typealias AnimatedValue = Pair<Float, Float>
@Composable
fun AnimatedValue.toAnimatable(
isPaused: Boolean,
duration: Int,
easing: Easing = LinearEasing,
): Animatable<Float, AnimationVector1D> {
return animatable(
values = this,
isPaused = isPaused,
duration = duration,
easing = easing,
)
}
@Composable
fun animatable(
values: AnimatedValue,
duration: Int,
isPaused: Boolean = false,
easing: Easing = LinearEasing,
): Animatable<Float, AnimationVector1D> {
val animatable = remember { Animatable(values.first) }
LaunchedEffect(isPaused) {
if (isPaused) {
animatable.stop()
} else {
animatable.animateTo(
targetValue = values.second,
animationSpec = tween(
durationMillis = duration,
easing = easing,
),
)
}
}
return animatable
}

View file

@ -1,9 +1,15 @@
package com.tangem.core.ui.utils
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import kotlin.math.roundToInt
@Stable
@Composable
@ -15,4 +21,16 @@ fun convertPxToDp(px: Float): Dp = convertPxToDp(px, density = LocalDensity.curr
fun Dp.toPx(density: Float): Float = this.value * density
fun convertPxToDp(px: Float, density: Float): Dp = Dp(value = px / density)
fun convertPxToDp(px: Float, density: Float): Dp = Dp(value = px / density)
fun Context.dpToPx(dp: Float): Float = dp * resources.displayMetrics.density
fun Context.pxToDp(px: Float): Float = (px / resources.displayMetrics.density).roundToInt().toFloat()
@Composable
fun Painter.dpSize(): DpSize = DpSize(
intrinsicSize.width.pxToDp().dp,
intrinsicSize.height.pxToDp().dp,
)
@Composable
private fun Float.pxToDp(): Float = LocalContext.current.pxToDp(this)

View file

@ -0,0 +1,20 @@
package com.tangem.core.ui.utils
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.core.graphics.drawable.toBitmap
/**
[REDACTED_AUTHOR]
*/
@Composable
fun asImageBitmap(@DrawableRes drawableId: Int): ImageBitmap {
val drawable = requireNotNull(AppCompatResources.getDrawable(LocalContext.current, drawableId)) {
"drawable is null"
}
return drawable.toBitmap().asImageBitmap()
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

View file

@ -0,0 +1,16 @@
<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.656,15.224C18.898,14.92 19.328,14.854 19.648,15.059L19.71,15.104L19.875,15.245C20.247,15.585 20.532,16.024 20.532,16.6C20.532,17.432 19.947,17.969 19.336,18.362C18.868,18.663 18.238,18.97 17.483,19.319L16.688,19.683L15.213,20.355C13.847,20.978 12.961,21.399 12,21.399C11.28,21.399 10.601,21.162 9.73,20.781L8.787,20.355L7.312,19.683C6.2,19.177 5.288,18.763 4.664,18.362C4.053,17.969 3.468,17.432 3.468,16.6C3.468,15.941 3.84,15.462 4.29,15.104L4.352,15.059C4.672,14.854 5.102,14.92 5.344,15.224C5.602,15.548 5.548,16.02 5.224,16.278L5.135,16.353C5.059,16.423 5.017,16.477 4.996,16.513C4.972,16.552 4.968,16.575 4.968,16.6C4.968,16.617 4.951,16.763 5.475,17.1C5.986,17.428 6.776,17.79 7.934,18.318L9.409,18.99L10.364,19.418C11.171,19.769 11.581,19.899 12,19.899C12.559,19.899 13.101,19.669 14.591,18.99L16.066,18.318L16.867,17.952C17.601,17.612 18.142,17.346 18.525,17.1C19.049,16.763 19.032,16.617 19.032,16.6C19.032,16.575 19.028,16.552 19.004,16.513C18.983,16.477 18.941,16.423 18.865,16.353L18.776,16.278L18.718,16.227C18.446,15.962 18.414,15.528 18.656,15.224Z"
android:fillColor="#1E1E1E"/>
<path
android:pathData="M3.791,10.784C4.031,10.446 4.499,10.367 4.836,10.607C5.174,10.847 5.253,11.315 5.013,11.653C4.972,11.711 4.968,11.741 4.968,11.766C4.968,11.783 4.951,11.929 5.475,12.266C5.986,12.594 6.776,12.956 7.934,13.484L9.409,14.156L10.364,14.584C11.171,14.936 11.581,15.065 12,15.065C12.559,15.065 13.101,14.835 14.591,14.156L16.066,13.484L16.867,13.118C17.601,12.778 18.142,12.512 18.525,12.266C19.049,11.929 19.032,11.783 19.032,11.766C19.032,11.753 19.031,11.74 19.025,11.722L18.987,11.653L18.946,11.588C18.758,11.258 18.847,10.832 19.164,10.607C19.48,10.382 19.911,10.437 20.162,10.723L20.209,10.784L20.282,10.893C20.44,11.152 20.532,11.444 20.532,11.766C20.532,12.598 19.947,13.135 19.336,13.528C18.868,13.829 18.238,14.137 17.483,14.486L16.688,14.849L15.213,15.521C13.847,16.143 12.961,16.565 12,16.565C11.28,16.565 10.601,16.328 9.73,15.947L8.787,15.521L7.312,14.849C6.2,14.342 5.288,13.929 4.664,13.528C4.053,13.135 3.468,12.598 3.468,11.766C3.468,11.398 3.588,11.069 3.791,10.784Z"
android:fillColor="#1E1E1E"/>
<path
android:pathData="M12,2.604C12.964,2.604 13.852,3.032 15.217,3.664L16.691,4.345L17.487,4.714C18.242,5.068 18.872,5.381 19.34,5.686C19.954,6.086 20.532,6.628 20.532,7.461C20.532,8.294 19.954,8.837 19.34,9.237C18.872,9.542 18.242,9.854 17.487,10.208L16.691,10.577L15.217,11.258C13.852,11.889 12.964,12.318 12,12.318C11.278,12.318 10.597,12.077 9.726,11.69L8.783,11.258L7.309,10.577C6.197,10.063 5.284,9.644 4.66,9.237C4.046,8.837 3.468,8.294 3.468,7.461C3.468,6.628 4.046,6.086 4.66,5.686C5.284,5.279 6.197,4.859 7.309,4.345L8.783,3.664L9.726,3.232C10.597,2.845 11.278,2.604 12,2.604ZM12,4.104C11.583,4.104 11.175,4.234 10.368,4.59L9.412,5.025L7.938,5.706C6.78,6.242 5.99,6.609 5.479,6.942C4.958,7.282 4.968,7.433 4.968,7.461C4.968,7.49 4.96,7.642 5.479,7.98C5.99,8.313 6.78,8.68 7.938,9.215L9.412,9.897L10.368,10.332C11.175,10.688 11.583,10.818 12,10.818C12.556,10.818 13.097,10.586 14.588,9.897L16.062,9.215L16.863,8.843C17.598,8.498 18.138,8.23 18.521,7.98C19.04,7.642 19.032,7.49 19.032,7.461C19.032,7.433 19.042,7.282 18.521,6.942C18.138,6.692 17.598,6.423 16.863,6.079L16.062,5.706L14.588,5.025C13.097,4.336 12.556,4.104 12,4.104Z"
android:fillColor="#1E1E1E"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="51dp"
android:height="11dp"
android:viewportWidth="74"
android:viewportHeight="16">
<path
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:pathData="M2.924,0H9.413C10.436,0 10.948,0 11.339,0.199C11.683,0.374 11.962,0.654 12.137,0.998C12.337,1.389 12.337,1.9 12.337,2.924V2.924V4.009H0V2.924C0,1.9 0,1.389 0.199,0.998C0.374,0.654 0.654,0.375 0.998,0.199C1.389,0 1.9,0 2.924,0ZM12.337,7.402H8.224L8.225,7.971V15.421H9.413H9.413C10.436,15.421 10.948,15.421 11.339,15.221C11.683,15.047 11.962,14.767 12.137,14.423C12.337,14.032 12.337,13.52 12.337,12.497V7.402ZM4.111,7.402H0V12.497C0,13.52 0,14.032 0.199,14.423C0.375,14.767 0.654,15.047 0.998,15.221C1.389,15.421 1.9,15.421 2.924,15.421H2.924H4.111V7.402ZM22.85,12.479C21.201,12.479 20.346,11.591 20.346,10.022V5.488H19.236V4.046H20.346V2.223H22.264V4.046H24.087V5.488H22.264V9.848C22.264,10.577 22.612,10.91 23.215,10.91C23.595,10.91 23.881,10.862 24.15,10.751V12.273C23.849,12.384 23.437,12.479 22.85,12.479ZM28.298,11.116C29.471,11.116 30.343,10.498 30.343,9.436V8.611H29.36C27.901,8.611 26.966,8.944 26.966,9.959C26.966,10.656 27.347,11.116 28.298,11.116ZM27.838,12.495C26.316,12.495 25.064,11.766 25.064,10.038C25.064,8.104 26.934,7.406 29.312,7.406H30.343V6.978C30.343,5.869 29.962,5.314 28.837,5.314C27.838,5.314 27.347,5.789 27.251,6.614H25.397C25.555,4.68 27.093,3.871 28.948,3.871C30.802,3.871 32.261,4.632 32.261,6.899V12.337H30.374V11.322C29.835,12.035 29.106,12.495 27.838,12.495ZM33.841,4.046V12.337H35.759V7.454C35.759,6.138 36.631,5.472 37.709,5.472C38.85,5.472 39.358,6.043 39.358,7.295V12.337H41.276V7.121C41.276,4.838 40.103,3.871 38.438,3.871C37.075,3.871 36.155,4.553 35.759,5.361V4.046H33.841ZM46.72,10.529C48.051,10.529 49.018,9.578 49.018,7.993V7.882C49.018,6.313 48.162,5.377 46.783,5.377C45.356,5.377 44.532,6.408 44.532,7.914V8.041C44.532,9.578 45.483,10.529 46.72,10.529ZM46.656,15.38C44.056,15.38 42.883,14.207 42.661,12.622H44.595C44.754,13.446 45.388,13.922 46.64,13.922C48.13,13.922 48.955,13.177 48.955,11.687V10.466C48.495,11.259 47.465,12.004 46.244,12.004C44.167,12.004 42.566,10.45 42.566,8.056V7.945C42.566,5.615 44.151,3.871 46.292,3.871C47.655,3.871 48.479,4.49 48.955,5.314V4.046H50.873V11.718C50.857,14.16 49.224,15.38 46.656,15.38ZM52.249,8.278C52.249,10.894 54.009,12.495 56.45,12.495C58.574,12.495 59.985,11.544 60.255,9.8H58.4C58.257,10.609 57.655,11.068 56.498,11.068C55.071,11.068 54.278,10.181 54.215,8.611H60.286V8.056C60.286,5.092 58.431,3.871 56.371,3.871C54.009,3.871 52.249,5.583 52.249,8.151V8.278ZM58.384,7.327H54.246C54.437,6.043 55.213,5.266 56.371,5.266C57.56,5.266 58.289,5.9 58.384,7.327ZM61.651,12.337V4.046H63.569V5.314C63.965,4.537 64.869,3.871 66.089,3.871C67.167,3.871 68.023,4.331 68.436,5.361C69.07,4.331 70.211,3.871 71.241,3.871C72.763,3.871 74,4.807 74,7.089V12.337H72.081V7.216C72.081,5.996 71.558,5.472 70.607,5.472C69.656,5.472 68.784,6.107 68.784,7.375V12.337H66.866V7.216C66.866,5.996 66.327,5.472 65.392,5.472C64.441,5.472 63.569,6.107 63.569,7.375V12.337H61.651Z" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB