Updated on 2026-08-14

This commit is contained in:
Tangem 2022-12-06 20:56:19 +03:00
parent 019e98b710
commit e0ae64248d
17 changed files with 417 additions and 144 deletions

View file

@ -48,9 +48,9 @@ import kotlin.coroutines.suspendCoroutine
class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) {
val canUseBiometry: Boolean
get() = tangemSdk.biometricManager.canAuthenticate || canEnrollBiometrics
get() = tangemSdk.biometricManager.canAuthenticate || needEnrollBiometrics
val canEnrollBiometrics: Boolean
val needEnrollBiometrics: Boolean
get() = tangemSdk.biometricManager.canEnrollBiometrics
val biometricManager: BiometricManager

View file

@ -30,6 +30,7 @@ import com.tangem.tap.userWalletsListManager
import com.tangem.tap.walletStoresManager
import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.rekotlin.Action
@ -194,18 +195,39 @@ class DetailsMiddleware {
fun handle(state: DetailsState, action: DetailsAction.AppSettings) {
when (action) {
is DetailsAction.AppSettings.SwitchPrivacySetting -> {
if (tangemSdkManager.canEnrollBiometrics) {
store.dispatch(DetailsAction.AppSettings.EnrollBiometrics)
}
when (action.setting) {
PrivacySetting.SaveWallets -> toggleSaveWallets(state, enable = action.enable)
PrivacySetting.SaveAccessCode -> toggleSaveAccessCodes(state, enable = action.enable)
}
}
is DetailsAction.AppSettings.SwitchPrivacySetting.Success -> Unit
is DetailsAction.AppSettings.EnrollBiometrics -> Unit
is DetailsAction.AppSettings.EnrollBiometrics.Enroll -> enrollBiometrics()
is DetailsAction.AppSettings.EnrollBiometrics.Cancel -> Unit
is DetailsAction.AppSettings.CheckBiometricsStatus -> {
checkBiometricsStatus(action.awaitStatusChange, state)
}
is DetailsAction.AppSettings.EnrollBiometrics -> {
enrollBiometrics()
}
is DetailsAction.AppSettings.SwitchPrivacySetting.Success,
is DetailsAction.AppSettings.BiometricsStatusChanged,
-> Unit
}
}
/**
* @param awaitStatusChange If true then start a new coroutine and check the biometric status every 100
* milliseconds until it changes
* */
private fun checkBiometricsStatus(awaitStatusChange: Boolean, state: DetailsState) {
scope.launch {
if (awaitStatusChange) {
while (state.needEnrollBiometrics == tangemSdkManager.needEnrollBiometrics) {
delay(timeMillis = 100)
}
}
store.dispatchOnMain(
DetailsAction.AppSettings.BiometricsStatusChanged(
needEnrollBiometrics = tangemSdkManager.needEnrollBiometrics,
),
)
}
}

View file

@ -157,15 +157,17 @@ private fun handlePrivacyAction(
state: DetailsState,
): DetailsState {
return when (action) {
is DetailsAction.AppSettings.SwitchPrivacySetting -> state
is DetailsAction.AppSettings.SwitchPrivacySetting.Success -> when (action.setting) {
PrivacySetting.SaveWallets -> state.copy(saveWallets = action.enable)
PrivacySetting.SaveAccessCode -> state.copy(saveAccessCodes = action.enable)
}
is DetailsAction.AppSettings.EnrollBiometrics -> state.copy(needEnrollBiometrics = true)
is DetailsAction.AppSettings.EnrollBiometrics.Enroll,
is DetailsAction.AppSettings.EnrollBiometrics.Cancel,
-> state.copy(needEnrollBiometrics = false)
is DetailsAction.AppSettings.BiometricsStatusChanged -> state.copy(
needEnrollBiometrics = action.needEnrollBiometrics,
)
is DetailsAction.AppSettings.SwitchPrivacySetting,
is DetailsAction.AppSettings.EnrollBiometrics,
is DetailsAction.AppSettings.CheckBiometricsStatus,
-> state
}
}

View file

@ -9,11 +9,12 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.google.accompanist.appcompattheme.AppCompatTheme
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.details.redux.DetailsState
import com.tangem.tap.store
import com.tangem.wallet.R
import org.rekotlin.StoreSubscriber
class AppSettingsFragment : Fragment(), StoreSubscriber<DetailsState> {
@ -24,8 +25,9 @@ class AppSettingsFragment : Fragment(), StoreSubscriber<DetailsState> {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(android.R.transition.fade)
exitTransition = inflater.inflateTransition(android.R.transition.fade)
enterTransition = inflater.inflateTransition(R.transition.fade)
exitTransition = inflater.inflateTransition(R.transition.fade)
viewModel.checkBiometricsStatus()
}
override fun onCreateView(
@ -58,6 +60,11 @@ class AppSettingsFragment : Fragment(), StoreSubscriber<DetailsState> {
}
}
override fun onResume() {
super.onResume()
viewModel.refreshBiometricsStatus()
}
override fun onStop() {
super.onStop()
store.unsubscribe(this)

View file

@ -1,24 +1,31 @@
package com.tangem.tap.features.details.ui.appsettings
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material.AlertDialog
import androidx.compose.material.MaterialTheme
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.tangem.core.ui.components.dialogs.EnrollBiometricsDialogContent
import com.tangem.core.ui.models.EnrollBiometricsDialog
import com.tangem.tap.common.compose.TangemTypography
import com.tangem.core.ui.components.SpacerH24
import com.tangem.core.ui.components.SpacerH32
import com.tangem.core.ui.components.SpacerH4
import com.tangem.core.ui.components.SpacerW32
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.details.redux.PrivacySetting
import com.tangem.tap.features.details.ui.appsettings.components.EnrollBiometricsCard
import com.tangem.tap.features.details.ui.appsettings.components.SettingsAlertDialog
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
import com.tangem.tap.features.details.ui.common.TangemSwitch
import com.tangem.wallet.R
@ -51,47 +58,43 @@ private fun AppSettings(
element = it,
onDialogStateChange = onDialogStateChange,
onSettingToggled = state.onSettingToggled,
modifier = modifier,
)
}
EnrollBiometricsDialog(dialog = state.enrollBiometricsDialog)
Column(
modifier = modifier
.fillMaxSize(),
) {
if (state.showEnrollBiometricsCard) {
EnrollBiometricsCard(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing8)
.fillMaxWidth(),
onClick = state.onEnrollBiometrics,
)
SpacerH24()
}
AppSettingsElement(
state = state,
setting = PrivacySetting.SaveWallets,
onDialogStateChange = onDialogStateChange,
modifier = modifier,
)
Spacer(modifier = Modifier.size(32.dp))
SpacerH32()
AppSettingsElement(
state = state,
setting = PrivacySetting.SaveAccessCode,
onDialogStateChange = onDialogStateChange,
modifier = modifier,
)
}
}
@Composable
private fun EnrollBiometricsDialog(
modifier: Modifier = Modifier,
dialog: EnrollBiometricsDialog?,
) {
if (dialog == null) return
EnrollBiometricsDialogContent(modifier, dialog)
}
@Composable
private fun AppSettingsElement(
modifier: Modifier = Modifier,
state: AppSettingsScreenState,
setting: PrivacySetting,
onDialogStateChange: (PrivacySetting?) -> Unit,
modifier: Modifier = Modifier,
) {
val titleRes = when (setting) {
PrivacySetting.SaveWallets -> R.string.app_settings_saved_wallet
@ -103,48 +106,63 @@ private fun AppSettingsElement(
}
val checked = state.settings[setting] ?: false
val titleTextColor by rememberUpdatedState(
newValue = if (state.isTogglesEnabled) {
TangemTheme.colors.text.primary1
} else {
TangemTheme.colors.text.secondary
},
)
val descriptionTextColor by rememberUpdatedState(
newValue = if (state.isTogglesEnabled) {
TangemTheme.colors.text.secondary
} else {
TangemTheme.colors.text.tertiary
},
)
Row(
modifier = modifier
.fillMaxWidth()
.padding(start = 20.dp),
.padding(horizontal = TangemTheme.dimens.spacing20),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Column(
modifier = Modifier.weight(weight = .9f),
verticalArrangement = Arrangement.Center,
modifier = modifier
.weight(0.6f)
.padding(end = 4.dp),
) {
Text(
text = stringResource(id = titleRes),
style = TangemTypography.subtitle1,
color = colorResource(id = R.color.text_primary_1),
style = TangemTheme.typography.subtitle1,
color = titleTextColor,
)
Spacer(modifier = Modifier.size(4.dp))
SpacerH4()
Text(
text = stringResource(id = subtitleRes),
style = TangemTypography.body2,
color = colorResource(id = R.color.text_secondary),
style = TangemTheme.typography.body2,
color = descriptionTextColor,
)
}
SpacerW32()
TangemSwitch(
checked = checked,
onCheckedChange = {
enabled = state.isTogglesEnabled,
onCheckedChange = { isChecked ->
onCheckedChange(
element = setting,
enabled = it,
enabled = isChecked,
onSettingToggled = state.onSettingToggled,
onDialogStateChange = onDialogStateChange,
)
},
modifier = modifier
.padding(20.dp),
)
}
}
private fun onCheckedChange(
element: PrivacySetting, enabled: Boolean,
element: PrivacySetting,
enabled: Boolean,
onSettingToggled: (PrivacySetting, Boolean) -> Unit,
onDialogStateChange: (PrivacySetting?) -> Unit,
) {
@ -152,82 +170,88 @@ private fun onCheckedChange(
if (!enabled) {
onDialogStateChange(element)
} else {
onSettingToggled(element, enabled)
onSettingToggled(element, true)
}
}
// region Preview
@Composable
private fun SettingsAlertDialog(
element: PrivacySetting,
onDialogStateChange: (PrivacySetting?) -> Unit,
onSettingToggled: (PrivacySetting, Boolean) -> Unit,
private fun AppSettingsScreenSample(
modifier: Modifier = Modifier,
) {
val text = when (element) {
PrivacySetting.SaveWallets -> R.string.app_settings_off_saved_wallet_alert_message
PrivacySetting.SaveAccessCode -> R.string.app_settings_off_saved_access_code_alert_message
Column(
modifier = modifier
.background(TangemTheme.colors.background.primary),
) {
AppSettingsScreen(
state = AppSettingsScreenState(
settings = mapOf(
PrivacySetting.SaveWallets to true,
PrivacySetting.SaveAccessCode to false,
),
showEnrollBiometricsCard = false,
isTogglesEnabled = true,
onSettingToggled = { _, _ -> },
onEnrollBiometrics = {},
),
onBackPressed = { },
)
}
AlertDialog(
onDismissRequest = { onDialogStateChange(null) },
confirmButton = {
TextButton(
onClick = {
onDialogStateChange(null)
},
) {
Text(
text = stringResource(id = R.string.common_cancel),
color = colorResource(id = R.color.text_secondary),
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
)
}
},
dismissButton = {
TextButton(
onClick = {
onDialogStateChange(null)
onSettingToggled(element, false)
},
modifier = modifier.padding(bottom = 14.dp),
) {
Text(
text = stringResource(id = R.string.common_delete),
color = colorResource(id = R.color.text_warning),
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
)
}
},
title = {
Text(
text = stringResource(id = R.string.common_attention),
color = colorResource(id = R.color.text_primary_1),
)
},
text = {
Text(
text = stringResource(id = text),
color = colorResource(id = R.color.text_secondary),
)
},
shape = MaterialTheme.shapes.medium.copy(all = CornerSize(size = 28.dp)),
modifier = modifier.padding(vertical = 34.dp, horizontal = 24.dp),
)
}
@Preview
@Preview(showBackground = true, widthDp = 360)
@Composable
fun AppSettingsScreenPreview() {
AppSettingsScreen(
state = AppSettingsScreenState(
settings = mapOf(
PrivacySetting.SaveWallets to true,
PrivacySetting.SaveAccessCode to false,
private fun AppSettingsScreenPreview_Light() {
TangemTheme {
AppSettingsScreenSample()
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun AppSettingsScreenPreview_Dark() {
TangemTheme(isDark = true) {
AppSettingsScreenSample()
}
}
@Composable
private fun AppSettingsScreen_EnrollBiometrics_Sample(
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.background(TangemTheme.colors.background.primary),
) {
AppSettingsScreen(
state = AppSettingsScreenState(
settings = mapOf(
PrivacySetting.SaveWallets to true,
PrivacySetting.SaveAccessCode to false,
),
showEnrollBiometricsCard = true,
isTogglesEnabled = false,
onSettingToggled = { _, _ -> },
onEnrollBiometrics = {},
),
onSettingToggled = { _, _ -> },
enrollBiometricsDialog = null,
),
onBackPressed = { },
)
}
onBackPressed = { },
)
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun AppSettingsScreen_EnrollBiometrics_Preview_Light() {
TangemTheme {
AppSettingsScreen_EnrollBiometrics_Sample()
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun AppSettingsScreen_EnrollBiometrics_Preview_Dark() {
TangemTheme(isDark = true) {
AppSettingsScreen_EnrollBiometrics_Sample()
}
}
// endregion Preview

View file

@ -1,10 +1,11 @@
package com.tangem.tap.features.details.ui.appsettings
import com.tangem.core.ui.models.EnrollBiometricsDialog
import com.tangem.tap.features.details.redux.PrivacySetting
data class AppSettingsScreenState(
val settings: Map<PrivacySetting, Boolean>,
val enrollBiometricsDialog: EnrollBiometricsDialog?,
val onSettingToggled: (PrivacySetting, Boolean) -> Unit,
val settings: Map<PrivacySetting, Boolean> = emptyMap(),
val showEnrollBiometricsCard: Boolean = false,
val isTogglesEnabled: Boolean = true,
val onSettingToggled: (PrivacySetting, Boolean) -> Unit = { _, _ -> /* no-op */ },
val onEnrollBiometrics: () -> Unit = { /* no-op */ },
)

View file

@ -1,6 +1,5 @@
package com.tangem.tap.features.details.ui.appsettings
import com.tangem.core.ui.models.EnrollBiometricsDialog
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.details.redux.DetailsAction
@ -20,8 +19,14 @@ class AppSettingsViewModel(private val store: Store<AppState>) {
PrivacySetting.SaveWallets to state.saveWallets,
PrivacySetting.SaveAccessCode to state.saveAccessCodes,
),
enrollBiometricsDialog = if (state.needEnrollBiometrics) createEnrollBiometricsDialog() else null,
onSettingToggled = { privacySetting, enabled -> onSettingsToggled(privacySetting, enabled) },
showEnrollBiometricsCard = state.needEnrollBiometrics,
isTogglesEnabled = !state.needEnrollBiometrics,
onSettingToggled = { privacySetting, enabled ->
onSettingsToggled(privacySetting, enabled)
},
onEnrollBiometrics = {
store.dispatchOnMain(DetailsAction.AppSettings.EnrollBiometrics)
},
)
}
@ -29,12 +34,11 @@ class AppSettingsViewModel(private val store: Store<AppState>) {
store.dispatch(DetailsAction.AppSettings.SwitchPrivacySetting(enable = enable, setting = setting))
}
private fun createEnrollBiometricsDialog() = EnrollBiometricsDialog(
onCancel = {
store.dispatchOnMain(DetailsAction.AppSettings.EnrollBiometrics.Cancel)
},
onEnroll = {
store.dispatchOnMain(DetailsAction.AppSettings.EnrollBiometrics.Enroll)
},
)
fun checkBiometricsStatus() {
store.dispatch(DetailsAction.AppSettings.CheckBiometricsStatus(awaitStatusChange = false))
}
fun refreshBiometricsStatus() {
store.dispatch(DetailsAction.AppSettings.CheckBiometricsStatus(awaitStatusChange = true))
}
}

View file

@ -0,0 +1,92 @@
package com.tangem.tap.features.details.ui.appsettings.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SpacerH4
import com.tangem.core.ui.components.SpacerW16
import com.tangem.core.ui.res.TangemTheme
import com.tangem.wallet.R
@OptIn(ExperimentalMaterialApi::class)
@Composable
internal fun EnrollBiometricsCard(
modifier: Modifier = Modifier,
onClick: () -> Unit,
) {
Surface(
modifier = modifier,
color = TangemTheme.colors.background.primary,
shape = TangemTheme.shapes.roundedCornersLarge,
onClick = onClick,
) {
Row(
modifier = Modifier.padding(all = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
) {
Icon(
painter = painterResource(id = R.drawable.ic_alert_circle_24),
tint = TangemTheme.colors.icon.attention,
contentDescription = null,
)
SpacerW16()
Column {
Text(
text = stringResource(id = R.string.app_settings_enable_biometrics_title),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
SpacerH4()
Text(
text = stringResource(id = R.string.app_settings_enable_biometrics_description),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
)
}
}
}
}
// region Preview
@Composable
private fun EnrollBiometricsCardSample(
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.background(TangemTheme.colors.background.secondary),
) {
EnrollBiometricsCard(onClick = {})
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun EnrollBiometricsCardPreview_Light() {
TangemTheme {
EnrollBiometricsCardSample()
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun EnrollBiometricsCardPreview_Dark() {
TangemTheme(isDark = true) {
EnrollBiometricsCardSample()
}
}
// endregion Preview

View file

@ -0,0 +1,101 @@
package com.tangem.tap.features.details.ui.appsettings.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.AlertDialog
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.TextButton
import com.tangem.core.ui.components.WarningTextButton
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.details.redux.PrivacySetting
import com.tangem.wallet.R
@Composable
internal fun SettingsAlertDialog(
element: PrivacySetting,
onDialogStateChange: (PrivacySetting?) -> Unit,
onSettingToggled: (PrivacySetting, Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
val text = when (element) {
PrivacySetting.SaveWallets -> R.string.app_settings_off_saved_wallet_alert_message
PrivacySetting.SaveAccessCode -> R.string.app_settings_off_saved_access_code_alert_message
}
AlertDialog(
onDismissRequest = { onDialogStateChange(null) },
confirmButton = {
TextButton(
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
text = stringResource(id = R.string.common_cancel),
onClick = {
onDialogStateChange(null)
},
)
},
dismissButton = {
WarningTextButton(
text = stringResource(id = R.string.common_delete),
onClick = {
onDialogStateChange(null)
onSettingToggled(element, false)
},
)
},
title = {
Text(
text = stringResource(id = R.string.common_attention),
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.h2,
)
},
text = {
Text(
text = stringResource(id = text),
color = TangemTheme.colors.text.secondary,
style = TangemTheme.typography.body2,
)
},
shape = TangemTheme.shapes.roundedCornersLarge,
modifier = modifier,
)
}
// region Preview
@Composable
private fun SettingsAlertDialogSample(
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.background(TangemTheme.colors.background.primary),
) {
SettingsAlertDialog(
element = PrivacySetting.SaveAccessCode,
onDialogStateChange = {},
onSettingToggled = { _, _ -> },
)
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun SettingsAlertDialogPreview_Light() {
TangemTheme {
SettingsAlertDialogSample()
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun SettingsAlertDialogPreview_Dark() {
TangemTheme(isDark = true) {
SettingsAlertDialogSample()
}
}
// endregion Preview

View file

@ -52,7 +52,7 @@ internal class SaveWalletMiddleware {
}
private fun saveWalletIfBiometricsEnrolled(state: SaveWalletState) {
if (tangemSdkManager.canEnrollBiometrics) {
if (tangemSdkManager.needEnrollBiometrics) {
store.dispatchOnMain(SaveWalletAction.EnrollBiometrics)
} else {
saveWallet(state)

View file

@ -224,4 +224,6 @@
<string name="user_wallet_list_editing_count">%d selected</string>
<string name="common_biometric_authentication">biometric authentication</string>
<string name="common_biometrics">biometrics</string>
<string name="app_settings_enable_biometrics_title">Enable biometric authentication</string>
<string name="app_settings_enable_biometrics_description">Go to settings to enable biometric authentication in the Tangem App</string>
</resources>

View file

@ -224,4 +224,6 @@
<string name="user_wallet_list_editing_count">%d selected</string>
<string name="common_biometric_authentication">biometric authentication</string>
<string name="common_biometrics">biometrics</string>
<string name="app_settings_enable_biometrics_title">Enable biometric authentication</string>
<string name="app_settings_enable_biometrics_description">Go to settings to enable biometric authentication in the Tangem App</string>
</resources>

View file

@ -224,4 +224,6 @@
<string name="user_wallet_list_editing_count">%d selected</string>
<string name="common_biometric_authentication">biometric authentication</string>
<string name="common_biometrics">biometrics</string>
<string name="app_settings_enable_biometrics_title">Enable biometric authentication</string>
<string name="app_settings_enable_biometrics_description">Go to settings to enable biometric authentication in the Tangem App</string>
</resources>

View file

@ -224,4 +224,6 @@
<string name="user_wallet_list_editing_count">%d выбрано</string>
<string name="common_biometric_authentication">биометрическую аутентификацию</string>
<string name="common_biometrics">биометрией</string>
<string name="app_settings_enable_biometrics_title">Включите биометрическую аутентификацию</string>
<string name="app_settings_enable_biometrics_description">Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem App</string>
</resources>

View file

@ -224,4 +224,6 @@
<string name="user_wallet_list_editing_count">%d selected</string>
<string name="common_biometric_authentication">biometric authentication</string>
<string name="common_biometrics">biometrics</string>
<string name="app_settings_enable_biometrics_title">Enable biometric authentication</string>
<string name="app_settings_enable_biometrics_description">Go to settings to enable biometric authentication in the Tangem App</string>
</resources>

View file

@ -59,6 +59,7 @@ data class TangemDimens internal constructor(
val spacing12: Dp = 12.dp,
val spacing14: Dp = 14.dp,
val spacing16: Dp = 16.dp,
val spacing20: Dp = 20.dp,
val spacing24: Dp = 24.dp,
val spacing32: Dp = 32.dp,
val spacing34: Dp = 34.dp,

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M13,13H11V7H13M13,17H11V15H13M12,2C10.687,2 9.386,2.259 8.173,2.761C6.96,3.264 5.858,4 4.929,4.929C3.054,6.804 2,9.348 2,12C2,14.652 3.054,17.196 4.929,19.071C5.858,20 6.96,20.736 8.173,21.239C9.386,21.741 10.687,22 12,22C14.652,22 17.196,20.946 19.071,19.071C20.946,17.196 22,14.652 22,12C22,10.687 21.741,9.386 21.239,8.173C20.736,6.96 20,5.858 19.071,4.929C18.142,4 17.04,3.264 15.827,2.761C14.614,2.259 13.313,2 12,2Z" />
</vector>