Updated on 2026-08-14
This commit is contained in:
commit
e4754fed3b
483 changed files with 9315 additions and 4084 deletions
|
|
@ -0,0 +1,99 @@
|
|||
package com.tangem.core.ui.components.bottomsheets
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.inputrow.InputRowDefault
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Generic options bottom sheet component
|
||||
*
|
||||
* @param config Bottom sheet configuration containing OptionsBottomSheetContent
|
||||
* @param title Title text for the bottom sheet
|
||||
* @param containerColor Background color of the bottom sheet
|
||||
*/
|
||||
@Composable
|
||||
fun OptionsBottomSheet(
|
||||
config: TangemBottomSheetConfig,
|
||||
title: TextReference,
|
||||
containerColor: androidx.compose.ui.graphics.Color = TangemTheme.colors.background.tertiary,
|
||||
) {
|
||||
TangemBottomSheet<OptionsBottomSheetContent>(
|
||||
config = config,
|
||||
titleText = title,
|
||||
containerColor = containerColor,
|
||||
content = { content ->
|
||||
OptionsBottomSheetContent(content = content)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OptionsBottomSheetContent(content: OptionsBottomSheetContent) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
) {
|
||||
content.options.forEachIndexed { index, option ->
|
||||
InputRowDefault(
|
||||
text = option.label,
|
||||
showDivider = index < content.options.size - 1,
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = content.options.size - 1,
|
||||
addDefaultPadding = false,
|
||||
)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable { content.onOptionClick(option.key) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun OptionsBottomSheetPreview() {
|
||||
TangemThemePreview {
|
||||
OptionsBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
content = OptionsBottomSheetContent(
|
||||
options = persistentListOf(
|
||||
BottomSheetOption(
|
||||
key = "option1",
|
||||
label = TextReference.Str("First Option"),
|
||||
),
|
||||
BottomSheetOption(
|
||||
key = "option2",
|
||||
label = TextReference.Str("Second Option"),
|
||||
),
|
||||
BottomSheetOption(
|
||||
key = "option3",
|
||||
label = TextReference.Str("Third Option"),
|
||||
),
|
||||
),
|
||||
onOptionClick = {},
|
||||
),
|
||||
),
|
||||
title = TextReference.Str("Select Option"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.core.ui.components.bottomsheets
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* @param key Unique identifier for the option
|
||||
* @param label Display text for the option
|
||||
*/
|
||||
data class BottomSheetOption(
|
||||
val key: String,
|
||||
val label: TextReference,
|
||||
)
|
||||
|
||||
/**
|
||||
* @param options List of options to display
|
||||
* @param onOptionClick Callback when an option is clicked, receives the option key
|
||||
*/
|
||||
data class OptionsBottomSheetContent(
|
||||
val options: ImmutableList<BottomSheetOption> = persistentListOf(),
|
||||
val onOptionClick: (String) -> Unit = {},
|
||||
) : TangemBottomSheetConfigContent
|
||||
|
|
@ -3,10 +3,11 @@ package com.tangem.core.ui.components.buttons.small
|
|||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -41,20 +42,19 @@ fun TangemIconButton(
|
|||
background: Color = TangemTheme.colors.button.secondary,
|
||||
iconTint: Color = TangemTheme.colors.icon.secondary,
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
Icon(
|
||||
painter = rememberVectorPainter(ImageVector.vectorResource(iconRes)),
|
||||
contentDescription = "",
|
||||
tint = iconTint,
|
||||
modifier = modifier
|
||||
.size(24.dp)
|
||||
.clip(shape)
|
||||
.background(background)
|
||||
.size(24.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(ImageVector.vectorResource(iconRes)),
|
||||
contentDescription = "",
|
||||
tint = iconTint,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
}
|
||||
.padding(4.dp)
|
||||
.clickable(
|
||||
onClick = onClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// region Preview
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.ui.Alignment.Companion.TopCenter
|
|||
import androidx.compose.ui.Alignment.Companion.TopStart
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
|
|
@ -23,6 +24,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|||
import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.StakingSendScreenTestTags
|
||||
import com.tangem.core.ui.utils.*
|
||||
import java.math.BigDecimal
|
||||
import java.text.DecimalFormat
|
||||
|
|
@ -102,7 +104,9 @@ fun AmountTextField(
|
|||
singleLine = true,
|
||||
readOnly = !isEnabled,
|
||||
visualTransformation = visualTransformation,
|
||||
modifier = Modifier.background(backgroundColor),
|
||||
modifier = Modifier
|
||||
.background(backgroundColor)
|
||||
.testTag(StakingSendScreenTestTags.INPUT_TEXT_FIELD),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.core.ui.components.fields
|
|||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -36,9 +37,9 @@ fun PinTextField(
|
|||
value: String,
|
||||
length: Int,
|
||||
isPasswordVisual: Boolean,
|
||||
pinTextColor: PinTextColor,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
wrongCode: Boolean = false,
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val textFieldValue = remember(value) {
|
||||
|
|
@ -58,7 +59,7 @@ fun PinTextField(
|
|||
}
|
||||
.focusRequester(focusRequester),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Number,
|
||||
keyboardType = KeyboardType.NumberPassword,
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
singleLine = true,
|
||||
|
|
@ -72,7 +73,7 @@ fun PinTextField(
|
|||
CellDecoration(
|
||||
length = length,
|
||||
isPasswordVisual = isPasswordVisual,
|
||||
wrongCode = wrongCode,
|
||||
pinTextColor = pinTextColor,
|
||||
value = value,
|
||||
)
|
||||
},
|
||||
|
|
@ -84,17 +85,25 @@ fun PinTextField(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
enum class PinTextColor {
|
||||
Primary,
|
||||
WrongCode,
|
||||
Success,
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber", "LongMethod")
|
||||
@Composable
|
||||
private fun CellDecoration(
|
||||
length: Int,
|
||||
wrongCode: Boolean,
|
||||
pinTextColor: PinTextColor,
|
||||
value: String,
|
||||
modifier: Modifier = Modifier,
|
||||
isPasswordVisual: Boolean = false,
|
||||
) {
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
val width = textMeasurer.measure("0")
|
||||
val minSize = textMeasurer.measure("0")
|
||||
val minWidth = maxOf(minSize.size.width.dp + 8.dp, 24.dp + 3.dp) // 24.dp is the minimum width of a pin cell
|
||||
val minHeight = maxOf(minSize.size.height.dp, 48.dp) // 48.dp is the minimum height of a pin cell
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
|
|
@ -107,6 +116,18 @@ private fun CellDecoration(
|
|||
""
|
||||
}
|
||||
|
||||
val color = when (pinTextColor) {
|
||||
PinTextColor.Primary -> {
|
||||
if (isPasswordVisual) {
|
||||
TangemTheme.colors.icon.informative
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
}
|
||||
}
|
||||
PinTextColor.WrongCode -> TangemTheme.colors.icon.warning
|
||||
PinTextColor.Success -> TangemTheme.colors.icon.accent
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
|
|
@ -119,26 +140,34 @@ private fun CellDecoration(
|
|||
targetState = char,
|
||||
transitionSpec = {
|
||||
(
|
||||
fadeIn(animationSpec = tween(220, delayMillis = 90)) +
|
||||
slideInVertically(animationSpec = tween(330, delayMillis = 0))
|
||||
fadeIn(animationSpec = tween(90, delayMillis = 90)) +
|
||||
slideInVertically(animationSpec = tween(220, delayMillis = 0))
|
||||
)
|
||||
.togetherWith(
|
||||
fadeOut(animationSpec = tween(90)) + slideOutVertically(tween(220)),
|
||||
)
|
||||
},
|
||||
) { text ->
|
||||
Text(
|
||||
modifier = Modifier.sizeIn(minWidth = width.size.width.dp + 8.dp, minHeight = 48.dp),
|
||||
text = text,
|
||||
style = TangemTheme.typography.h3,
|
||||
color = if (wrongCode) {
|
||||
TangemTheme.colors.text.warning
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 48.sp,
|
||||
)
|
||||
if (isPasswordVisual && text.isNotEmpty()) {
|
||||
Canvas(
|
||||
Modifier.sizeIn(minWidth = minWidth, minHeight = minHeight),
|
||||
) {
|
||||
drawCircle(
|
||||
color = color,
|
||||
radius = 4.dp.toPx(),
|
||||
center = center,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
modifier = Modifier.sizeIn(minWidth = minWidth, minHeight = minHeight),
|
||||
text = text,
|
||||
style = TangemTheme.typography.h3,
|
||||
color = color,
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 48.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -152,10 +181,18 @@ private fun Preview() {
|
|||
var text by remember { mutableStateOf("123") }
|
||||
|
||||
Column {
|
||||
PinTextField(
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
isPasswordVisual = true,
|
||||
pinTextColor = PinTextColor.Success,
|
||||
length = 6,
|
||||
)
|
||||
PinTextField(
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
isPasswordVisual = false,
|
||||
pinTextColor = PinTextColor.Primary,
|
||||
length = 6,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,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.SwapTokenScreenTestTags
|
||||
import com.tangem.core.ui.test.BaseBlockTestTags
|
||||
|
||||
/**
|
||||
* [InputRowDefault](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-807&mode=design&t=86eKp9izWxUvmoCq-4)
|
||||
|
|
@ -67,20 +67,23 @@ fun InputRowDefault(
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.testTag(SwapTokenScreenTestTags.NETWORK_FEE_BLOCK),
|
||||
.testTag(BaseBlockTestTags.BLOCK),
|
||||
) {
|
||||
title?.let {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = titleColor,
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing8),
|
||||
modifier = Modifier
|
||||
.padding(bottom = TangemTheme.dimens.spacing8)
|
||||
.testTag(BaseBlockTestTags.BLOCK_TITLE),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = textColor,
|
||||
modifier = Modifier.testTag(BaseBlockTestTags.BLOCK_TEXT),
|
||||
)
|
||||
}
|
||||
iconRes?.let {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ fun InputRowRecipient(
|
|||
val (titleText, color) = if (isError && error != null) {
|
||||
error to TangemTheme.colors.text.warning
|
||||
} else {
|
||||
title to TangemTheme.colors.text.secondary
|
||||
title to TangemTheme.colors.text.tertiary
|
||||
}
|
||||
DividerContainer(
|
||||
modifier = modifier,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import com.tangem.core.ui.components.RectangleShimmer
|
|||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
|
||||
/**
|
||||
* Market price block
|
||||
|
|
@ -120,7 +120,7 @@ private fun PriceBlock(state: MarketPriceBlockState, priceWidthDp: Dp) {
|
|||
)
|
||||
}
|
||||
} else {
|
||||
Price(price = BigDecimalFormatter.EMPTY_BALANCE_SIGN, modifier = priceModifier)
|
||||
Price(price = DASH_SIGN, modifier = priceModifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ private const val DISABLED_ICON_ALPHA = 0.4f
|
|||
fun ProviderChooseCrypto(providerChooseUM: ProviderChooseUM, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
ConstraintLayout(
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.selectedBorder(isSelected = providerChooseUM.isSelected)
|
||||
.clickable(
|
||||
enabled = !providerChooseUM.hasError(),
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import androidx.compose.runtime.remember
|
|||
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.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -23,6 +24,7 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.StakingDetailsScreenTestTags
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
|
|
@ -54,7 +56,8 @@ fun RoundableCornersRow(
|
|||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
)
|
||||
.testTag(StakingDetailsScreenTestTags.PARAMETER_BLOCK),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
|
|
@ -63,6 +66,7 @@ fun RoundableCornersRow(
|
|||
color = startTextColor,
|
||||
maxLines = 1,
|
||||
style = startTextStyle,
|
||||
modifier = Modifier.testTag(StakingDetailsScreenTestTags.PARAMETER_NAME),
|
||||
)
|
||||
if (iconResId != null && iconClick != null) {
|
||||
Icon(
|
||||
|
|
@ -85,6 +89,7 @@ fun RoundableCornersRow(
|
|||
color = endTextColor,
|
||||
maxLines = 1,
|
||||
style = endTextStyle,
|
||||
modifier = Modifier.testTag(StakingDetailsScreenTestTags.PARAMETER_VALUE),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.core.ui.components.rows
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -23,7 +22,7 @@ import com.tangem.core.ui.components.atoms.text.EllipsisText
|
|||
import com.tangem.core.ui.components.atoms.text.TextEllipsis
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
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.SelectNetworkFeeBottomSheetTestTags
|
||||
|
|
@ -31,7 +30,7 @@ import com.tangem.utils.StringsSigns
|
|||
|
||||
@Composable
|
||||
fun SelectorRowItem(
|
||||
@StringRes titleRes: Int,
|
||||
title: TextReference,
|
||||
@DrawableRes iconRes: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
paddingValues: PaddingValues = PaddingValues(TangemTheme.dimens.spacing12),
|
||||
|
|
@ -80,7 +79,7 @@ fun SelectorRowItem(
|
|||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(titleRes),
|
||||
text = title.resolveReference(),
|
||||
style = textStyle,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing8),
|
||||
|
|
@ -151,7 +150,7 @@ private fun RowScope.SelectorValueContent(
|
|||
private fun SelectorRowItemPreview() {
|
||||
TangemThemePreview {
|
||||
SelectorRowItem(
|
||||
titleRes = R.string.common_fee_selector_option_slow,
|
||||
title = resourceReference(R.string.common_fee_selector_option_slow),
|
||||
iconRes = R.drawable.ic_tortoise_24,
|
||||
preDot = TextReference.Str("1000 ETH"),
|
||||
postDot = TextReference.Str("1000 $"),
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavController
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* The ViewModel is scoped to the parent route Navigation graph
|
||||
* and is provided using the Hilt-generated ViewModel factory
|
||||
*
|
||||
* ```
|
||||
* val navController = rememberNavController()
|
||||
*
|
||||
* navigation(
|
||||
* route = "parent",
|
||||
* startDestination = "parent/1"
|
||||
* ) {
|
||||
* composable("route/1") { entry ->
|
||||
* val viewModel = entry.parentHiltViewModel(navController)
|
||||
* }
|
||||
* composable("route/2") { entry ->
|
||||
* val viewModel = entry.parentHiltViewModel(navController)
|
||||
* }
|
||||
* composable("route/3") { entry ->
|
||||
* val viewModel = entry.parentHiltViewModel(navController)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param navController NavController within the common NavGraph
|
||||
* @throws Exception if there is no parent route
|
||||
*/
|
||||
@Composable
|
||||
inline fun <reified T : ViewModel> NavBackStackEntry.parentHiltViewModel(navController: NavController): T {
|
||||
val viewModelStoreOwner = remember(this) {
|
||||
try {
|
||||
navController.getBackStackEntry(this.destination.parent!!.id)
|
||||
} catch (e: Exception) {
|
||||
Timber.tag("scopedViewModel").e(e, "There is no parent route'")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
return hiltViewModel<T>(viewModelStoreOwner)
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import android.R
|
||||
import android.content.Context
|
||||
import android.graphics.Color.*
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@Deprecated("Use only in legacy fragments")
|
||||
fun Fragment.setStatusBarColor(@ColorRes colorResId: Int) {
|
||||
with(requireActivity().window) {
|
||||
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
statusBarColor = ContextCompat.getColor(requireContext(), colorResId)
|
||||
val view = view ?: return
|
||||
val windowInsetsController = WindowCompat.getInsetsController(this, view)
|
||||
windowInsetsController.isAppearanceLightStatusBars = luminance(requireContext(), colorResId)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO replace by android.graphics.luminance() after bump min API to 24
|
||||
@Suppress("MagicNumber")
|
||||
fun luminance(context: Context, @ColorRes colorRes: Int): Boolean {
|
||||
val color = context.resources.getColor(colorRes, null)
|
||||
if (R.color.transparent == color) return true
|
||||
var rtnValue = false
|
||||
val rgb = intArrayOf(red(color), green(color), blue(color))
|
||||
val brightness = sqrt(
|
||||
rgb[0] * rgb[0] * .241 +
|
||||
rgb[1] * rgb[1] * .691 +
|
||||
rgb[2] * rgb[2] * .068,
|
||||
).toInt()
|
||||
|
||||
// color is light
|
||||
if (brightness >= 200) {
|
||||
rtnValue = true
|
||||
}
|
||||
return rtnValue
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import androidx.compose.foundation.LocalIndication
|
|||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
|
|
@ -72,26 +71,24 @@ fun Modifier.conditionalCompose(
|
|||
fun Modifier.selectedBorder(
|
||||
isSelected: Boolean,
|
||||
width: Dp = 2.5.dp,
|
||||
color: Color = TangemTheme.colors.text.accent.copy(alpha = 0.1f),
|
||||
color: Color = TangemTheme.colors.text.accent,
|
||||
radius: Dp = 16.dp,
|
||||
) = conditionalCompose(
|
||||
condition = isSelected,
|
||||
modifier = {
|
||||
border(
|
||||
outsetBorder(
|
||||
width = width,
|
||||
color = color,
|
||||
shape = RoundedCornerShape(radius),
|
||||
color = color.copy(alpha = 0.15f),
|
||||
shape = RoundedCornerShape(radius + 2.dp),
|
||||
)
|
||||
.padding(width)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors.text.accent,
|
||||
shape = RoundedCornerShape(radius - 2.dp),
|
||||
color = color,
|
||||
shape = RoundedCornerShape(radius),
|
||||
)
|
||||
.clip(RoundedCornerShape(radius - 2.dp))
|
||||
.clip(RoundedCornerShape(radius))
|
||||
},
|
||||
otherModifier = {
|
||||
padding(width)
|
||||
.clip(RoundedCornerShape(radius - 2.dp))
|
||||
clip(RoundedCornerShape(radius))
|
||||
},
|
||||
)
|
||||
|
|
@ -121,7 +121,10 @@ fun BigDecimalFiatFormat.price(): BigDecimalFormat = BigDecimalFormat { value ->
|
|||
|
||||
private fun BigDecimal.isLessThanThreshold() = this > BigDecimal.ZERO && this < FIAT_FORMAT_THRESHOLD
|
||||
|
||||
private fun getFiatPriceAmountWithScale(value: BigDecimal): Pair<BigDecimal, Int> {
|
||||
/**
|
||||
* Returns amount with correct scale
|
||||
*/
|
||||
fun getFiatPriceAmountWithScale(value: BigDecimal): Pair<BigDecimal, Int> {
|
||||
return if (value < BigDecimal.ONE) {
|
||||
val leadingZeroes = value.scale() - value.precision()
|
||||
val scale = leadingZeroes + FRACTIONAL_PART_LENGTH_AFTER_LEADING_ZEROES
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
package com.tangem.core.ui.screen
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* An abstract base class for bottom sheet dialogs that use Compose for UI rendering.
|
||||
* Extends [BottomSheetDialogFragment] and implements [ComposeScreen] interface.
|
||||
*/
|
||||
abstract class ComposeBottomSheetFragment : BottomSheetDialogFragment(), ComposeScreen {
|
||||
|
||||
/**
|
||||
* The initial state of the bottom sheet. Default is [BottomSheetBehavior.STATE_EXPANDED].
|
||||
*/
|
||||
open val initialBottomSheetState = BottomSheetBehavior.STATE_EXPANDED
|
||||
|
||||
/**
|
||||
* The fraction of the screen height that the bottom sheet should take when expanded.
|
||||
* Default is `null`, indicating that the height will be determined by the content.
|
||||
*/
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
open val expandedHeightFraction: Float? = null
|
||||
|
||||
override val screenModifier: Modifier
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = Modifier
|
||||
.fillMaxWidth()
|
||||
.let {
|
||||
if (expandedHeightFraction != null) it.fillMaxHeight(expandedHeightFraction!!) else it
|
||||
}
|
||||
.background(
|
||||
color = TangemTheme.colors.background.primary,
|
||||
shape = TangemTheme.shapes.bottomSheet,
|
||||
)
|
||||
|
||||
override fun getTheme(): Int = R.style.AppTheme_TransparentBottomSheetDialog
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return createComposeView(
|
||||
context = inflater.context,
|
||||
activity = requireActivity(),
|
||||
overrideSystemBarColors = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
|
||||
(dialog as BottomSheetDialog).behavior.apply {
|
||||
state = initialBottomSheetState
|
||||
skipCollapsed = true
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package com.tangem.core.ui.screen
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.core.ui.R
|
||||
|
||||
/**
|
||||
* An abstract base class for fragments that use Compose for UI rendering.
|
||||
* Extends [Fragment] and implements [ComposeScreen] interface.
|
||||
*/
|
||||
abstract class ComposeFragment : Fragment(), ComposeScreen {
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val isTransitionsInflated = TransitionInflater.from(requireContext()).inflateTransitions()
|
||||
|
||||
return createComposeView(inflater.context, requireActivity()).also {
|
||||
it.isTransitionGroup = isTransitionsInflated
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
||||
/*
|
||||
* We need to manually dispatch configuration changes to the Compose view.
|
||||
*
|
||||
|
||||
* `android:configChanges="uiMode"` is set in the manifest.
|
||||
* */
|
||||
view?.dispatchConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates transitions for the fragment. Override this method to customize
|
||||
* enter and exit transitions for the fragment.
|
||||
*
|
||||
* @return `true` if transitions were inflated; `false` otherwise.
|
||||
*/
|
||||
protected open fun TransitionInflater.inflateTransitions(): Boolean {
|
||||
enterTransition = inflateTransition(R.transition.fade)
|
||||
exitTransition = inflateTransition(R.transition.fade)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object BaseBlockTestTags {
|
||||
const val BLOCK = "BASE_BLOCK"
|
||||
const val BLOCK_TITLE = "BASE_BLOCK_TITLE"
|
||||
const val BLOCK_TEXT = "BASE_BLOCK_REWARDS_TEXT"
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object StakingDetailsScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "TOKEN_DETAILS_SCREEN_CONTAINER"
|
||||
|
||||
const val BANNER_IMAGE = "TOKEN_DETAILS_SCREEN_BANNER_IMAGE"
|
||||
const val BANNER_TEXT = "TOKEN_DETAILS_SCREEN_BANNER_TEXT"
|
||||
|
||||
const val PARAMETER_BLOCK = "STAKING_DETAILS_PARAMETER_BLOCK"
|
||||
const val PARAMETER_NAME = "STAKING_DETAILS_PARAMETER_NAME"
|
||||
const val PARAMETER_VALUE = "STAKING_DETAILS_PARAMETER_VALUE"
|
||||
const val TOS_TEXT = "STAKING_DETAILS_TOS_TEXT"
|
||||
|
||||
const val ACTIVE_STAKING_BLOCK = "STAKING_DETAILS_ACTIVE_STAKING_BLOCK"
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object StakingSendDetailsScreenTestTags {
|
||||
|
||||
const val PRIMARY_AMOUNT = "STAKING_SEND_DETAILS_SCREEN_PRIMARY_AMOUNT"
|
||||
const val SECONDARY_AMOUNT = "TAKING_SEND_DETAILS_SCREEN_SECONDARY_AMOUNT"
|
||||
|
||||
const val VALIDATOR_BLOCK = "TAKING_SEND_DETAILS_SCREEN_VALIDATOR_BLOCK"
|
||||
const val NETWORK_FEE_BLOCK = "TAKING_SEND_DETAILS_SCREEN_NETWORK_FEE_BLOCK"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.core.ui.test
|
||||
|
||||
object StakingSendScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "STAKING_SEND_SCREEN_CONTAINER"
|
||||
|
||||
const val AMOUNT_CONTAINER_TITLE = "STAKING_SEND_SCREEN_AMOUNT_CONTAINER_TITLE"
|
||||
const val AMOUNT_CONTAINER_TEXT = "STAKING_SEND_SCREEN_AMOUNT_CONTAINER_TEXT"
|
||||
const val INPUT_TEXT_FIELD = "STAKING_SEND_SCREEN_INPUT_TEXT_FIELD"
|
||||
const val SECONDARY_AMOUNT = "STAKING_SEND_SCREEN_SECONDARY_AMOUNT"
|
||||
|
||||
const val CURRENCY_BUTTON = "STAKING_SEND_SCREEN_CURRENCY_BUTTON"
|
||||
const val FIAT_ICON = "STAKING_SEND_SCREEN_FIAT_ICON"
|
||||
const val CURRENCY_ICON = "STAKING_SEND_SCREEN_CURRENCY_ICON"
|
||||
const val MAX_BUTTON = "STAKING_SEND_SCREEN_MAX_BUTTON"
|
||||
const val PREVIOUS_BUTTON = "STAKING_SEND_SCREEN_PREVIOUS_BUTTON"
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ object SwapTokenScreenTestTags {
|
|||
const val SWAP_TEXT_FIELD = "SWAP_TOKEN_SCREEN_SWAP_TEXT_FIELD"
|
||||
const val RECEIVE_TEXT_FIELD = "SWAP_TOKEN_SCREEN_RECEIVE_TEXT_FIELD"
|
||||
const val RECEIVE_AMOUNT_SHIMMER = "SWAP_TOKEN_SCREEN_RECEIVE_AMOUNT_SHIMMER"
|
||||
const val NETWORK_FEE_BLOCK = "SWAP_TOKEN_SCREEN_NETWORK_FEE_BLOCK"
|
||||
const val PROVIDERS_BLOCK = "SWAP_TOKEN_SCREEN_PROVIDERS_BLOCK"
|
||||
const val SWAP_BUTTON = "SWAP_TOKEN_SCREEN_SWAP_BUTTON"
|
||||
const val TOKEN = "SWAP_TOKEN_SCREEN_TOKEN"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,19 @@ package com.tangem.core.ui.test
|
|||
|
||||
object TokenDetailsScreenTestTags {
|
||||
const val SCREEN_CONTAINER = "TOKEN_DETAILS_SCREEN_CONTAINER"
|
||||
|
||||
const val TOKEN_TITLE = "TOKEN_DETAILS_SCREEN_TOKEN_TITLE"
|
||||
const val ACTION_BUTTON = "TOKEN_DETAILS_SCREEN_ACTION_BUTTON"
|
||||
const val HORIZONTAL_ACTION_CHIPS = "TOKEN_DETAILS_SCREEN_HORIZONTAL_ACTION_CHIPS"
|
||||
|
||||
const val STAKING_BLOCK = "TOKEN_DETAILS_SCREEN_STAKING_BLOCK"
|
||||
const val STAKING_AVAILABLE_BLOCK = "TOKEN_DETAILS_SCREEN_STAKING_AVAILABLE_BLOCK"
|
||||
const val STAKING_CURRENCY_ICON = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_CURRENCY_ICON"
|
||||
const val STAKING_SERVICE_TITLE = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_SERVICE_TITLE"
|
||||
const val STAKING_SERVICE_TEXT = "TOKEN_DETAILS_SCREEN_STAKING_STAKING_SERVICE_TEXT"
|
||||
const val STAKING_FIAT_AMOUNT = "TOKEN_DETAILS_SCREEN_STAKING_FIAT_AMOUNT"
|
||||
const val STAKING_DOT = "TOKEN_DETAILS_SCREEN_STAKING_DOT"
|
||||
const val STAKING_TOKEN_AMOUNT = "TOKEN_DETAILS_SCREEN_STAKING_TOKEN_AMOUNT"
|
||||
const val STAKING_REWARD_VALUE = "TOKEN_DETAILS_SCREEN_STAKING_REWARD_VALUE"
|
||||
const val STAKING_CHEVRON_ICON = "TOKEN_DETAILS_SCREEN_STAKING_CHEVRON_ICON"
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.utils.StringsSigns.LOWER_SIGN
|
||||
import com.tangem.utils.StringsSigns.TILDE_SIGN
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.NumberFormat
|
||||
import java.util.Currency
|
||||
import java.util.Locale
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
object BigDecimalFormatter {
|
||||
|
||||
const val EMPTY_BALANCE_SIGN = DASH_SIGN
|
||||
private const val CAN_BE_LOWER_SIGN = LOWER_SIGN
|
||||
|
||||
private val FIAT_FORMAT_THRESHOLD = BigDecimal("0.01")
|
||||
|
||||
private const val FIAT_MARKET_DEFAULT_DIGITS = 2
|
||||
private const val FIAT_MARKET_EXTENDED_DIGITS = 6
|
||||
private const val FRACTIONAL_PART_LENGTH_AFTER_LEADING_ZEROES = 4
|
||||
|
||||
private val usdCurrency = Currency.getInstance("USD")
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun formatFiatAmount(
|
||||
fiatAmount: BigDecimal?,
|
||||
fiatCurrencyCode: String,
|
||||
fiatCurrencySymbol: String,
|
||||
decimals: Int = FIAT_MARKET_DEFAULT_DIGITS,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
withApproximateSign: Boolean = false,
|
||||
): String {
|
||||
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
|
||||
|
||||
val formatterCurrency = getCurrency(fiatCurrencyCode)
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = decimals
|
||||
minimumFractionDigits = decimals
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return if (fiatAmount.checkFiatThreshold()) {
|
||||
buildString {
|
||||
append(CAN_BE_LOWER_SIGN)
|
||||
append(
|
||||
formatter.format(FIAT_FORMAT_THRESHOLD)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val formattedAmount = formatter.format(fiatAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
|
||||
if (withApproximateSign) {
|
||||
buildString {
|
||||
append(TILDE_SIGN)
|
||||
append(formattedAmount)
|
||||
}
|
||||
} else {
|
||||
formattedAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun formatFiatAmountUncapped(
|
||||
fiatAmount: BigDecimal?,
|
||||
fiatCurrencyCode: String,
|
||||
fiatCurrencySymbol: String,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String {
|
||||
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
|
||||
val formatterCurrency = getCurrency(fiatCurrencyCode)
|
||||
|
||||
val digits = if (fiatAmount.checkFiatThreshold()) {
|
||||
FIAT_MARKET_EXTENDED_DIGITS
|
||||
} else {
|
||||
FIAT_MARKET_DEFAULT_DIGITS
|
||||
}
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = digits
|
||||
minimumFractionDigits = FIAT_MARKET_DEFAULT_DIGITS
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return formatter.format(fiatAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
}
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun formatFiatPriceUncapped(
|
||||
fiatAmount: BigDecimal?,
|
||||
fiatCurrencyCode: String,
|
||||
fiatCurrencySymbol: String,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String {
|
||||
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
|
||||
val formatterCurrency = getCurrency(fiatCurrencyCode)
|
||||
|
||||
val (formattedAmount, finalScale) = getFiatPriceUncappedWithScale(value = fiatAmount)
|
||||
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = finalScale
|
||||
minimumFractionDigits = FIAT_MARKET_DEFAULT_DIGITS
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return formatter.format(formattedAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
}
|
||||
|
||||
@Deprecated("Use BigDecimal.format")
|
||||
fun getFiatPriceUncappedWithScale(value: BigDecimal): Pair<BigDecimal, Int> {
|
||||
return if (value < BigDecimal.ONE) {
|
||||
val leadingZeroes = value.scale() - value.precision()
|
||||
val scale = leadingZeroes + FRACTIONAL_PART_LENGTH_AFTER_LEADING_ZEROES
|
||||
|
||||
val amount = value
|
||||
.setScale(scale, RoundingMode.HALF_UP)
|
||||
.stripTrailingZeros()
|
||||
|
||||
amount to amount.scale()
|
||||
} else {
|
||||
value to FIAT_MARKET_DEFAULT_DIGITS
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrency(code: String): Currency {
|
||||
return runCatching { Currency.getInstance(code) }
|
||||
.getOrElse { e ->
|
||||
// Currency code is not valid ISO 4217 code
|
||||
if (e is IllegalArgumentException) {
|
||||
usdCurrency
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BigDecimal.checkFiatThreshold() = this > BigDecimal.ZERO && this < FIAT_FORMAT_THRESHOLD
|
||||
}
|
||||
19
core/ui/src/main/res/drawable/ic_mobile_wallet_icon_24.xml
Normal file
19
core/ui/src/main/res/drawable/ic_mobile_wallet_icon_24.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:pathData="M6.839,11.095C7.657,11.095 8.32,11.759 8.32,12.576V20.148C8.32,20.966 7.657,21.629 6.839,21.629H2.864C2.046,21.629 1.383,20.966 1.383,20.148V12.576C1.383,11.759 2.046,11.095 2.864,11.095H6.839ZM14.064,2.37C14.831,2.37 15.401,2.37 15.894,2.502C17.227,2.858 18.268,3.897 18.625,5.226C18.709,5.54 18.74,5.885 18.751,6.291C18.895,6.304 19.034,6.319 19.167,6.337C20.062,6.457 20.853,6.719 21.486,7.351C22.119,7.983 22.382,8.772 22.503,9.665C22.618,10.516 22.617,11.591 22.617,12.895V14.957C22.617,16.261 22.618,17.336 22.503,18.187C22.382,19.08 22.12,19.869 21.486,20.501C20.853,21.133 20.062,21.395 19.167,21.515C18.314,21.629 17.236,21.629 15.929,21.629H9.999C9.987,21.629 9.975,21.628 9.963,21.628C10.176,21.18 10.297,20.678 10.297,20.149V12.577C10.297,10.668 8.749,9.12 6.84,9.12H2.865C2.335,9.12 1.832,9.24 1.383,9.453V5.259C1.383,3.664 2.679,2.37 4.278,2.37H14.064ZM18.406,12.436C17.468,12.436 16.707,13.196 16.707,14.134C16.707,15.073 17.468,15.833 18.406,15.833C19.345,15.833 20.105,15.073 20.105,14.134C20.105,13.196 19.344,12.436 18.406,12.436ZM4.159,12.573C3.723,12.573 3.369,12.926 3.369,13.363C3.369,13.799 3.723,14.153 4.159,14.153H5.542C5.978,14.153 6.332,13.799 6.332,13.363C6.332,12.926 5.978,12.573 5.542,12.573H4.159ZM4.278,4.296C3.745,4.296 3.313,4.727 3.313,5.259C3.313,5.791 3.745,6.222 4.278,6.222H15.929C16.237,6.222 16.533,6.222 16.816,6.224C16.808,5.975 16.791,5.839 16.76,5.724C16.581,5.059 16.061,4.54 15.394,4.362C15.18,4.304 14.889,4.296 13.93,4.296H4.278Z">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:centerX="7.343" android:centerY="0.784" android:gradientRadius="33.786" android:type="radial">
|
||||
|
||||
<item android:color="#FF96999B" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF4D4E53" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
</vector>
|
||||
13
core/ui/src/main/res/drawable/ic_passcode_lock_32.xml
Normal file
13
core/ui/src/main/res/drawable/ic_passcode_lock_32.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="33dp"
|
||||
android:viewportWidth="32"
|
||||
android:viewportHeight="33">
|
||||
<path
|
||||
android:pathData="M16,4.062C17.587,4.062 19.084,4.339 20.489,4.893C21.894,5.447 23.151,6.22 24.259,7.212C25.366,8.204 26.272,9.361 26.974,10.684C27.677,11.998 28.119,13.425 28.301,14.962H30.26C30.5,14.962 30.682,15.012 30.806,15.111C30.93,15.21 30.988,15.342 30.98,15.508C30.971,15.665 30.901,15.843 30.769,16.041L28.066,19.885C27.892,20.133 27.694,20.257 27.47,20.257C27.255,20.249 27.065,20.125 26.9,19.885L24.197,16.029C24.064,15.839 23.994,15.665 23.986,15.508C23.986,15.342 24.048,15.21 24.172,15.111C24.296,15.012 24.474,14.962 24.705,14.962H26.677C26.495,13.648 26.094,12.428 25.474,11.304C24.862,10.172 24.077,9.184 23.118,8.34C22.159,7.497 21.072,6.84 19.857,6.369C18.65,5.898 17.364,5.662 16,5.662C14.405,5.662 12.904,5.988 11.499,6.642C10.101,7.286 8.903,8.171 7.903,9.295C7.712,9.502 7.518,9.614 7.32,9.63C7.121,9.638 6.944,9.585 6.786,9.469C6.613,9.345 6.505,9.167 6.464,8.936C6.423,8.696 6.497,8.469 6.687,8.254C7.836,6.956 9.217,5.935 10.829,5.191C12.441,4.438 14.165,4.062 16,4.062ZM16,28.938C14.413,28.938 12.916,28.656 11.511,28.094C10.106,27.54 8.849,26.767 7.741,25.775C6.634,24.792 5.724,23.642 5.013,22.328C4.311,21.005 3.868,19.575 3.686,18.038H1.727C1.487,18.038 1.31,17.988 1.194,17.889C1.07,17.789 1.012,17.657 1.02,17.492C1.02,17.327 1.086,17.149 1.219,16.959L3.922,13.102C4.096,12.862 4.29,12.743 4.505,12.743C4.728,12.743 4.926,12.862 5.1,13.102L7.803,16.971C7.936,17.161 8.002,17.339 8.002,17.504C8.002,17.661 7.94,17.789 7.816,17.889C7.7,17.988 7.522,18.038 7.283,18.038H5.323C5.497,19.344 5.894,20.563 6.514,21.696C7.134,22.82 7.919,23.808 8.87,24.659C9.829,25.503 10.912,26.16 12.119,26.631C13.334,27.102 14.628,27.338 16,27.338C17.587,27.338 19.079,27.011 20.476,26.358C21.882,25.713 23.089,24.825 24.097,23.692C24.288,23.494 24.478,23.39 24.668,23.382C24.866,23.366 25.044,23.411 25.201,23.518C25.375,23.651 25.482,23.837 25.524,24.076C25.565,24.308 25.49,24.531 25.3,24.746C24.16,26.036 22.779,27.057 21.159,27.809C19.546,28.561 17.827,28.938 16,28.938Z"
|
||||
android:fillColor="#0099FF"/>
|
||||
<path
|
||||
android:pathData="M16.153,10.844C17.725,10.924 18.975,12.223 18.975,13.815V15.295C19.876,15.317 20.6,16.055 20.6,16.961V20.492C20.6,21.413 19.853,22.159 18.933,22.159H13.069C12.148,22.159 11.402,21.413 11.402,20.492V16.961C11.402,16.055 12.125,15.318 13.026,15.295V13.815C13.026,12.172 14.357,10.84 16,10.84L16.153,10.844ZM16,12.23C15.125,12.23 14.415,12.939 14.415,13.815V15.294H17.586V13.815C17.586,12.939 16.876,12.23 16,12.23Z"
|
||||
android:fillColor="#0099FF"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
20
core/ui/src/main/res/drawable/ic_passcode_lock_56.xml
Normal file
20
core/ui/src/main/res/drawable/ic_passcode_lock_56.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="56dp"
|
||||
android:viewportWidth="56"
|
||||
android:viewportHeight="56">
|
||||
<path
|
||||
android:pathData="M49.875,26.906V20.475C49.875,17.902 49.875,16.616 49.377,15.633C48.939,14.769 48.24,14.066 47.381,13.626C46.403,13.125 45.124,13.125 42.566,13.125H13.434C10.876,13.125 9.597,13.125 8.619,13.626C7.76,14.066 7.061,14.769 6.623,15.633C6.125,16.616 6.125,17.902 6.125,20.475V28.744C6.125,31.316 6.125,32.603 6.623,33.585C7.061,34.45 7.76,35.153 8.619,35.593C9.597,36.094 10.876,36.094 13.434,36.094H25.716M28,24.609H28.011M38.546,24.609H38.557M17.454,24.609H17.466M28.571,24.609C28.571,24.927 28.315,25.184 28,25.184C27.685,25.184 27.429,24.927 27.429,24.609C27.429,24.292 27.685,24.035 28,24.035C28.315,24.035 28.571,24.292 28.571,24.609ZM39.117,24.609C39.117,24.927 38.861,25.184 38.546,25.184C38.23,25.184 37.975,24.927 37.975,24.609C37.975,24.292 38.23,24.035 38.546,24.035C38.861,24.035 39.117,24.292 39.117,24.609ZM18.025,24.609C18.025,24.927 17.77,25.184 17.454,25.184C17.139,25.184 16.883,24.927 16.883,24.609C16.883,24.292 17.139,24.035 17.454,24.035C17.77,24.035 18.025,24.292 18.025,24.609Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#008EFF"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M45.242,38.664V34.645C45.242,32.425 43.452,30.625 41.244,30.625C39.037,30.625 37.247,32.425 37.247,34.645V38.664M36.905,47.852H45.584C46.864,47.852 47.503,47.852 47.992,47.601C48.422,47.381 48.771,47.03 48.99,46.597C49.239,46.106 49.239,45.463 49.239,44.177V42.339C49.239,41.053 49.239,40.41 48.99,39.918C48.771,39.486 48.422,39.135 47.992,38.914C47.503,38.664 46.864,38.664 45.584,38.664H36.905C35.625,38.664 34.986,38.664 34.497,38.914C34.067,39.135 33.718,39.486 33.499,39.918C33.25,40.41 33.25,41.053 33.25,42.339V44.177C33.25,45.463 33.25,46.106 33.499,46.597C33.718,47.03 34.067,47.381 34.497,47.601C34.986,47.852 35.625,47.852 36.905,47.852Z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="3"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#008EFF"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
17
core/ui/src/main/res/drawable/img_approvale_new_24.xml
Normal file
17
core/ui/src/main/res/drawable/img_approvale_new_24.xml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<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="#0099FF"
|
||||
android:pathData="M16.539,8.682C16.769,8.338 16.677,7.872 16.332,7.642C15.988,7.412 15.522,7.505 15.292,7.849L10.88,14.452L8.677,11.614C8.423,11.287 7.952,11.228 7.625,11.482C7.297,11.736 7.238,12.207 7.492,12.534L10.333,16.194C10.481,16.384 10.712,16.492 10.953,16.484C11.193,16.475 11.416,16.351 11.55,16.151L16.539,8.682Z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#0099FF"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M12,1.746C6.337,1.746 1.746,6.337 1.746,12C1.746,17.663 6.337,22.254 12,22.254C17.663,22.254 22.254,17.663 22.254,12C22.254,6.337 17.663,1.746 12,1.746ZM3.246,12C3.246,7.165 7.165,3.246 12,3.246C16.835,3.246 20.754,7.165 20.754,12C20.754,16.835 16.835,20.754 12,20.754C7.165,20.754 3.246,16.835 3.246,12Z" />
|
||||
|
||||
</vector>
|
||||
Loading…
Add table
Add a link
Reference in a new issue