Updated on 2026-08-14
This commit is contained in:
commit
bf0cb06cb3
297 changed files with 4771 additions and 2376 deletions
|
|
@ -1,121 +1,20 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.*
|
||||
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
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.states.Item
|
||||
import com.tangem.core.ui.components.states.SelectableItemsState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.valentinilk.shimmer.shimmer
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
/**
|
||||
* Small card with text information attached to the edges
|
||||
*
|
||||
* @param startText text information attached to the left edge
|
||||
* @param endText text information attached to the right edge
|
||||
* @param isLoading if true, shimmer is shown instead of [startText] and [endText]
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=290%3A339&t=WdN5XpixzZLlQAZO-4"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun SmallInfoCard(startText: String, endText: String, isLoading: Boolean = false) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
CardInfoBox(
|
||||
startText = startText,
|
||||
endText = endText,
|
||||
isLoading = isLoading,
|
||||
disclaimer = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Small card with text information attached to the edges
|
||||
*
|
||||
* @param startText text information attached to the left edge
|
||||
* @param endText text information attached to the right edge
|
||||
* @param disclaimer closable description text
|
||||
* @param isLoading if true, shimmer is shown instead of [startText] and [endText]
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=1007%3A709&t=X19ZWlYP0rnmdcDO-4"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun SmallInfoCardWithDisclaimer(startText: String, endText: String, disclaimer: String, isLoading: Boolean = false) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
CardInfoBox(
|
||||
startText = startText,
|
||||
endText = endText,
|
||||
isLoading = isLoading,
|
||||
disclaimer = disclaimer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [SmallInfoCard] with warning information shown underneath it.
|
||||
*
|
||||
* @param startText text information attached to the left edge
|
||||
* @param endText text information attached to the right edge
|
||||
* @param warningText text of the warning
|
||||
* @param disclaimer closable description text
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=515%3A1541&t=qYGQJdtf1eeUPdkR-1"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun SmallInfoCardWithWarning(startText: String, endText: String, warningText: String, disclaimer: String? = null) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.size12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
CardInfoBox(startText = startText, endText = endText, disclaimer = disclaimer)
|
||||
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
|
||||
WarningItem(warningText = warningText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Small card with an icon to the left and title with description shown to the right of it.
|
||||
|
|
@ -140,7 +39,8 @@ fun CardWithIcon(
|
|||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
tonalElevation = TangemTheme.dimens.elevation2,
|
||||
shadowElevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
IconWithTitleAndDescription(
|
||||
title = title,
|
||||
|
|
@ -223,393 +123,12 @@ internal fun IconWithTitleAndDescription(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Card with text information attached to the edges and selectable items
|
||||
*
|
||||
* @param state state of block with items
|
||||
* @param isLoading if true, shimmer is shown instead of data
|
||||
* @param disclaimer description text
|
||||
* @param onSelect listener on item selection
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=290-217&t=FMnRqkWPAgdZSdhv-0"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun <T> SelectableInfoCard(
|
||||
state: SelectableItemsState<T>,
|
||||
onSelect: (Item<T>) -> Unit,
|
||||
isLoading: Boolean = false,
|
||||
disclaimer: String? = null,
|
||||
) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
SelectableInfoBlock(
|
||||
state = state,
|
||||
isLoading = isLoading,
|
||||
disclaimer = disclaimer,
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Card with text information attached to the edges, selectable items and warning
|
||||
*
|
||||
* @param state state of block with items
|
||||
* @param isLoading if true, shimmer is shown instead of data
|
||||
* @param disclaimer description text
|
||||
* @param onSelect listener on item selection
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=290-217&t=FMnRqkWPAgdZSdhv-0"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun <T> SelectableInfoCardWithWarning(
|
||||
state: SelectableItemsState<T>,
|
||||
warningText: String,
|
||||
onSelect: (Item<T>) -> Unit,
|
||||
isLoading: Boolean = false,
|
||||
disclaimer: String? = null,
|
||||
) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
) {
|
||||
SelectableInfoBlock(
|
||||
state = state,
|
||||
isLoading = isLoading,
|
||||
disclaimer = disclaimer,
|
||||
warningText = warningText,
|
||||
onSelect = onSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region elements
|
||||
|
||||
@Composable
|
||||
private fun CardInfoBox(startText: String, endText: String, disclaimer: String?, isLoading: Boolean = false) {
|
||||
val isOpened = remember { mutableStateOf(false) }
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(TangemTheme.dimens.size48)
|
||||
.clickable { isOpened.value = !isOpened.value }
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = startText,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
||||
if (isLoading) {
|
||||
ShimmerItem(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12)
|
||||
} else {
|
||||
Row {
|
||||
Text(
|
||||
text = endText,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
if (disclaimer != null) {
|
||||
val chevronIcon = if (isOpened.value) {
|
||||
painterResource(id = R.drawable.ic_chevron_up_24)
|
||||
} else {
|
||||
painterResource(id = R.drawable.ic_chevron_24)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
painter = chevronIcon,
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isOpened.value && disclaimer != null) {
|
||||
DisclaimerItem(disclaimer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun <T> SelectableInfoBlock(
|
||||
state: SelectableItemsState<T>,
|
||||
onSelect: (Item<T>) -> Unit,
|
||||
isLoading: Boolean = false,
|
||||
disclaimer: String? = null,
|
||||
warningText: String? = null,
|
||||
) {
|
||||
var isOpened by remember { mutableStateOf(false) }
|
||||
Column {
|
||||
if (isLoading) {
|
||||
ShimmerItem(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12)
|
||||
} else {
|
||||
val selectedItem = state.selectedItem
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(TangemTheme.dimens.size48)
|
||||
.clickable { isOpened = !isOpened }
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = selectedItem.startText.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
||||
Row {
|
||||
Text(
|
||||
text = selectedItem.endText.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
val chevronIcon = if (isOpened) {
|
||||
painterResource(id = R.drawable.ic_chevron_up_24)
|
||||
} else {
|
||||
painterResource(id = R.drawable.ic_chevron_24)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
painter = chevronIcon,
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
|
||||
if (isOpened) {
|
||||
state.items.forEach { item ->
|
||||
SelectableItem(item) {
|
||||
onSelect.invoke(item)
|
||||
}
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
if (disclaimer != null) {
|
||||
DisclaimerItem(disclaimer)
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
}
|
||||
if (warningText?.isNotEmpty() == true) {
|
||||
WarningItem(warningText = warningText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun <T> SelectableItem(item: Item<T>, onSelect: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(TangemTheme.dimens.size48)
|
||||
.clickable { onSelect() }
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = item.startText.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = item.endText.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
val size = TangemTheme.dimens.size24
|
||||
if (item.isSelected) {
|
||||
Icon(
|
||||
modifier = Modifier.size(size),
|
||||
painter = painterResource(id = R.drawable.ic_check_24),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
)
|
||||
} else {
|
||||
SpacerW(width = size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShimmerItem(width: Dp, height: Dp) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(width = width, height = height)
|
||||
.shimmer()
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius3),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisclaimerItem(disclaimer: String) {
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing14,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.height(TangemTheme.dimens.size40)
|
||||
.width(TangemTheme.dimens.size40),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_heart_24),
|
||||
tint = TangemTheme.colors.icon.secondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
SpacerW12()
|
||||
Text(
|
||||
text = disclaimer,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WarningItem(warningText: String) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing14,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.height(TangemTheme.dimens.size40)
|
||||
.width(TangemTheme.dimens.size40),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.img_attention_20),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
SpacerW12()
|
||||
Text(
|
||||
text = warningText,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion elements
|
||||
|
||||
// region Preview
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun CardsPreview() {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
SmallInfoCard(
|
||||
startText = "Balance",
|
||||
endText = "0.4405434 BTC",
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
|
||||
SmallInfoCardWithDisclaimer(
|
||||
startText = "Balance",
|
||||
endText = "0.4405434 BTC",
|
||||
disclaimer = "Not enough funds for fee on your Polygon wallet to create a transaction." +
|
||||
"Top up your Polygon wallet first.",
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
|
||||
SmallInfoCardWithWarning(
|
||||
startText = "Balance",
|
||||
endText = "0.4405434 BTC",
|
||||
warningText = "Not enough funds for fee on your Polygon wallet to create a transaction. " +
|
||||
"Top up your Polygon wallet first.",
|
||||
disclaimer = "Not enough funds for fee on your Polygon wallet to create a transaction." +
|
||||
"Top up your Polygon wallet first.",
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
|
||||
private fun Preview_CardWithIcon() {
|
||||
TangemThemePreview {
|
||||
CardWithIcon(
|
||||
title = "Permit is valid until",
|
||||
description = "26:30",
|
||||
|
|
@ -621,52 +140,6 @@ private fun CardsPreview() {
|
|||
)
|
||||
},
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
|
||||
val state = SelectableItemsState(
|
||||
selectedItem = Item(0, TextReference.Str("Balance"), TextReference.Str("0.4405434 BTC"), true, ""),
|
||||
items = listOf(
|
||||
Item(0, TextReference.Str("Normal"), TextReference.Str("0.4405434 BTC"), true, ""),
|
||||
Item(1, TextReference.Str("Priority"), TextReference.Str("0.46 BTC"), false, ""),
|
||||
).toImmutableList(),
|
||||
)
|
||||
SelectableInfoCard(
|
||||
state = state,
|
||||
isLoading = false,
|
||||
disclaimer = "Not enough funds for fee on your Polygon wallet to create a transaction",
|
||||
onSelect = {},
|
||||
)
|
||||
|
||||
SpacerH32()
|
||||
|
||||
SelectableInfoCardWithWarning(
|
||||
state = state,
|
||||
isLoading = false,
|
||||
warningText = "Not enough funds for fee on your Polygon wallet to create a transaction. " +
|
||||
"Top up your Polygon wallet first.",
|
||||
disclaimer = "Not enough funds for fee on your Polygon wallet to create a transaction",
|
||||
onSelect = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_Cards() {
|
||||
TangemThemePreview {
|
||||
CardsPreview()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true)
|
||||
@Preview(widthDp = 328, heightDp = 48, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_SimpleInfoCard() {
|
||||
TangemThemePreview {
|
||||
SmallInfoCard(startText = "Balance", endText = "0.4405434 BTC")
|
||||
SmallInfoCardWithDisclaimer(startText = "Balance", endText = "0.4405434 BTC", disclaimer = "test")
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
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
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
/**
|
||||
* A card with a warning icon to the left and title with description shown to the right of it.
|
||||
*
|
||||
* @param title title of the warning in bold
|
||||
* @param description description of the warning
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=290%3A217&t=yMepJZTRh5bLkOoJ-1"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun WarningCard(title: String, description: String, icon: @Composable (() -> Unit)? = null) {
|
||||
WarningCardSurface(
|
||||
content = {
|
||||
WarningBody(
|
||||
title = title,
|
||||
description = description,
|
||||
icon = icon,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*
|
||||
* @param title title of the warning in bold
|
||||
* @param description description of the warning
|
||||
* @param onClick action to be performed when warning is clicked
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=402%3A528&t=yMepJZTRh5bLkOoJ-1"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun ClickableWarningCard(
|
||||
title: String,
|
||||
description: String,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
WarningCardSurface(
|
||||
content = {
|
||||
WarningBody(title = title, description = description, icon = icon) {
|
||||
SpacerW12()
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_chevron_right_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* [WarningCard], but clickable and with a 'refresh' icon at the right edge
|
||||
*
|
||||
* @param title title of the warning in bold
|
||||
* @param description description of the warning
|
||||
* @param onClick action to be performed when warning is clicked
|
||||
*
|
||||
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=290%3A217&t=yMepJZTRh5bLkOoJ-1"
|
||||
* >Figma component</a>
|
||||
*/
|
||||
@Composable
|
||||
fun RefreshableWarningCard(
|
||||
title: String,
|
||||
description: String,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
WarningCardSurface(
|
||||
content = {
|
||||
WarningBody(title = title, description = description, icon = icon) {
|
||||
SpacerW12()
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_refresh_24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
// region elements
|
||||
|
||||
@Composable
|
||||
private fun WarningBody(
|
||||
title: String,
|
||||
description: String?,
|
||||
iconBackground: Color = TangemTheme.colors.background.secondary,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
additionalContent: @Composable () -> Unit = {},
|
||||
) {
|
||||
IconWithTitleAndDescription(
|
||||
title = title,
|
||||
description = description,
|
||||
additionalContent = additionalContent,
|
||||
icon = icon ?: {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.img_attention_20),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
iconBackground = iconBackground,
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun WarningCardSurface(onClick: (() -> Unit)? = null, content: @Composable () -> Unit) {
|
||||
Card(
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.size12),
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
onClick = onClick ?: {},
|
||||
enabled = onClick != null,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun WarningsPreview() {
|
||||
TangemThemePreview {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
WarningCard(
|
||||
title = "Exchange rate has expired",
|
||||
description = "To access all the networks, you need to scan the card.",
|
||||
)
|
||||
SpacerH32()
|
||||
ClickableWarningCard(
|
||||
title = "Exchange rate has expired",
|
||||
description = "To access all the networks, you need to scan the card.",
|
||||
onClick = {},
|
||||
)
|
||||
SpacerH32()
|
||||
RefreshableWarningCard(
|
||||
title = "Exchange rate has expired",
|
||||
description = "To access all the networks, you need to scan the card.",
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion Preview
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.core.ui.pullToRefresh
|
||||
package com.tangem.core.ui.components.containers.pullToRefresh
|
||||
|
||||
/**
|
||||
* Pull to refresh config data
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package com.tangem.core.ui.components.containers.pullToRefresh
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator
|
||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TangemPullToRefreshContainer(
|
||||
config: PullToRefreshConfig,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val state = rememberPullToRefreshState()
|
||||
PullToRefreshBox(
|
||||
isRefreshing = config.isRefreshing,
|
||||
onRefresh = {
|
||||
config.onRefresh(PullToRefreshConfig.ShowRefreshState())
|
||||
},
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
indicator = {
|
||||
Indicator(
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
isRefreshing = config.isRefreshing,
|
||||
state = state,
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
},
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 720)
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 720, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun TangemPullToRefreshContainer_Preview() {
|
||||
TangemThemePreview {
|
||||
TangemPullToRefreshContainer(
|
||||
config = PullToRefreshConfig(isRefreshing = true, {}),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
.background(TangemTheme.colors.background.secondary),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
package com.tangem.core.ui.components.notifications
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import androidx.constraintlayout.compose.Visibility
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButton
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.LocalIsInDarkTheme
|
||||
import com.tangem.core.ui.res.TangemColorPalette.Dark6
|
||||
import com.tangem.core.ui.res.TangemColorPalette.Light4
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Custom notification with image background
|
||||
* @see [Swap Promo](https://www.figma.com/file/Vs6SkVsFnUPsSCNwlnVf5U/Android-%E2%80%93-UI?type=design&node-id=8713-6307&mode=dev)
|
||||
*/
|
||||
@Suppress("LongMethod", "DestructuringDeclarationWithTooManyEntries")
|
||||
@Composable
|
||||
fun NotificationWithBackground(config: NotificationConfig, modifier: Modifier = Modifier) {
|
||||
val button = config.buttonsState as? NotificationConfig.ButtonsState.SecondaryButtonConfig
|
||||
|
||||
ConstraintLayout(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = TangemTheme.dimens.size62)
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.clickable(
|
||||
enabled = config.onClick != null,
|
||||
onClick = config.onClick ?: {},
|
||||
),
|
||||
) {
|
||||
val (iconRef, titleRef, subtitleRef, closeIconRef, buttonRef, backgroundRef) = createRefs()
|
||||
|
||||
val spacing2 = TangemTheme.dimens.spacing2
|
||||
val spacing12 = TangemTheme.dimens.spacing12
|
||||
val spacing14 = TangemTheme.dimens.spacing14
|
||||
|
||||
Image(
|
||||
painter = painterResource(config.backgroundResId ?: R.drawable.img_swap_promo_blue_banner_background),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier.constrainAs(backgroundRef) {
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(parent.start)
|
||||
end.linkTo(parent.end)
|
||||
bottom.linkTo(parent.bottom)
|
||||
width = Dimension.fillToConstraints
|
||||
height = Dimension.fillToConstraints
|
||||
visibility = if (config.backgroundResId == null) Visibility.Gone else Visibility.Visible
|
||||
},
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(config.iconResId),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size34)
|
||||
.constrainAs(iconRef) {
|
||||
start.linkTo(parent.start, spacing12)
|
||||
linkTo(titleRef.top, subtitleRef.bottom, bias = 0.1f)
|
||||
},
|
||||
)
|
||||
|
||||
val titleText = config.title?.resolveReference()
|
||||
if (titleText != null) {
|
||||
Text(
|
||||
text = titleText,
|
||||
style = TangemTheme.typography.button,
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
modifier = Modifier.constrainAs(titleRef) {
|
||||
top.linkTo(parent.top, spacing12)
|
||||
start.linkTo(iconRef.end, spacing12)
|
||||
end.linkTo(closeIconRef.start, spacing2)
|
||||
width = Dimension.fillToConstraints
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = config.subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
modifier = Modifier.constrainAs(subtitleRef) {
|
||||
top.linkTo(titleRef.bottom, spacing2)
|
||||
start.linkTo(iconRef.end, spacing12)
|
||||
end.linkTo(parent.end, spacing12)
|
||||
bottom.linkTo(buttonRef.top, spacing12, spacing14)
|
||||
width = Dimension.fillToConstraints
|
||||
},
|
||||
)
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.text.constantWhite,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size16)
|
||||
.constrainAs(closeIconRef) {
|
||||
top.linkTo(parent.top, spacing12)
|
||||
end.linkTo(parent.end, spacing12)
|
||||
}
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(bounded = false),
|
||||
) {
|
||||
config.onCloseClick?.invoke()
|
||||
},
|
||||
)
|
||||
val isDarkMode = LocalIsInDarkTheme.current
|
||||
TangemButton(
|
||||
modifier = Modifier.constrainAs(buttonRef) {
|
||||
start.linkTo(parent.start, spacing12)
|
||||
end.linkTo(parent.end, spacing12)
|
||||
bottom.linkTo(parent.bottom, spacing12)
|
||||
width = Dimension.fillToConstraints
|
||||
visibility = if (button == null) {
|
||||
Visibility.Gone
|
||||
} else {
|
||||
Visibility.Visible
|
||||
}
|
||||
},
|
||||
text = button?.text?.resolveReference().orEmpty(),
|
||||
icon = TangemButtonIconPosition.Start(button?.iconResId ?: R.drawable.ic_exchange_vertical_24),
|
||||
onClick = button?.onClick ?: {},
|
||||
colors = ButtonColors(
|
||||
containerColor = if (isDarkMode) Light4 else TangemTheme.colors.button.secondary,
|
||||
contentColor = Dark6,
|
||||
disabledContainerColor = TangemTheme.colors.button.disabled,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
),
|
||||
textStyle = TangemTheme.typography.subtitle1,
|
||||
enabled = true,
|
||||
showProgress = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//region preview
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun NotificationWithBackgroundPreview(
|
||||
@PreviewParameter(NotificationWithBackgroundPreviewProvider::class) config: NotificationConfig,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
NotificationWithBackground(config = config)
|
||||
}
|
||||
}
|
||||
|
||||
private class NotificationWithBackgroundPreviewProvider : PreviewParameterProvider<NotificationConfig> {
|
||||
override val values: Sequence<NotificationConfig>
|
||||
get() = sequenceOf(
|
||||
NotificationConfig(
|
||||
title = resourceReference(id = R.string.main_swap_changelly_promotion_title),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.main_swap_changelly_promotion_message,
|
||||
formatArgs = wrappedList("1", "2", "3"),
|
||||
),
|
||||
iconResId = R.drawable.img_swap_promo,
|
||||
backgroundResId = R.drawable.img_swap_promo_blue_banner_background,
|
||||
),
|
||||
NotificationConfig(
|
||||
title = resourceReference(
|
||||
id = R.string.token_swap_changelly_promotion_title,
|
||||
formatArgs = wrappedList("1"),
|
||||
),
|
||||
subtitle = stringReference(
|
||||
"Swap multiple currencies between any chains you wish. Swap multiple " +
|
||||
"currencies between any chains you wish. Swap multiple " +
|
||||
"currencies between any chains you wish. Commission free period " +
|
||||
"till Dec 31.",
|
||||
),
|
||||
iconResId = R.drawable.img_swap_promo,
|
||||
backgroundResId = R.drawable.img_swap_promo_blue_banner_background,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(id = R.string.token_swap_promotion_button),
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
//endregion
|
||||
|
|
@ -1,169 +0,0 @@
|
|||
package com.tangem.core.ui.components.notifications
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
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.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButton
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.LocalIsInDarkTheme
|
||||
import com.tangem.core.ui.res.TangemColorPalette.Dark6
|
||||
import com.tangem.core.ui.res.TangemColorPalette.Light4
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
private val OxkPromoColor = Color(0xFFBCFF2F)
|
||||
|
||||
@Composable
|
||||
fun OkxPromoNotification(config: NotificationConfig, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(Dark6),
|
||||
) {
|
||||
Content(config = config)
|
||||
Button(config = config)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(config: NotificationConfig) {
|
||||
Row {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(image = ImageVector.vectorResource(id = R.drawable.img_okx_dex_logo)),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.constant,
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens.spacing12)
|
||||
.align(Alignment.CenterVertically),
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
val titleText = config.title?.resolveReference()
|
||||
|
||||
if (titleText != null) {
|
||||
Text(
|
||||
text = titleText,
|
||||
style = TangemTheme.typography.button,
|
||||
color = OxkPromoColor,
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = config.subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.constantWhite,
|
||||
)
|
||||
}
|
||||
config.onCloseClick?.let {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(
|
||||
image = ImageVector.vectorResource(R.drawable.ic_close_24),
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.constant,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing8,
|
||||
end = TangemTheme.dimens.spacing8,
|
||||
)
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(radius = TangemTheme.dimens.radius10),
|
||||
onClick = it,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Button(config: NotificationConfig) {
|
||||
val button = config.buttonsState as? NotificationConfig.ButtonsState.SecondaryButtonConfig
|
||||
|
||||
button?.let {
|
||||
val isDarkMode = LocalIsInDarkTheme.current
|
||||
TangemButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing12,
|
||||
bottom = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
text = button.text.resolveReference(),
|
||||
icon = TangemButtonIconPosition.Start(button.iconResId ?: R.drawable.ic_exchange_vertical_24),
|
||||
onClick = button.onClick,
|
||||
colors = ButtonColors(
|
||||
containerColor = if (isDarkMode) Light4 else TangemTheme.colors.button.secondary,
|
||||
contentColor = Dark6,
|
||||
disabledContainerColor = TangemTheme.colors.button.disabled,
|
||||
disabledContentColor = TangemTheme.colors.text.disabled,
|
||||
),
|
||||
textStyle = TangemTheme.typography.subtitle1,
|
||||
enabled = true,
|
||||
showProgress = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(widthDp = 360)
|
||||
@Preview(widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun OkxPromoNotification_Preview(
|
||||
@PreviewParameter(OkxPromoNotificationPreviewProvider::class) data: NotificationConfig,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
OkxPromoNotification(data)
|
||||
}
|
||||
}
|
||||
|
||||
private class OkxPromoNotificationPreviewProvider : PreviewParameterProvider<NotificationConfig> {
|
||||
override val values: Sequence<NotificationConfig>
|
||||
get() = sequenceOf(
|
||||
NotificationConfig(
|
||||
title = resourceReference(R.string.swap_promo_title),
|
||||
subtitle = resourceReference(R.string.swap_promo_text),
|
||||
iconResId = R.drawable.img_okx_dex_logo,
|
||||
onCloseClick = {},
|
||||
),
|
||||
NotificationConfig(
|
||||
title = resourceReference(R.string.swap_promo_title),
|
||||
subtitle = resourceReference(R.string.swap_promo_text),
|
||||
iconResId = R.drawable.img_okx_dex_logo,
|
||||
onCloseClick = {},
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.token_swap_promotion_button),
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
package com.tangem.core.ui.components.notifications
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.FixedScale
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
@Composable
|
||||
fun RingPromoNotification(config: NotificationConfig, modifier: Modifier = Modifier) {
|
||||
var textHeightDp by remember { mutableStateOf(0.dp) }
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(shape = TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemColorPalette.Dark6),
|
||||
) {
|
||||
PromoImage(
|
||||
backgroundRes = config.backgroundResId,
|
||||
iconRes = config.iconResId,
|
||||
modifier = Modifier.height(textHeightDp),
|
||||
)
|
||||
PromoText(
|
||||
title = config.title,
|
||||
subtitle = config.subtitle,
|
||||
onSizeChange = { textHeightDp = it },
|
||||
)
|
||||
CloseableIconButton(
|
||||
onClick = config.onCloseClick,
|
||||
modifier = Modifier.align(alignment = Alignment.TopEnd),
|
||||
iconTint = TangemTheme.colors.icon.informative,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PromoImage(@DrawableRes backgroundRes: Int?, @DrawableRes iconRes: Int, modifier: Modifier = Modifier) {
|
||||
if (backgroundRes == null) return
|
||||
ConstraintLayout(
|
||||
modifier = modifier,
|
||||
) {
|
||||
val (background, image) = createRefs()
|
||||
|
||||
Image(
|
||||
painter = painterResource(backgroundRes),
|
||||
contentDescription = null,
|
||||
contentScale = FixedScale(2f),
|
||||
modifier = Modifier
|
||||
.constrainAs(background) {
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(image.start)
|
||||
end.linkTo(image.end)
|
||||
},
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(id = iconRes),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier
|
||||
.requiredWidth(width = 60.dp)
|
||||
.wrapContentHeight(Alignment.Top, unbounded = true)
|
||||
.constrainAs(image) {
|
||||
top.linkTo(parent.top, 11.dp)
|
||||
start.linkTo(parent.start, 9.dp)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PromoText(title: TextReference?, subtitle: TextReference, onSizeChange: (Dp) -> Unit) {
|
||||
val density = LocalDensity.current
|
||||
|
||||
Box(
|
||||
modifier = Modifier.onSizeChanged {
|
||||
with(density) { onSizeChange(it.height.toDp()) }
|
||||
},
|
||||
) {
|
||||
TextsBlock(
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
subtitleColor = TangemTheme.colors.text.tertiary,
|
||||
titleColor = TangemTheme.colors.text.constantWhite,
|
||||
modifier = Modifier
|
||||
.wrapContentHeight()
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 80.dp, top = 12.dp, end = 12.dp, bottom = 12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, locale = "fr")
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun RingPromoNotification_Preview() {
|
||||
TangemThemePreview {
|
||||
RingPromoNotification(
|
||||
NotificationConfig(
|
||||
title = resourceReference(id = R.string.ring_promo_title),
|
||||
subtitle = resourceReference(id = R.string.ring_promo_text),
|
||||
iconResId = R.drawable.img_card_with_ring,
|
||||
backgroundResId = R.drawable.img_ring_promo_background,
|
||||
onCloseClick = { },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
|
@ -3,11 +3,11 @@
|
|||
package com.tangem.core.ui.components.progressbar
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -39,7 +39,7 @@ import kotlin.math.abs
|
|||
* @param strokeCap stroke cap to use for the ends of this progress indicator
|
||||
*/
|
||||
@Composable
|
||||
fun LinearProgressIndicator(
|
||||
fun TangemLinearProgressIndicator(
|
||||
progress: () -> Float,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = TangemTheme.colors.text.accent,
|
||||
|
|
@ -61,6 +61,99 @@ fun LinearProgressIndicator(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress indicators loading status with infinite animation
|
||||
*
|
||||
* @param modifier the [Modifier] to be applied to this progress indicator
|
||||
* @param color The color of the progress indicator.
|
||||
* @param backgroundColor The color of the background behind the indicator, visible when the
|
||||
* progress has not reached that area of the overall indicator yet.
|
||||
* @param strokeCap stroke cap to use for the ends of this progress indicator
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun TangemLinearProgressIndicator(
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = TangemTheme.colors.text.accent,
|
||||
backgroundColor: Color = TangemTheme.colors.background.tertiary,
|
||||
strokeCap: StrokeCap = StrokeCap.Round,
|
||||
) {
|
||||
val infiniteTransition = rememberInfiniteTransition()
|
||||
val firstLineHead by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = LINEAR_ANIMATION_DURATION
|
||||
0f at FIRST_LINE_HEAD_DELAY using FIRST_LINE_HEAD_EASING
|
||||
1f at FIRST_LINE_HEAD_DURATION + FIRST_LINE_HEAD_DELAY
|
||||
},
|
||||
),
|
||||
)
|
||||
val firstLineTail by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = LINEAR_ANIMATION_DURATION
|
||||
0f at FIRST_LINE_TAIL_DELAY using FIRST_LINE_TAIL_EASING
|
||||
1f at FIRST_LINE_TAIL_DURATION + FIRST_LINE_TAIL_DELAY
|
||||
},
|
||||
),
|
||||
)
|
||||
val secondLineHead by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = LINEAR_ANIMATION_DURATION
|
||||
0f at SECOND_LINE_HEAD_DELAY using SECOND_LINE_HEAD_EASING
|
||||
1f at SECOND_LINE_HEAD_DURATION + SECOND_LINE_HEAD_DELAY
|
||||
},
|
||||
),
|
||||
)
|
||||
val secondLineTail by infiniteTransition.animateFloat(
|
||||
0f,
|
||||
1f,
|
||||
infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = LINEAR_ANIMATION_DURATION
|
||||
0f at SECOND_LINE_TAIL_DELAY using SECOND_LINE_TAIL_EASING
|
||||
1f at SECOND_LINE_TAIL_DURATION + SECOND_LINE_TAIL_DELAY
|
||||
},
|
||||
),
|
||||
)
|
||||
Canvas(
|
||||
modifier
|
||||
.increaseSemanticsBounds()
|
||||
.semantics(mergeDescendants = true) {
|
||||
progressBarRangeInfo = ProgressBarRangeInfo.Indeterminate
|
||||
}
|
||||
.size(height = 4.dp, width = 24.dp),
|
||||
) {
|
||||
val strokeWidth = size.height
|
||||
drawLinearIndicatorBackground(backgroundColor, strokeWidth, strokeCap)
|
||||
if (firstLineHead - firstLineTail > 0) {
|
||||
drawLinearIndicator(
|
||||
firstLineHead,
|
||||
firstLineTail,
|
||||
color,
|
||||
strokeWidth,
|
||||
strokeCap,
|
||||
)
|
||||
}
|
||||
if ((secondLineHead - secondLineTail) > 0) {
|
||||
drawLinearIndicator(
|
||||
secondLineHead,
|
||||
secondLineTail,
|
||||
color,
|
||||
strokeWidth,
|
||||
strokeCap,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Modifier.increaseSemanticsBounds(): Modifier {
|
||||
val padding = 10.dp
|
||||
return this
|
||||
|
|
@ -129,16 +222,51 @@ private fun DrawScope.drawLinearIndicator(
|
|||
private fun DrawScope.drawLinearIndicatorBackground(color: Color, strokeWidth: Float, strokeCap: StrokeCap) =
|
||||
drawLinearIndicator(0f, 1f, color, strokeWidth, strokeCap)
|
||||
|
||||
// Indeterminate linear indicator transition specs
|
||||
// Total duration for one cycle
|
||||
private const val LINEAR_ANIMATION_DURATION = 1800
|
||||
|
||||
// Duration of the head and tail animations for both lines
|
||||
private const val FIRST_LINE_HEAD_DURATION = 750
|
||||
private const val FIRST_LINE_TAIL_DURATION = 850
|
||||
private const val SECOND_LINE_HEAD_DURATION = 567
|
||||
private const val SECOND_LINE_TAIL_DURATION = 533
|
||||
|
||||
// Delay before the start of the head and tail animations for both lines
|
||||
private const val FIRST_LINE_HEAD_DELAY = 0
|
||||
private const val FIRST_LINE_TAIL_DELAY = 333
|
||||
private const val SECOND_LINE_HEAD_DELAY = 1000
|
||||
private const val SECOND_LINE_TAIL_DELAY = 1267
|
||||
|
||||
private val FIRST_LINE_HEAD_EASING = CubicBezierEasing(0.2f, 0f, 0.8f, 1f)
|
||||
private val FIRST_LINE_TAIL_EASING = CubicBezierEasing(0.4f, 0f, 1f, 1f)
|
||||
private val SECOND_LINE_HEAD_EASING = CubicBezierEasing(0f, 0f, 0.65f, 1f)
|
||||
private val SECOND_LINE_TAIL_EASING = CubicBezierEasing(0.1f, 0f, 0.45f, 1f)
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun LinearProgressIndicator_Preview() {
|
||||
TangemThemePreview {
|
||||
LinearProgressIndicator(
|
||||
progress = { 0.5f },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.secondary)
|
||||
.padding(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
TangemLinearProgressIndicator(
|
||||
progress = { 0.6f },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||
)
|
||||
TangemLinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -87,6 +87,8 @@ fun getActiveIconRes(blockchainId: String): Int {
|
|||
"fact0rn" -> R.drawable.img_fact0rn_22
|
||||
"odyssey", "odyssey/test" -> R.drawable.img_odyssey_chain_22
|
||||
"bitrock", "bitrock/test" -> R.drawable.img_bitrock_22
|
||||
"sonic", "sonic/test" -> R.drawable.img_sonic_22
|
||||
"apechain", "apechain/test" -> R.drawable.img_apecoin_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -172,6 +174,8 @@ fun getActiveIconResByCoinId(coinId: String): Int {
|
|||
"fact0rn" -> R.drawable.img_fact0rn_22
|
||||
"odyssey", "odyssey/test" -> R.drawable.img_odyssey_chain_22
|
||||
"bitrock", "bitrock/test" -> R.drawable.img_bitrock_22
|
||||
"sonic", "sonic/test" -> R.drawable.img_sonic_22
|
||||
"apechain", "apechain/test" -> R.drawable.img_apecoin_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -260,6 +264,8 @@ fun getGreyedOutIconRes(blockchainId: String): Int {
|
|||
"fact0rn" -> R.drawable.ic_fact0rn_22
|
||||
"odyssey", "odyssey/test" -> R.drawable.ic_odyssey_chain_22
|
||||
"bitrock", "bitrock/test" -> R.drawable.ic_bitrock_22
|
||||
"sonic", "sonic/test" -> R.drawable.ic_sonic_22
|
||||
"apechain", "apechain/test" -> R.drawable.ic_apecoin_22
|
||||
else -> R.drawable.ic_alert_24
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.CornerRadius
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.isSimple
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Outline
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
||||
/**
|
||||
* Modifier that adds an outset border to the content.
|
||||
* @see [androidx.compose.foundation.border]
|
||||
*
|
||||
* @param width The width of the border.
|
||||
* @param color The color of the border.
|
||||
* @param shape The shape of the border.
|
||||
*/
|
||||
fun Modifier.outsetBorder(width: Dp, color: Color, shape: Shape) = drawWithCache {
|
||||
val outline = shape.createOutline(size, layoutDirection, this)
|
||||
val cornerRadius = when {
|
||||
outline is Outline.Rounded && outline.roundRect.isSimple -> outline.roundRect.topLeftCornerRadius
|
||||
else -> CornerRadius.Zero
|
||||
}
|
||||
|
||||
onDrawWithContent {
|
||||
drawContent()
|
||||
drawRoundRect(
|
||||
color = color,
|
||||
topLeft = Offset(-width.toPx() / 2, -width.toPx() / 2),
|
||||
size = size.copy(
|
||||
width = size.width + width.toPx(),
|
||||
height = size.height + width.toPx(),
|
||||
),
|
||||
cornerRadius = cornerRadius,
|
||||
style = Stroke(width = width.toPx()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.core.ui.security
|
||||
|
||||
import android.view.WindowManager
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.tangem.core.ui.utils.findActivity
|
||||
|
||||
/**
|
||||
* Disables screenshots for the current composition.
|
||||
*/
|
||||
@Composable
|
||||
fun DisableScreenshotsDisposableEffect() {
|
||||
val activity = LocalContext.current.findActivity()
|
||||
DisposableEffect(activity) {
|
||||
activity.window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE,
|
||||
)
|
||||
onDispose {
|
||||
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
|
||||
fun Context.findActivity(): Activity {
|
||||
var context = this
|
||||
while (context is ContextWrapper) {
|
||||
if (context is Activity) return context
|
||||
context = context.baseContext
|
||||
}
|
||||
error("Cannot find activity. Please make sure that the context is attached to an activity.")
|
||||
}
|
||||
78
core/ui/src/main/res/drawable/ic_apecoin_22.xml
Normal file
78
core/ui/src/main/res/drawable/ic_apecoin_22.xml
Normal file
File diff suppressed because one or more lines are too long
9
core/ui/src/main/res/drawable/ic_loader.xml
Normal file
9
core/ui/src/main/res/drawable/ic_loader.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="8dp" android:viewportHeight="8" android:viewportWidth="32" android:width="32dp">
|
||||
|
||||
<path android:fillColor="#0099FF" android:pathData="M28,8C25.791,8 24,6.209 24,4C24,1.791 25.791,0 28,0C30.209,0 32,1.791 32,4C32,6.209 30.209,8 28,8Z"/>
|
||||
|
||||
<path android:fillAlpha="0.55" android:fillColor="#0099FF" android:pathData="M15,7C13.343,7 12,5.657 12,4C12,2.343 13.343,1 15,1C16.657,1 18,2.343 18,4C18,5.657 16.657,7 15,7Z" android:strokeAlpha="0.55"/>
|
||||
|
||||
<path android:fillAlpha="0.25" android:fillColor="#0099FF" android:pathData="M3,7C1.343,7 0,5.657 0,4C0,2.343 1.343,1 3,1C4.657,1 6,2.343 6,4C6,5.657 4.657,7 3,7Z" android:strokeAlpha="0.25"/>
|
||||
|
||||
</vector>
|
||||
10
core/ui/src/main/res/drawable/ic_sonic_22.xml
Normal file
10
core/ui/src/main/res/drawable/ic_sonic_22.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<path
|
||||
android:pathData="M9.154,4.949C11.859,7.642 15.264,9.422 18.998,10.097C18.548,6.106 15.147,3 11.014,3C9.922,3 8.882,3.218 7.932,3.61C8.318,4.074 8.729,4.525 9.154,4.949ZM3,11.597C3.121,13.214 3.722,14.698 4.665,15.904L4.709,15.861C5.678,14.906 6.94,14.037 8.462,13.28C9.797,12.616 11.329,12.046 12.98,11.597H3ZM12.63,12.942L12.632,12.941L12.634,12.939L12.63,12.942ZM12.63,12.942C11.053,13.828 9.588,14.931 8.308,16.205C7.831,16.68 7.373,17.184 6.945,17.706L6.839,17.835C6.345,17.536 5.889,17.184 5.472,16.79L5.542,16.722C7.107,15.172 9.607,13.849 12.63,12.942ZM5.542,5.278C7.107,6.83 9.609,8.152 12.632,9.061C11.054,8.173 9.588,7.07 8.307,5.795C7.831,5.322 7.373,4.818 6.943,4.294L6.837,4.165C6.344,4.464 5.887,4.816 5.472,5.21L5.542,5.278ZM9.154,17.051C8.728,17.475 8.316,17.926 7.932,18.39C8.88,18.782 9.922,19 11.014,19C15.147,19 18.548,15.894 19,11.901C15.266,12.576 11.861,14.357 9.155,17.05L9.154,17.051ZM8.462,8.72C6.94,7.963 5.678,7.094 4.709,6.139L4.665,6.096C3.722,7.302 3.121,8.786 3,10.403H12.978C11.327,9.954 9.797,9.384 8.461,8.718L8.462,8.72Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
18
core/ui/src/main/res/drawable/ic_tangem_card_grey.xml
Normal file
18
core/ui/src/main/res/drawable/ic_tangem_card_grey.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="315dp"
|
||||
android:height="198dp"
|
||||
android:viewportWidth="315"
|
||||
android:viewportHeight="198">
|
||||
<path
|
||||
android:pathData="M10,0L305,0A10,10 0,0 1,315 10L315,188A10,10 0,0 1,305 198L10,198A10,10 0,0 1,0 188L0,10A10,10 0,0 1,10 0z"
|
||||
android:fillColor="#C9C9C9"/>
|
||||
<path
|
||||
android:pathData="M167.85,73.95H147.56C144.35,73.95 142.75,73.95 141.53,74.57C140.45,75.13 139.58,76.01 139.03,77.09C138.41,78.33 138.41,79.94 138.41,83.17V86.66H177V83.17C177,79.94 177,78.33 176.38,77.09C175.83,76.01 174.95,75.13 173.88,74.57C172.66,73.95 171.05,73.95 167.85,73.95Z"
|
||||
android:fillColor="#4A4A4A"/>
|
||||
<path
|
||||
android:pathData="M164.13,97.13H177V113.35C177,116.57 177,118.18 176.37,119.42C175.83,120.5 174.95,121.38 173.88,121.94C172.65,122.56 171.05,122.56 167.85,122.56H164.13V97.13Z"
|
||||
android:fillColor="#4A4A4A"/>
|
||||
<path
|
||||
android:pathData="M151.27,97.13H138.41V113.35C138.41,116.57 138.41,118.18 139.03,119.42C139.58,120.5 140.45,121.38 141.53,121.94C142.75,122.56 144.35,122.56 147.55,122.56H151.27V97.13Z"
|
||||
android:fillColor="#4A4A4A"/>
|
||||
</vector>
|
||||
7
core/ui/src/main/res/drawable/ic_wallet_filled_24.xml
Normal file
7
core/ui/src/main/res/drawable/ic_wallet_filled_24.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<vector 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:fillColor="#1E1E1E" android:fillType="evenOdd" android:pathData="M5,6.25C3.481,6.25 2.25,7.481 2.25,9V18C2.25,19.519 3.481,20.75 5,20.75H19C20.519,20.75 21.75,19.519 21.75,18V9C21.75,7.481 20.519,6.25 19,6.25H5ZM16.5,12.25C15.81,12.25 15.25,12.81 15.25,13.5C15.25,14.19 15.81,14.75 16.5,14.75C17.19,14.75 17.75,14.19 17.75,13.5C17.75,12.81 17.19,12.25 16.5,12.25Z"/>
|
||||
|
||||
<path android:fillColor="#1E1E1E" android:pathData="M16.485,3.069C17.754,2.73 19,3.687 19,5.001H9L16.485,3.069Z"/>
|
||||
|
||||
</vector>
|
||||
88
core/ui/src/main/res/drawable/img_apecoin_22.xml
Normal file
88
core/ui/src/main/res/drawable/img_apecoin_22.xml
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 786 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB |
20
core/ui/src/main/res/drawable/img_sonic_22.xml
Normal file
20
core/ui/src/main/res/drawable/img_sonic_22.xml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="22dp"
|
||||
android:height="22dp"
|
||||
android:viewportWidth="22"
|
||||
android:viewportHeight="22">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"/>
|
||||
<path
|
||||
android:pathData="M11,0L11,0A11,11 0,0 1,22 11L22,11A11,11 0,0 1,11 22L11,22A11,11 0,0 1,0 11L0,11A11,11 0,0 1,11 0z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M0,0h22v22h-22z"
|
||||
android:fillColor="#080A0B"/>
|
||||
<path
|
||||
android:pathData="M9.154,4.949C11.859,7.642 15.264,9.422 18.999,10.097C18.548,6.106 15.147,3 11.014,3C9.922,3 8.882,3.218 7.932,3.61C8.318,4.074 8.729,4.525 9.154,4.949ZM3,11.597C3.121,13.214 3.722,14.698 4.665,15.904L4.709,15.861C5.678,14.906 6.94,14.037 8.462,13.28C9.797,12.616 11.329,12.046 12.98,11.597H3ZM12.63,12.942L12.632,12.941L12.634,12.939L12.63,12.942ZM12.63,12.942C11.053,13.828 9.588,14.931 8.308,16.205C7.831,16.68 7.373,17.184 6.945,17.706L6.839,17.835C6.345,17.536 5.889,17.184 5.472,16.79L5.542,16.722C7.107,15.172 9.607,13.849 12.63,12.942ZM5.542,5.278C7.107,6.83 9.609,8.152 12.632,9.061C11.054,8.173 9.588,7.07 8.307,5.795C7.831,5.322 7.373,4.818 6.943,4.294L6.837,4.165C6.344,4.464 5.887,4.816 5.472,5.21L5.542,5.278ZM9.154,17.051C8.728,17.475 8.316,17.926 7.932,18.39C8.88,18.782 9.922,19 11.014,19C15.147,19 18.548,15.894 19,11.901C15.266,12.576 11.861,14.357 9.155,17.05L9.154,17.051ZM8.462,8.72C6.94,7.963 5.678,7.094 4.709,6.139L4.665,6.096C3.722,7.302 3.121,8.786 3,10.403H12.978C11.327,9.954 9.797,9.384 8.461,8.718L8.462,8.72Z"
|
||||
android:fillColor="#F5F5F5"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Loading…
Add table
Add a link
Reference in a new issue