Updated on 2026-08-14

This commit is contained in:
Tangem 2022-03-02 18:52:05 +00:00
commit 86d788ed42
27 changed files with 298 additions and 104 deletions

View file

@ -80,8 +80,8 @@ dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'com.tangem:blockchain:develop-64'
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-132'
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-132'
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-134'
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-134'
// WebView
implementation "androidx.browser:browser:1.3.0"

@ -1 +1 @@
Subproject commit 180252680e80b88f8fec9add783207d41d30e4e3
Subproject commit 44ccf4e7766e51e869eabe8bc68026b5b7edd237

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.analytics
import com.shopify.buy3.Storefront
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
import com.tangem.tap.common.extensions.filterNotNull
@ -13,6 +14,11 @@ abstract class AnalyticsHandler {
params: Map<String, String> = emptyMap()
)
abstract fun triggerEvent(
event: String,
params: Map<String, String>
)
abstract fun logCardSdkError(
error: TangemSdkError,
actionToLog: Analytics.ActionToLog,
@ -71,6 +77,13 @@ abstract class AnalyticsHandler {
)
}
}
}
fun logShopifyOrder(order: Storefront.Order) {
triggerEvent(getOrderEvent(), getOrderParams(order))
}
protected abstract fun getOrderEvent(): String
protected abstract fun getOrderParams(order: Storefront.Order): Map<String, String>
}

View file

@ -1,7 +1,10 @@
package com.tangem.tap.common.analytics
import android.app.Application
import com.appsflyer.AFInAppEventParameterName
import com.appsflyer.AFInAppEventType
import com.appsflyer.AppsFlyerLib
import com.shopify.buy3.Storefront
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
@ -30,4 +33,30 @@ class AppsFlyerAnalyticsHandler(val context: Application): AnalyticsHandler() {
//TODO
}
override fun getOrderEvent(): String = AFInAppEventType.PURCHASE
override fun getOrderParams(order: Storefront.Order): Map<String, String> {
val sku = order.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: "unknown"
val discountCode =
(order.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code
val discountParams = if (discountCode != null ){
mapOf(AFInAppEventParameterName.COUPON_CODE to discountCode)
} else {
mapOf()
}
return mapOf(
AFInAppEventParameterName.CONTENT_ID to sku,
AFInAppEventParameterName.REVENUE to order.totalPriceV2.amount,
AFInAppEventParameterName.CURRENCY to order.currencyCode.name
) + discountParams
}
override fun triggerEvent(event: String, params: Map<String, String>) {
AppsFlyerLib.getInstance().logEvent(context, event, params)
}
}

View file

