Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-04 15:19:16 +03:00
parent 1858d5453a
commit 90abe449d6
23 changed files with 1741 additions and 11 deletions

View file

@ -0,0 +1,202 @@
package com.tangem.core.ui.ds.image
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
/**
* Composable function for displaying a wallet icon based on the provided [DeviceIconUM] state.
*
* The icon can represent different types of devices, such as cards, rings, stubs, or mobile wallet,
* with customizable colors and styles.
*
* @param state The state of the device icon, which determines its appearance.
* @param modifier Optional [Modifier] for styling the composable.
*/
@Composable
fun TangemDeviceIcon(state: DeviceIconUM, modifier: Modifier = Modifier) {
when (state) {
is DeviceIconUM.Card -> DeviceIcon(
modifier = modifier,
isRing = false,
mainColor = state.mainColor,
secondColor = state.secondColor,
thirdColor = state.thirdColor,
tColor = null,
)
is DeviceIconUM.Ring -> DeviceIcon(
modifier = modifier,
isRing = true,
mainColor = state.mainColor,
secondColor = state.cardColor,
thirdColor = state.secondCardColor,
tColor = null,
)
is DeviceIconUM.Stub -> DeviceIcon(
modifier = modifier,
isRing = false,
mainColor = Color.Unspecified,
secondColor = Color.Unspecified.takeIf { state.cardsCount > 1 },
thirdColor = Color.Unspecified.takeIf { state.cardsCount > 2 },
tColor = TangemTheme.colors2.graphic.neutral.secondary,
)
DeviceIconUM.Mobile -> Icon(
modifier = modifier,
imageVector = ImageVector.vectorResource(R.drawable.ic_shield_24),
contentDescription = null,
tint = TangemTheme.colors2.graphic.status.attention,
)
}
}
@Composable
private fun DeviceIcon(
isRing: Boolean,
mainColor: Color,
secondColor: Color?,
thirdColor: Color?,
tColor: Color?,
modifier: Modifier = Modifier,
) {
val main = mainColor.takeOrElse { TangemTheme.colors2.graphic.neutral.tertiaryConstant }
val second = secondColor?.takeOrElse { TangemTheme.colors2.graphic.neutral.tertiaryConstant }
val third = thirdColor?.takeOrElse { TangemTheme.colors2.graphic.neutral.tertiaryConstant }
val borderColor = TangemTheme.colors2.border.walletIcon
val imageVector = remember(isRing, main, second, third, borderColor, tColor) {
when {
isRing && second != null && third != null -> WalletIconVectorBuilders.buildRingWithCard2(
mainColor = main,
cardColor = second,
secondCardColor = third,
borderColor = borderColor,
)
!isRing && second != null && third != null -> WalletIconVectorBuilders.buildCard3(
mainColor = main,
secondColor = second,
thirdColor = third,
tColor = tColor,
borderColor = borderColor,
)
isRing && second != null -> WalletIconVectorBuilders.buildRingWithCard(
mainColor = main,
cardColor = second,
borderColor = borderColor,
)
!isRing && second != null -> WalletIconVectorBuilders.buildCard2(
mainColor = main,
secondColor = second,
tColor = tColor,
borderColor = borderColor,
)
isRing -> WalletIconVectorBuilders.buildRing(
mainColor = main,
borderColor = borderColor,
)
else -> WalletIconVectorBuilders.buildCard(
mainColor = main,
borderColor = borderColor,
tColor = tColor,
)
}
}
Icon(
imageVector = imageVector,
contentDescription = null,
modifier = modifier,
tint = Color.Unspecified,
)
}
// region Preview
private val previewCardBlue
get() = Color(0xFF1C5FBF)
private val previewCardGold
get() = Color(0xFFD4A017)
private val previewCardPurple
get() = Color(0xFF7B2FBE)
private val previewRingGreen
get() = Color(0xFF2ECC71)
private val previewStates: List<Pair<String, DeviceIconUM>>
get() = listOf(
"Card 1" to DeviceIconUM.Card(
mainColor = previewCardBlue,
secondColor = null,
),
"Card 2" to DeviceIconUM.Card(
mainColor = previewCardBlue,
secondColor = previewCardGold,
),
"Card 3" to DeviceIconUM.Card(
mainColor = previewCardBlue,
secondColor = previewCardGold,
thirdColor = previewCardPurple,
),
"Ring" to DeviceIconUM.Ring(
mainColor = previewRingGreen,
),
"Ring + Card" to DeviceIconUM.Ring(
mainColor = previewRingGreen,
cardColor = previewCardBlue,
),
"Ring + 2 Cards" to DeviceIconUM.Ring(
mainColor = previewRingGreen,
cardColor = previewCardBlue,
secondCardColor = previewCardGold,
),
"Stub 1" to DeviceIconUM.Stub(cardsCount = 1),
"Stub 2" to DeviceIconUM.Stub(cardsCount = 2),
"Stub 3" to DeviceIconUM.Stub(cardsCount = 3),
"Mobile" to DeviceIconUM.Mobile,
)
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun TangemDeviceIcon_Preview() {
TangemThemePreviewRedesign {
Column(
modifier = Modifier
.background(TangemTheme.colors.background.primary)
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
previewStates.forEach { (label, state) ->
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
TangemDeviceIcon(
modifier = Modifier.size(40.dp),
state = state,
)
Text(
text = label,
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
)
}
}
}
}
}
// endregion