@ -2,9 +2,11 @@ package com.tangem.tap.common.analytics
import android.os.Bundle
import androidx.core.os.bundleOf
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.ktx.Firebase
import com.shopify.buy3.Storefront
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
@ -57,4 +59,30 @@ object FirebaseAnalyticsHandler : AnalyticsHandler() {
params.forEach { (key, value) -> FirebaseCrashlytics.getInstance().setCustomKey(key, value) }
FirebaseCrashlytics.getInstance().recordException(error)
}
override fun getOrderEvent(): String = FirebaseAnalytics.Event.PURCHASE
override fun getOrderParams(order: Storefront.Order): Map<String, String> {
val sku = order.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: "unknown"
val discountCode =
(order.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code
val discountParams = if (discountCode != null ){
mapOf(FirebaseAnalytics.Param.DISCOUNT to discountCode)
} else {
mapOf()
}
return mapOf(
FirebaseAnalytics.Param.ITEM_ID to sku,
FirebaseAnalytics.Param.VALUE to order.totalPriceV2.amount,
FirebaseAnalytics.Param.CURRENCY to order.currencyCode.name
) + discountParams
}
override fun triggerEvent(event: String, params: Map<String, String>) {
Firebase.analytics.logEvent(event, params.toBundle())
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.tap.common.analytics
import android.app.Application
import com.shopify.buy3.Storefront
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
@ -15,6 +16,10 @@ class GlobalAnalyticsHandler(val analyticsHandlers: List<AnalyticsHandler>) :
analyticsHandlers.forEach { it.triggerEvent(event, card, blockchain) }
}
override fun triggerEvent(event: String, params: Map<String, String>) {
analyticsHandlers.forEach { it.triggerEvent(event, params) }
}
override fun logCardSdkError(
error: TangemSdkError,
actionToLog: Analytics.ActionToLog,
@ -28,6 +33,14 @@ class GlobalAnalyticsHandler(val analyticsHandlers: List<AnalyticsHandler>) :
analyticsHandlers.forEach { it.logError(error, params) }
}
override fun getOrderEvent(): String {
return ""
}
override fun getOrderParams(order: Storefront.Order): Map<String, String> {
return emptyMap()
}
companion object {
fun createDefaultAnalyticHandlers(context: Application): GlobalAnalyticsHandler {
return GlobalAnalyticsHandler(

View file

@ -4,9 +4,11 @@ import android.app.Application
import android.content.Intent
import com.google.android.gms.wallet.PaymentData
import com.shopify.buy3.Storefront
import com.tangem.tap.common.analytics.AnalyticsHandler
import com.tangem.tap.common.shop.data.ProductType
import com.tangem.tap.common.shop.data.TangemProduct
import com.tangem.tap.common.shop.data.TotalSum
import com.tangem.tap.common.shop.shopify.ShopifyService
import com.tangem.tap.common.shop.shopify.ShopifyShop
import com.tangem.tap.common.shop.shopify.data.CheckoutItem
import kotlinx.coroutines.async
@ -197,6 +199,18 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
return checkouts[productType]!!.webUrl
}
suspend fun waitForCheckout(
productType: ProductType,
analyticsHandler: AnalyticsHandler?
) {
val result = shopifyService.checkout(true, checkouts[productType]!!.id)
result.onSuccess {
if (it.order != null) {
analyticsHandler?.logShopifyOrder(order = it.order)
}
}
}
companion object {
const val TANGEM_WALLET_2_CARDS_SKU = "TG115x2"
const val TANGEM_WALLET_3_CARDS_SKU = "TG115x3"

View file

@ -1,4 +1,4 @@
package com.tangem.tap.common.shop
package com.tangem.tap.common.shop.shopify
import android.app.Application
import com.shopify.buy3.GraphCallResult
@ -7,7 +7,6 @@ import com.shopify.buy3.RetryHandler
import com.shopify.buy3.Storefront.*
import com.shopify.graphql.support.ID
import com.shopify.graphql.support.Input
import com.tangem.tap.common.shop.shopify.ShopifyShop
import com.tangem.tap.common.shop.shopify.data.CheckoutItem
import com.tangem.tap.common.shop.shopify.data.checkoutFieldsFragment
import com.tangem.tap.common.shop.shopify.data.collectionFieldsFragment
@ -85,7 +84,7 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
when (result) {
is GraphCallResult.Success -> {
val checkout = result.response.data?.node as? Checkout
checkout == null
checkout?.order == null
}
is GraphCallResult.Failure -> false
}

View file

@ -63,6 +63,8 @@ object TapWorkarounds {
"AB04" to Blockchain.Dogecoin,
"AB05" to Blockchain.BSC,
"AB06" to Blockchain.XRP,
"AB07" to Blockchain.Bitcoin,
"AB08" to Blockchain.Ethereum,
)
}

View file

@ -145,7 +145,20 @@ private class CreateWalletTangemWallet : ProductCommandProcessor<CreateProductWa
when (result) {
is CompletionResult.Success -> {
primaryCard = result.data
deriveKeys(createWalletResponse, session, callback)
when {
card.settings.isHDWalletAllowed -> {
deriveKeys(createWalletResponse, session, callback)
}
else -> {
callback(
CompletionResult.Success(
CreateProductWalletTaskResponse(
card = session.environment.card!!, primaryCard = primaryCard
)
)
)
}
}
}
is CompletionResult.Failure -> {
callback(CompletionResult.Failure(result.error))

View file

@ -1,6 +1,5 @@
package com.tangem.tap.domain.tasks.product
import com.tangem.*
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.common.CompletionResult
@ -241,7 +240,7 @@ private class ScanWalletProcessor(
callback: (result: CompletionResult<ScanResponse>) -> Unit
) {
val derivations = collectDerivations(card)
if (derivations.isEmpty()) {
if (derivations.isEmpty() || !card.settings.isHDWalletAllowed) {
callback(
CompletionResult.Success(
ScanResponse(

View file

@ -0,0 +1,45 @@
package com.tangem.tap.features.home.compose
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.ExperimentalUnitApi
@OptIn(ExperimentalUnitApi::class)
@Composable
fun AutoSizeText(
text: String,
textStyle: TextStyle,
modifier: Modifier = Modifier
) {
val scaledTextStyle = remember { mutableStateOf(textStyle) }
val readyToDraw = remember { mutableStateOf(false) }
val onTextLayout: (TextLayoutResult) -> Unit = { result ->
if (result.didOverflowWidth) {
val fontSize = result.size.width / result.multiParagraph.width
scaledTextStyle.value =
// scaledTextStyle.value.copy(fontSize = TextUnit(fontSize, TextUnitType.Sp))
scaledTextStyle.value.copy(fontSize = scaledTextStyle.value.fontSize * 0.9)
} else {
readyToDraw.value = true
}
}
Text(
text = text,
modifier = modifier.drawWithContent {
if (readyToDraw.value) {
drawContent()
}
},
softWrap = false,
onTextLayout = onTextLayout,
style = scaledTextStyle.value,
)
}

View file

@ -9,7 +9,6 @@ import androidx.compose.animation.scaleIn
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
@ -21,6 +20,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
@ -88,32 +88,27 @@ fun FirstStoriesContent(
StartingScreenState.MEET_TANGEM -> R.string.story_meet_title
}
if (screenState.value != StartingScreenState.MEET_TANGEM) {
Text(
text = text?.let { stringResource(text) } ?: "",
fontSize = 60.sp,
fontWeight = FontWeight.SemiBold,
modifier = Modifier
.padding(start = 40.dp, end = 40.dp)
.alpha(if (screenState.value == StartingScreenState.SHOW_CARD) 0f else 1f),
color = Color.White,
textAlign = TextAlign.Center,
val style = TextStyle(
fontSize = 60.sp,
fontWeight = FontWeight.SemiBold,
color = Color.White,
textAlign = TextAlign.Center,
)
)
if (screenState.value != StartingScreenState.MEET_TANGEM) {
AutoSizeText(text = text?.let { stringResource(text) } ?: "", textStyle = style,
modifier = Modifier
.padding(start = 40.dp, end = 40.dp)
.alpha(if (screenState.value == StartingScreenState.SHOW_CARD) 0f else 1f),)
}
AnimatedVisibility(
visible = screenState.value == StartingScreenState.MEET_TANGEM,
enter = slideInVertically() { it / 2 }
) {
Text(
text = text?.let { stringResource(text) } ?: "",
fontSize = 60.sp,
fontWeight = FontWeight.SemiBold,
AutoSizeText(text = text?.let { stringResource(text) } ?: "", textStyle = style,
modifier = Modifier
.padding(start = 40.dp, end = 40.dp),
color = Color.White,
textAlign = TextAlign.Center
.padding(start = 40.dp, end = 40.dp)
)
}

View file

@ -9,10 +9,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.tangem.wallet.R
import androidx.compose.foundation.layout.fillMaxWidth as fillMaxWidth1
@Composable
fun HomeButtons(
@ -25,13 +25,13 @@ fun HomeButtons(
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = modifier.fillMaxWidth1()
modifier = modifier.fillMaxWidth()
) {
Spacer(modifier = Modifier.size(16.dp))
Button(
modifier = Modifier
.weight(1f)
.height(42.dp),
.height(42.dp)
,
onClick = onScanButtonClick,
colors = ButtonDefaults.textButtonColors(
backgroundColor = if (isDarkBackground) darkColorBackground else Color.White,
@ -41,7 +41,8 @@ fun HomeButtons(
Text(
text = stringResource(id = R.string.home_button_scan),
fontWeight = FontWeight.Medium,
fontSize = 16.sp
fontSize = 16.sp,
textAlign = TextAlign.Center
)
}
Spacer(modifier = Modifier.size(8.dp))
@ -58,9 +59,9 @@ fun HomeButtons(
Text(
text = stringResource(id = R.string.home_button_order),
fontWeight = FontWeight.Medium,
fontSize = 16.sp
fontSize = 16.sp,
textAlign = TextAlign.Center
)
}
Spacer(modifier = Modifier.size(16.dp))
}
}

View file

@ -54,28 +54,46 @@ fun StoriesScreen(
Modifier
.fillMaxSize()
.background(Color(0xFF090E13))
.pointerInput(Unit) {
val maxWidth = this.size.width
detectTapGestures(
onPress = {
val pressStartTime = System.currentTimeMillis()
isPressed.value = true
this.tryAwaitRelease()
val pressEndTime = System.currentTimeMillis()
val totalPressTime = pressEndTime - pressStartTime
if (totalPressTime < 200) {
val isTapOnRightTwoTiers = (it.x > (maxWidth / 2))
if (isTapOnRightTwoTiers) {
goToNextScreen()
} else {
goToPreviousScreen()
}
}
isPressed.value = false
},
)
}
) {
Row(
Modifier.fillMaxSize()
) {
Box(
Modifier
.weight(1f)
.fillMaxHeight()
.pointerInput(isPressed) {
detectTapGestures(
onPress = {
val pressStartTime = System.currentTimeMillis()
isPressed.value = true
this.tryAwaitRelease()
val pressEndTime = System.currentTimeMillis()
val totalPressTime = pressEndTime - pressStartTime
if (totalPressTime < 200) goToPreviousScreen()
isPressed.value = false
},
)
}
)
Box(
Modifier.weight(1f)
.fillMaxHeight()
.pointerInput(isPressed) {
detectTapGestures(
onPress = {
val pressStartTime = System.currentTimeMillis()
isPressed.value = true
this.tryAwaitRelease()
val pressEndTime = System.currentTimeMillis()
val totalPressTime = pressEndTime - pressStartTime
if (totalPressTime < 200) goToNextScreen()
isPressed.value = false
},
)
}
)
}
if (!isDarkBackground) {
Image(
painter = painterResource(id = R.drawable.ic_overlay),
@ -87,9 +105,8 @@ fun StoriesScreen(
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier =
Modifier
.fillMaxSize()
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
Spacer(modifier = Modifier.size(24.dp))
StoriesProgressBar(
@ -111,12 +128,6 @@ fun StoriesScreen(
.align(Alignment.Start),
colorFilter = if (isDarkBackground) null else ColorFilter.tint(Color.Black)
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
when (currentStep.value) {
1 -> FirstStoriesContent(pause, stepDuration) { hideContent.value = it }
2 -> StoriesRevolutionaryWallet()
@ -130,7 +141,8 @@ fun StoriesScreen(
isDarkBackground = isDarkBackground,
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(37.dp),
.padding(37.dp)
.fillMaxWidth(),
onScanButtonClick = onScanButtonClick,
onShopButtonClick = onShopButtonClick
)

View file

@ -61,7 +61,7 @@ fun StoriesGeneralContent(
Image(
painter = painterResource(id = imageSource),
contentDescription = null,
contentScale = ContentScale.FillWidth,
contentScale = if(isDarkBackground) ContentScale.Inside else ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth()
)
}

View file

@ -332,8 +332,8 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)
}
} else {
btnAlternativeAction.setText(R.string.onboarding_button_receive_crypto)
btnAlternativeAction.setOnClickListener {
btnMainAction.setText(R.string.onboarding_button_receive_crypto)
btnMainAction.setOnClickListener {
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)
}

View file

@ -124,10 +124,15 @@ private fun handleWalletAction(action: Action) {
} else {
null
}
BackupAction.IntroduceBackup(url)
if (walletState.backupState.backupStep == BackupStep.InitBackup ||
walletState.backupState.backupStep == BackupStep.Finished) {
BackupAction.IntroduceBackup(url)
} else {
null
}
}
}
store.dispatch(newAction)
newAction?.let { store.dispatch(it) }
}
OnboardingWalletAction.OnBackPressed -> {
when (walletState.backupState.backupStep) {

View file

@ -204,7 +204,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
viewPagerBackupInfo.hide()
tabLayoutBackupInfo.hide()
imvSecondBackupCard.show()
imvFirstBackupCard.show()
imvSecondBackupCard.show()
}
@ -236,12 +236,15 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
tvHeader.text = getText(R.string.onboarding_title_one_backup_card)
tvBody.text = getText(R.string.onboarding_subtitle_one_backup_card)
cardsWidget.toFan(false)
cardsWidget.getFirstBackupCardView().animate().alpha(1f).setDuration(400)
cardsWidget.getSecondBackupCardView().alpha = 0.2f
}
2 -> {
tvHeader.text = getText(R.string.onboarding_title_two_backup_cards)
tvBody.text = getText(R.string.onboarding_subtitle_two_backup_cards)
cardsWidget.toFan(false)
cardsWidget.getFirstBackupCardView().alpha = 1f
cardsWidget.getSecondBackupCardView().animate().alpha(1f).setDuration(400)
}

View file

@ -230,7 +230,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
}
private fun handleFeeState(fg: SendFragment, state: FeeState) = with(fg.binding.clNetworkFee) {
chipGroup.fitChipsByGroupWidth()
// chipGroup.fitChipsByGroupWidth()
fg.view?.findViewById<ViewGroup>(R.id.clNetworkFee)?.show(state.mainLayoutIsVisible)
flExpandCollapse.imvExpandCollapse.rotation = if (state.controlsLayoutIsVisible) 0f else 180f
llFeeControlsContainer.show(state.controlsLayoutIsVisible)

View file

@ -34,5 +34,7 @@ sealed class ShopAction : Action {
data class SelectProduct(val productType: ProductType) : ShopAction()
object FinishSuccessfulOrder : ShopAction()
object ResetState : ShopAction()
}

View file

@ -110,7 +110,18 @@ private fun handle(action: Action) {
}
ShopAction.StartWebCheckout -> {
store.dispatchOpenUrl(shopService.getCheckoutUrl(shopState.selectedProduct))
store.dispatch(ShopAction.FinishSuccessfulOrder)
}
is ShopAction.FinishSuccessfulOrder -> {
scope.launch {
shopService.waitForCheckout(
productType = shopState.selectedProduct,
analyticsHandler = store.state.globalState.analyticsHandlers
)
}
}
}
}

View file

@ -63,6 +63,7 @@ private fun internalReduce(action: Action, state: ShopState): ShopState {
ShopAction.BuyWithGooglePay.UserCancelled -> {
state
}
ShopAction.FinishSuccessfulOrder -> state
ShopAction.ResetState -> ShopState()
}
}

View file

@ -137,7 +137,7 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber<
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
imvThird?.show(show)
imvThird.show(show)
}
})
}

View file

@ -53,7 +53,8 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="36dp"
android:layout_marginTop="24dp"
android:paddingTop="16dp"
android:paddingBottom="44dp"
android:layout_marginEnd="36dp"
android:layout_weight="1"
app:layout_constraintBottom_toTopOf="@+id/tv_header"
@ -109,7 +110,6 @@
android:id="@+id/tv_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="44dp"
android:layout_marginBottom="14dp"
android:fontFamily="sans-serif-medium"
android:text="@string/shop_one_wallet"
@ -343,7 +343,7 @@
android:layout_height="48sp"
android:layout_marginStart="14dp"
android:layout_marginEnd="14dp"
android:layout_marginBottom="70dp"
android:layout_marginBottom="20dp"
android:text="@string/shop_buy_now"
android:textAlignment="center"
android:textSize="16sp" />

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/clNetworkFee"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -35,39 +36,47 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/flExpandCollapse">
<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup"
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:translationY="-8dp"
app:selectionRequired="true"
app:singleLine="true"
app:singleSelection="true">
android:clipChildren="false">
<com.google.android.material.chip.Chip
android:id="@+id/chipLow"
style="@style/TapChip"
<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_fee_picker_low" />
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:translationY="-8dp"
app:selectionRequired="true"
app:singleLine="true"
app:singleSelection="true">
<com.google.android.material.chip.Chip
android:id="@+id/chipNormal"
style="@style/TapChip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_fee_picker_normal" />
<com.google.android.material.chip.Chip
android:id="@+id/chipLow"
style="@style/TapChip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_fee_picker_low" />
<com.google.android.material.chip.Chip
android:id="@+id/chipPriority"
style="@style/TapChip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_fee_picker_priority" />
<com.google.android.material.chip.Chip
android:id="@+id/chipNormal"
style="@style/TapChip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_fee_picker_normal"
tools:text="Нормальный" />
</com.google.android.material.chip.ChipGroup>
<com.google.android.material.chip.Chip
android:id="@+id/chipPriority"
style="@style/TapChip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_fee_picker_priority"
tools:text="Приоритетный" />
</com.google.android.material.chip.ChipGroup>
</HorizontalScrollView>
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/swIncludeFee"

View file

@ -27,7 +27,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintGuide_percent="0.37" />
app:layout_constraintGuide_percent="0.5" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_confirm_long"