View file

@ -0,0 +1,24 @@
package com.tangem.core.ui.ds.image
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
@Immutable
sealed interface DeviceIconUM {
data class Card(
val mainColor: Color,
val secondColor: Color?,
val thirdColor: Color? = null,
) : DeviceIconUM
data class Ring(
val mainColor: Color = Color.Unspecified,
val cardColor: Color? = null,
val secondCardColor: Color? = null,
) : DeviceIconUM
data class Stub(val cardsCount: Int) : DeviceIconUM
data object Mobile : DeviceIconUM
}

File diff suppressed because it is too large Load diff

View file

@ -326,7 +326,10 @@ class TangemColors2 internal constructor(
class Border internal constructor(
val neutral: Neutral,
val status: Status,
walletIcon: Color,
) {
var walletIcon by mutableStateOf(walletIcon)
private set
@Stable
class Neutral internal constructor(
@ -367,6 +370,7 @@ class TangemColors2 internal constructor(
fun update(other: Border) {
neutral.update(other.neutral)
status.update(other.status)
walletIcon = other.walletIcon
}
}

View file

@ -85,6 +85,7 @@ private fun lightThemeColors2(): TangemColors2 {
warning = TangemColorPalette.Amaranth,
attention = TangemColorPalette.Tangerine,
),
walletIcon = TangemColorPalette.Dark_10,
)
val overlay = TangemColors2.Overlay(
overlayPrimary = TangemColorPalette.Overlay1,
@ -247,6 +248,7 @@ private fun darkThemeColors2(): TangemColors2 {
warning = TangemColorPalette.Flamingo,
attention = TangemColorPalette.Mustard,
),
walletIcon = TangemColorPalette.Light_10,
)
val overlay = TangemColors2.Overlay(
overlayPrimary = TangemColorPalette.Overlay1,

View file

@ -0,0 +1,5 @@
<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="#000000" android:pathData="M12,21C11.922,21 11.824,20.984 11.707,20.952C11.596,20.927 11.478,20.882 11.354,20.818C9.992,20.06 8.838,19.391 7.892,18.811C6.954,18.232 6.197,17.671 5.623,17.129C5.056,16.581 4.642,15.992 4.381,15.361C4.127,14.724 4,13.975 4,13.115V5.909C4,5.418 4.108,5.065 4.323,4.848C4.538,4.625 4.848,4.434 5.252,4.275C5.48,4.185 5.793,4.068 6.191,3.921C6.588,3.768 7.025,3.606 7.501,3.434C7.984,3.255 8.463,3.083 8.939,2.918C9.421,2.745 9.861,2.593 10.259,2.459C10.657,2.319 10.97,2.21 11.198,2.134C11.328,2.096 11.459,2.064 11.589,2.038C11.726,2.013 11.863,2 12,2C12.137,2 12.274,2.013 12.411,2.038C12.548,2.064 12.681,2.096 12.812,2.134C13.04,2.21 13.35,2.319 13.741,2.459C14.139,2.593 14.575,2.745 15.051,2.918C15.534,3.09 16.013,3.262 16.489,3.434C16.972,3.606 17.412,3.765 17.809,3.911C18.207,4.058 18.52,4.179 18.748,4.275C19.159,4.44 19.469,4.631 19.677,4.848C19.892,5.065 20,5.418 20,5.909V13.115C20,13.975 19.876,14.73 19.628,15.38C19.381,16.024 18.973,16.626 18.406,17.187C17.845,17.747 17.092,18.314 16.147,18.888C15.208,19.461 14.041,20.105 12.646,20.818C12.522,20.882 12.401,20.927 12.284,20.952C12.173,20.984 12.078,21 12,21ZM7.599,11.815C7.599,11.943 7.641,12.048 7.726,12.131C7.817,12.207 7.928,12.245 8.059,12.245H11.482L9.653,17.034C9.568,17.244 9.571,17.416 9.663,17.55C9.76,17.684 9.894,17.754 10.064,17.76C10.233,17.76 10.386,17.674 10.523,17.502L16.049,10.726C16.16,10.598 16.215,10.471 16.215,10.344C16.215,10.216 16.17,10.111 16.078,10.028C15.993,9.945 15.886,9.904 15.755,9.904H12.333L14.161,5.116C14.246,4.905 14.24,4.737 14.142,4.609C14.05,4.475 13.92,4.408 13.751,4.408C13.588,4.402 13.434,4.485 13.291,4.657L7.765,11.433C7.654,11.554 7.599,11.682 7.599,11.815Z"/>
</vector>