Updated on 2026-08-14

This commit is contained in:
Tangem 2022-02-24 13:43:33 +03:00
parent 694dd1cf18
commit 56a123f485
39 changed files with 1485 additions and 1291 deletions

View file

@ -1,6 +1,5 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
@ -15,11 +14,9 @@ android {
targetSdkVersion 31
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
androidExtensions {
experimental = true
}
buildFeatures {
compose true
viewBinding true
}
buildTypes {
release {
@ -150,6 +147,8 @@ dependencies {
implementation 'androidx.compose.ui:ui-tooling:1.1.0'
implementation "com.google.accompanist:accompanist-appcompat-theme:0.23.0"
implementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.6'
testImplementation 'junit:junit:4.13.2'
testImplementation "com.google.truth:truth:1.1.3"
androidTestImplementation 'androidx.test.ext:junit:1.1.3'

View file

@ -6,6 +6,7 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.snackbar.Snackbar
import com.tangem.TangemSdk
import com.tangem.operations.backup.BackupService
@ -25,7 +26,7 @@ import com.tangem.tap.domain.TangemSdkManager
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.shop.redux.ShopAction
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_main.*
import com.tangem.wallet.databinding.ActivityMainBinding
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -63,6 +64,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler {
private var snackbar: Snackbar? = null
private val dialogManager = DialogManager()
private val intentHandler = IntentHandler()
private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::bind)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -102,7 +104,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler {
override fun onResume() {
super.onResume()
notificationsHandler = NotificationsHandler(fragment_container)
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0
val isScannedBefore = store.state.globalState.scanResponse != null
@ -139,7 +141,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler {
if (snackbar != null) return
snackbar = Snackbar.make(
fragment_container, getString(text), Snackbar.LENGTH_INDEFINITE
binding.fragmentContainer, getString(text), Snackbar.LENGTH_INDEFINITE
)
if (buttonTitle != null && action != null) {
snackbar?.setAction(getString(buttonTitle), action)

View file

@ -37,6 +37,11 @@ fun Context.getColorCompat(@ColorRes colorRes: Int): Int {
return ContextCompat.getColor(this, colorRes)
}
@ColorInt
fun View.getColor(@ColorRes colorRes: Int): Int {
return ContextCompat.getColor(context, colorRes)
}
fun View.getString(@StringRes id: Int): String {
return context.getString(id)
}
@ -165,4 +170,8 @@ fun Context.safeStartActivity(
} finally {
finally?.invoke()
}
}
fun View.getString(resId: Int, vararg formatArgs: Any?): String {
return context.getString(resId, formatArgs)
}

View file

@ -5,15 +5,18 @@ import android.view.View
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidgetState
import com.tangem.tap.domain.twins.TwinsCardWidget
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.test_leapfrog_fragment.*
import com.tangem.wallet.databinding.TestLeapfrogFragmentBinding
class TestLeapfrogFragment : Fragment(R.layout.test_leapfrog_fragment) {
private lateinit var twinsCardWidget: TwinsCardWidget
private val binding: TestLeapfrogFragmentBinding by viewBinding(TestLeapfrogFragmentBinding::bind)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -28,30 +31,29 @@ class TestLeapfrogFragment : Fragment(R.layout.test_leapfrog_fragment) {
val leapfrog = LeapfrogWidget(leapfrogContainer)
twinsCardWidget = TwinsCardWidget(leapfrog) { 200f }
btn_twin_welcome.setOnClickListener {
binding.btnTwinWelcome.setOnClickListener {
twinsCardWidget.toWelcome()
}
btn_twin_to_leapfrog.setOnClickListener {
binding.btnTwinToLeapfrog.setOnClickListener {
twinsCardWidget.toLeapfrog()
}
btn_twin_activate.setOnClickListener {
binding.btnTwinActivate.setOnClickListener {
twinsCardWidget.toActivate()
}
btn_lp_init.setOnClickListener {
binding.btnLpInit.setOnClickListener {
leapfrog.initViews()
}
btn_lp_unfold.setOnClickListener {
binding.btnLpUnfold.setOnClickListener {
leapfrog.unfold()
}
btn_lp_fold.setOnClickListener {
binding.btnLpFold.setOnClickListener {
leapfrog.fold()
}
btn_lp_leap.setOnClickListener {
binding.btnLpLeap.setOnClickListener {
leapfrog.leap()
}
btn_lp_leap_back.setOnClickListener {
binding.btnLpLeapBack.setOnClickListener {
leapfrog.leapBack()
}
}

View file

@ -5,6 +5,7 @@ import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.tap.common.extensions.getDrawable
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.details.redux.ConfirmScreenState
@ -12,12 +13,14 @@ 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 kotlinx.android.synthetic.main.fragment_details_confirm.*
import com.tangem.wallet.databinding.FragmentDetailsConfirmBinding
import org.rekotlin.StoreSubscriber
class DetailsConfirmFragment : Fragment(R.layout.fragment_details_confirm),
StoreSubscriber<DetailsState> {
private val binding: FragmentDetailsConfirmBinding by viewBinding(FragmentDetailsConfirmBinding::bind)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
@ -47,38 +50,39 @@ class DetailsConfirmFragment : Fragment(R.layout.fragment_details_confirm),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setNavigationOnClickListener {
binding.toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo())
}
}
override fun newState(state: DetailsState) {
if (activity == null) return
if (activity == null || view == null) return
setState(state)
}
private fun setState(state: DetailsState) = with(binding) {
when (state.confirmScreenState) {
ConfirmScreenState.EraseWallet -> {
toolbar.title = getString(R.string.details_row_title_reset_factory_settings)
tv_warning_description.text = getString(R.string.details_row_title_reset_factory_settings_warning)
btn_confirm.text = getString(R.string.details_row_title_reset_factory_settings)
btn_confirm.setCompoundDrawablesRelativeWithIntrinsicBounds(
null, null, getDrawable(R.drawable.ic_send), null
tvWarningDescription.text = getString(R.string.details_row_title_reset_factory_settings_warning)
btnConfirm.text = getString(R.string.details_row_title_reset_factory_settings)
btnConfirm.setCompoundDrawablesRelativeWithIntrinsicBounds(
null, null, getDrawable(R.drawable.ic_send), null
)
btn_confirm.setOnClickListener { store.dispatch(DetailsAction.ResetToFactory.Confirm) }
btnConfirm.setOnClickListener { store.dispatch(DetailsAction.ResetToFactory.Confirm) }
}
ConfirmScreenState.LongTap, ConfirmScreenState.AccessCode,
ConfirmScreenState.PassCode -> {
toolbar.title = getString(R.string.details_manage_security_title)
tv_warning_description.text = getString(R.string.details_security_management_warning)
btn_confirm.text = getString(R.string.common_save_changes)
btn_confirm.setCompoundDrawablesRelativeWithIntrinsicBounds(
null, null, getDrawable(R.drawable.ic_save), null
tvWarningDescription.text = getString(R.string.details_security_management_warning)
btnConfirm.text = getString(R.string.common_save_changes)
btnConfirm.setCompoundDrawablesRelativeWithIntrinsicBounds(
null, null, getDrawable(R.drawable.ic_save), null
)
btn_confirm.setOnClickListener { store.dispatch(DetailsAction.ManageSecurity.SaveChanges) }
btnConfirm.setOnClickListener { store.dispatch(DetailsAction.ManageSecurity.SaveChanges) }
}
}
}
}

View file

@ -6,6 +6,7 @@ import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
@ -18,13 +19,13 @@ import com.tangem.tap.features.details.redux.SecurityOption
import com.tangem.tap.features.feedback.FeedbackEmail
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_details.*
import kotlinx.android.synthetic.main.fragment_wallet.toolbar
import com.tangem.wallet.databinding.FragmentDetailsBinding
import org.rekotlin.StoreSubscriber
class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<DetailsState> {
private var currencySelectionDialog = CurrencySelectionDialog()
private val binding: FragmentDetailsBinding by viewBinding(FragmentDetailsBinding::bind)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -55,15 +56,18 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setNavigationOnClickListener {
binding.toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo())
}
}
override fun newState(state: DetailsState) {
if (activity == null) return
if (activity == null || view == null) return
setState(state)
}
private fun setState(state: DetailsState) = with (binding){
if (state.cardInfo != null) {
val cardId = if (state.isTangemTwins) {
@ -71,61 +75,61 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
} else {
state.cardInfo.cardId
}
tv_card_id.text = cardId
tv_issuer.text = state.cardInfo.issuer
tv_signed_hashes.text = getString(
tvCardId.text = cardId
tvIssuer.text = state.cardInfo.issuer
tvSignedHashes.text = getString(
R.string.details_row_subtitle_signed_hashes_format,
state.cardInfo.signedHashes.toString()
)
}
tv_signed_hashes.show(!state.isTangemTwins)
tv_signed_hashes_title.show(!state.isTangemTwins)
tvSignedHashes.show(!state.isTangemTwins)
tvSignedHashesTitle.show(!state.isTangemTwins)
tv_disclaimer.setOnClickListener { store.dispatch(DetailsAction.ShowDisclaimer) }
tvDisclaimer.setOnClickListener { store.dispatch(DetailsAction.ShowDisclaimer) }
tv_card_tou.show(state.cardTermsOfUseUrl != null)
tv_card_tou.setOnClickListener {
tvCardTou.show(state.cardTermsOfUseUrl != null)
tvCardTou.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = state.cardTermsOfUseUrl
startActivity(intent)
}
if (state.isTangemTwins) {
tv_reset_to_factory.show()
tv_reset_to_factory.text = getText(R.string.details_row_title_twins_recreate)
tv_reset_to_factory.setOnClickListener {
tvResetToFactory.show()
tvResetToFactory.text = getText(R.string.details_row_title_twins_recreate)
tvResetToFactory.setOnClickListener {
store.dispatch(DetailsAction.ReCreateTwinsWallet(state.twinCardsState.cardNumber!!))
}
} else {
tv_reset_to_factory.show()
tv_reset_to_factory.text = getText(R.string.details_row_title_reset_factory_settings)
tv_reset_to_factory.setOnClickListener {
tvResetToFactory.show()
tvResetToFactory.text = getText(R.string.details_row_title_reset_factory_settings)
tvResetToFactory.setOnClickListener {
store.dispatch(DetailsAction.ResetToFactory.Check)
store.dispatch(DetailsAction.ResetToFactory.Proceed)
}
}
tv_create_backup.show(state.createBackupAllowed)
tv_create_backup.setOnClickListener {
tvCreateBackup.show(state.createBackupAllowed)
tvCreateBackup.setOnClickListener {
store.dispatch(DetailsAction.CreateBackup)
}
tv_app_currency.text = state.appCurrencyState.fiatCurrencyName
tvAppCurrency.text = state.appCurrencyState.fiatCurrencyName
tv_app_currency_title.setOnClickListener {
tvAppCurrencyTitle.setOnClickListener {
store.dispatch(DetailsAction.AppCurrencyAction.ChooseAppCurrency)
}
tv_send_feedback.setOnClickListener {
tvSendFeedback.setOnClickListener {
store.dispatch(GlobalAction.SendFeedback(FeedbackEmail()))
}
tv_wallet_connect.show(state.scanResponse?.card?.isMultiwalletAllowed == true)
tv_wallet_connect.setOnClickListener {
tvWalletConnect.show(state.scanResponse?.card?.isMultiwalletAllowed == true)
tvWalletConnect.setOnClickListener {
store.dispatch(NavigationAction.NavigateTo(AppScreen.WalletConnectSessions))
}
ll_manage_security.setOnClickListener {
llManageSecurity.setOnClickListener {
store.dispatch(DetailsAction.ManageSecurity.CheckCurrentSecurityOption(state.scanResponse!!.card))
}
@ -135,7 +139,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
SecurityOption.AccessCode -> R.string.details_manage_security_access_code
null -> null
}
currentSecurity?.let { tv_security.text = getString(it) }
currentSecurity?.let { tvSecurity.text = getString(it) }
if (state.appCurrencyState.showAppCurrencyDialog &&
!state.appCurrencyState.fiatCurrencies.isNullOrEmpty()) {
@ -147,7 +151,6 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
} else {
currencySelectionDialog.clear()
}
}
}

View file

@ -5,6 +5,7 @@ import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.common.card.Card
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.navigation.NavigationAction
@ -13,14 +14,17 @@ import com.tangem.tap.features.details.redux.DetailsState
import com.tangem.tap.features.details.redux.SecurityOption
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_details_confirm.toolbar
import kotlinx.android.synthetic.main.fragment_details_security.*
import com.tangem.wallet.databinding.FragmentDetailsSecurityBinding
import org.rekotlin.StoreSubscriber
import java.util.*
class DetailsSecurityFragment : Fragment(R.layout.fragment_details_security),
StoreSubscriber<DetailsState> {
private val binding: FragmentDetailsSecurityBinding by viewBinding(
FragmentDetailsSecurityBinding::bind
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
@ -49,14 +53,14 @@ class DetailsSecurityFragment : Fragment(R.layout.fragment_details_security),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setNavigationOnClickListener {
binding.toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo())
}
setOnClickListeners()
}
private fun setOnClickListeners() {
btn_save_changes.setOnClickListener {
binding.btnSaveChanges.setOnClickListener {
store.state.detailsState.securityScreenState?.selectedOption?.let {
store.dispatch(DetailsAction.ManageSecurity.ConfirmSelection(it))
}
@ -64,7 +68,8 @@ class DetailsSecurityFragment : Fragment(R.layout.fragment_details_security),
}
override fun newState(state: DetailsState) {
if (activity == null) return
if (activity == null || view == null) return
selectSecurityOption(state.securityScreenState?.selectedOption)
for (option in SecurityOption.values()) {
setupOption(
@ -73,18 +78,18 @@ class DetailsSecurityFragment : Fragment(R.layout.fragment_details_security),
?: EnumSet.noneOf(SecurityOption::class.java)
)
}
tv_access_code_unavailable_disclaimer.show(
binding.tvAccessCodeUnavailableDisclaimer.show(
state.scanResponse?.card?.backupStatus == Card.BackupStatus.NoBackup
)
}
private fun selectSecurityOption(securityOption: SecurityOption?) {
radiobutton_long_tap.isChecked =
securityOption == SecurityOption.LongTap && radiobutton_long_tap.isEnabled
radiobutton_passcode.isChecked =
securityOption == SecurityOption.PassCode && radiobutton_passcode.isEnabled
radiobutton_access_code.isChecked =
securityOption == SecurityOption.AccessCode && radiobutton_access_code.isEnabled
private fun selectSecurityOption(securityOption: SecurityOption?) = with(binding) {
radiobuttonLongTap.isChecked =
securityOption == SecurityOption.LongTap && radiobuttonLongTap.isEnabled
radiobuttonPasscode.isChecked =
securityOption == SecurityOption.PassCode && radiobuttonPasscode.isEnabled
radiobuttonAccessCode.isChecked =
securityOption == SecurityOption.AccessCode && radiobuttonAccessCode.isEnabled
}
private fun setupOption(option: SecurityOption, allowedOptions: EnumSet<SecurityOption>) {
@ -95,46 +100,46 @@ class DetailsSecurityFragment : Fragment(R.layout.fragment_details_security),
}
}
private fun enableLongTap(enable: Boolean) {
group_long_tap.show(enable)
private fun enableLongTap(enable: Boolean) = with(binding) {
groupLongTap.show(enable)
val alpha = if (enable) 1f else 0.5f
tv_long_tap_description.alpha = alpha
tv_long_tap_title.alpha = alpha
radiobutton_long_tap.alpha = alpha
radiobutton_long_tap.isEnabled = enable
v_long_tap.isClickable = enable
tvLongTapDescription.alpha = alpha
tvLongTapTitle.alpha = alpha
radiobuttonLongTap.alpha = alpha
radiobuttonLongTap.isEnabled = enable
vLongTap.isClickable = enable
if (enable) {
v_long_tap.setOnClickListener {
vLongTap.setOnClickListener {
store.dispatch(DetailsAction.ManageSecurity.SelectOption(SecurityOption.LongTap))
}
}
}
private fun enablePasscode(enable: Boolean) {
group_passcode.show(enable)
private fun enablePasscode(enable: Boolean) = with(binding) {
groupPasscode.show(enable)
val alpha = if (enable) 1f else 0.5f
tv_passcode_description.alpha = alpha
tv_passcode_title.alpha = alpha
radiobutton_passcode.alpha = alpha
radiobutton_passcode.isEnabled = enable
v_passcode.isClickable = enable
tvPasscodeDescription.alpha = alpha
tvPasscodeTitle.alpha = alpha
radiobuttonPasscode.alpha = alpha
radiobuttonPasscode.isEnabled = enable
vPasscode.isClickable = enable
if (enable) {
v_passcode.setOnClickListener {
vPasscode.setOnClickListener {
store.dispatch(DetailsAction.ManageSecurity.SelectOption(SecurityOption.PassCode))
}
}
}
private fun enableAccessCode(enable: Boolean) {
group_access_code.show(enable)
private fun enableAccessCode(enable: Boolean) = with(binding) {
groupAccessCode.show(enable)
val alpha = if (enable) 1f else 0.5f
tv_access_code_description.alpha = alpha
tv_access_code_title.alpha = alpha
radiobutton_access_code.alpha = alpha
radiobutton_access_code.isEnabled = enable
v_access_code.isClickable = enable
tvAccessCodeDescription.alpha = alpha
tvAccessCodeTitle.alpha = alpha
radiobuttonAccessCode.alpha = alpha
radiobuttonAccessCode.isEnabled = enable
vAccessCode.isClickable = enable
if (enable) {
v_access_code.setOnClickListener {
vAccessCode.setOnClickListener {
store.dispatch(DetailsAction.ManageSecurity.SelectOption(SecurityOption.AccessCode))
}
}

View file

@ -3,14 +3,15 @@ package com.tangem.tap.features.details.ui.walletconnect
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.chip.Chip
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.item_wallet_connect_session.view.*
class WalletConnectSessionsAdapter
: ListAdapter<WalletConnectSession, WalletConnectSessionsAdapter.SessionsViewHolder>(
@ -41,15 +42,14 @@ class WalletConnectSessionsAdapter
RecyclerView.ViewHolder(view) {
fun bind(session: WalletConnectSession) {
view.tv_card_id.text = view.context.getString(
view.findViewById<TextView>(R.id.tv_card_id).text = view.context.getString(
R.string.wallet_connect_card_number, session.wallet.cardId
)
view.tv_d_app_name.text = session.peerMeta.name
view.findViewById<TextView>(R.id.tv_d_app_name).text = session.peerMeta.name
view.btn_disconnect.setOnClickListener {
view.findViewById<Chip>(R.id.btn_disconnect).setOnClickListener {
store.dispatch(WalletConnectAction.DisconnectSession(session.session))
}
}
}
}

View file

@ -1,19 +1,20 @@
package com.tangem.tap.features.details.ui.walletconnect
import android.app.Dialog
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_wallet_connect_sessions.*
import com.tangem.wallet.databinding.FragmentWalletConnectSessionsBinding
import org.rekotlin.StoreSubscriber
@ -21,7 +22,9 @@ class WalletConnectSessionsFragment : Fragment(R.layout.fragment_wallet_connect_
StoreSubscriber<WalletConnectState> {
private lateinit var walletConnectSessionsAdapter: WalletConnectSessionsAdapter
private var dialog: Dialog? = null
private val binding: FragmentWalletConnectSessionsBinding by viewBinding(
FragmentWalletConnectSessionsBinding::bind
)
override fun onCreate(savedInstanceState: Bundle?) {
@ -53,7 +56,7 @@ class WalletConnectSessionsFragment : Fragment(R.layout.fragment_wallet_connect_
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setNavigationOnClickListener {
binding.toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo())
}
setOnClickListeners()
@ -61,27 +64,31 @@ class WalletConnectSessionsFragment : Fragment(R.layout.fragment_wallet_connect_
}
private fun setOnClickListeners() {
fab_open_session.setOnClickListener {
binding.fabOpenSession.setOnClickListener {
store.dispatch(WalletConnectAction.StartWalletConnect(activity = requireActivity()))
}
}
private fun setupTransactionsRecyclerView() {
private fun setupTransactionsRecyclerView() = with(binding) {
walletConnectSessionsAdapter = WalletConnectSessionsAdapter()
rv_wallet_connect_sessions.layoutManager = LinearLayoutManager(requireContext())
rv_wallet_connect_sessions.adapter = walletConnectSessionsAdapter
rvWalletConnectSessions.layoutManager = LinearLayoutManager(requireContext())
rvWalletConnectSessions.adapter = walletConnectSessionsAdapter
walletConnectSessionsAdapter.submitList(store.state.walletConnectState.sessions)
}
override fun newState(state: WalletConnectState) {
if (activity == null) return
if (activity == null || view == null) return
fab_open_session.show(!state.loading)
pb_wallet_connect.show(state.loading)
binding.fabOpenSession.show(!state.loading)
binding.pbWalletConnect.show(state.loading)
walletConnectSessionsAdapter.submitList(state.sessions)
rv_wallet_connect_sessions.show(state.sessions.isNotEmpty())
tv_no_sessions.show(state.sessions.isEmpty())
tv_no_sessions_title.show(state.sessions.isEmpty())
showUpdatedList(state.sessions)
}
private fun showUpdatedList(sessions: List<WalletConnectSession>) = with(binding) {
walletConnectSessionsAdapter.submitList(sessions)
rvWalletConnectSessions.show(sessions.isNotEmpty())
tvNoSessions.show(sessions.isEmpty())
tvNoSessionsTitle.show(sessions.isEmpty())
}
}

View file

@ -5,18 +5,21 @@ import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
import com.tangem.tap.features.disclaimer.redux.DisclaimerState
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_disclaimer.*
import com.tangem.wallet.databinding.FragmentDisclaimerBinding
import org.rekotlin.StoreSubscriber
class DisclaimerFragment : Fragment(R.layout.fragment_disclaimer),
StoreSubscriber<DisclaimerState> {
private val binding: FragmentDisclaimerBinding by viewBinding(FragmentDisclaimerBinding::bind)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
@ -45,22 +48,22 @@ class DisclaimerFragment : Fragment(R.layout.fragment_disclaimer),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setNavigationOnClickListener {
binding.toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo())
}
setOnClickListeners()
}
private fun setOnClickListeners() {
btn_accept.setOnClickListener { store.dispatch(DisclaimerAction.AcceptDisclaimer) }
binding.btnAccept.setOnClickListener { store.dispatch(DisclaimerAction.AcceptDisclaimer) }
}
override fun newState(state: DisclaimerState) {
if (activity == null) return
if (activity == null || view == null) return
if (state.accepted) {
half_transparent_overlay.hide()
btn_accept.hide()
binding.halfTransparentOverlay.hide()
binding.btnAccept.hide()
}
}
}

View file

@ -56,7 +56,7 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber<HomeState
override fun newState(state: HomeState) {
if (activity == null) return
if (activity == null || view == null) return
homeState.value = state
}

View file

@ -1,6 +1,8 @@
package com.tangem.tap.features.onboarding
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tangem.tap.common.extensions.copyToClipboard
@ -8,11 +10,11 @@ import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.common.extensions.dispatchShare
import com.tangem.tap.common.extensions.dispatchToastNotification
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.features.wallet.redux.AddressData
import com.tangem.tap.features.wallet.redux.Currency
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.dialog_onboarding_address_info.*
import kotlinx.android.synthetic.main.layout_pseudo_toolbar.*
import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding
/**
[REDACTED_AUTHOR]
@ -22,30 +24,40 @@ class AddressInfoBottomSheetDialog(
context: Context
) : BottomSheetDialog(context) {
init {
this.setContentView(R.layout.dialog_onboarding_address_info)
var binding: DialogOnboardingAddressInfoBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DialogOnboardingAddressInfoBinding
.inflate(LayoutInflater.from(context))
setContentView(binding!!.root)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
setOnCancelListener { store.dispatchDialogHide() }
setOnCancelListener {
store.dispatchDialogHide()
binding = null
}
}
override fun show() {
super.show()
val data = stateDialog.addressData
showData(data = stateDialog.addressData)
}
imv_close.setOnClickListener {
private fun showData(data: AddressData) = with(binding!!) {
pseudoToolbar.imvClose.setOnClickListener {
dismissWithAnimation = true
cancel()
}
imv_qr_code.setImageBitmap(data.qrCode)
tv_address.text = data.address
btn_fl_copy_address.setOnClickListener {
imvQrCode.setImageBitmap(data.qrCode)
tvAddress.text = data.address
btnFlCopyAddress.setOnClickListener {
context.copyToClipboard(data.address)
store.dispatchToastNotification(R.string.copy_toast_msg)
}
btn_fl_share.setOnClickListener {
btnFlShare.setOnClickListener {
store.dispatchShare(data.shareUrl)
}
tv_receive_message.text = getQRReceiveMessage(tv_receive_message.context, stateDialog.currency)
tvReceiveMessage.text = getQRReceiveMessage(tvReceiveMessage.context, stateDialog.currency)
}
}

View file

@ -1,14 +1,12 @@
package com.tangem.tap.features.onboarding.products
import android.os.Bundle
import android.view.View
import com.tangem.tap.common.extensions.inflate
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.transitions.HomeToOnboardingTransition
import com.tangem.tap.features.BaseStoreFragment
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
import kotlinx.android.synthetic.main.view_confetti.*
import com.tangem.wallet.databinding.FragmentOnboardingMainBinding
import com.tangem.wallet.databinding.ViewOnboardingProgressBinding
import org.rekotlin.StoreSubscriber
/**
@ -16,26 +14,22 @@ import org.rekotlin.StoreSubscriber
*/
abstract class BaseOnboardingFragment<T> : BaseStoreFragment(R.layout.fragment_onboarding_main), StoreSubscriber<T> {
protected val binding: FragmentOnboardingMainBinding by viewBinding(FragmentOnboardingMainBinding::bind)
protected val pbBinding: ViewOnboardingProgressBinding by viewBinding(ViewOnboardingProgressBinding::bind)
override fun configureTransitions() {
enterTransition = HomeToOnboardingTransition()
exitTransition = HomeToOnboardingTransition()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
onboarding_top_container.inflate(getOnboardingTopContainerId(), true)
}
abstract fun getOnboardingTopContainerId(): Int
protected fun showConfetti(show: Boolean) {
lav_confetti.show(show)
protected fun showConfetti(show: Boolean) = with(binding.vConfetti) {
lavConfetti.show(show)
if (show) {
lav_confetti.playAnimation()
lavConfetti.playAnimation()
} else {
lav_confetti.cancelAnimation()
lavConfetti.cancelAnimation()
}
}
}

View file

@ -26,11 +26,6 @@ import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteStat
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteStep
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.*
import kotlinx.android.synthetic.main.layout_onboarding_container_top.*
import kotlinx.android.synthetic.main.view_onboarding_progress.*
import kotlinx.android.synthetic.main.view_onboarding_tv_balance.*
/**
[REDACTED_AUTHOR]
@ -41,8 +36,6 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
@ -52,11 +45,11 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
super.onViewCreated(view, savedInstanceState)
addBackPressHandler(this)
imv_front_card.transitionName = ShareElement.imvFrontCard
binding.onboardingTopContainer.imvFrontCard.transitionName = ShareElement.imvFrontCard
startPostponedEnterTransition()
toolbar.setTitle(R.string.onboarding_title)
btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container)
binding.toolbar.setTitle(R.string.onboarding_title)
btnRefreshBalanceWidget = RefreshBalanceWidget(binding.onboardingTopContainer.onboardingMainContainer)
store.dispatch(OnboardingNoteAction.LoadCardArtwork)
store.dispatch(OnboardingNoteAction.DetermineStepOfScreen)
@ -72,19 +65,19 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
}
override fun newState(state: OnboardingNoteState) {
if (activity == null) return
if (activity == null || view == null) return
Picasso.get()
.load(state.cardArtworkUrl)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(imv_front_card)
.load(state.cardArtworkUrl)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(binding.onboardingTopContainer.imvFrontCard)
pb_state.max = state.steps.size - 1
pb_state.progress = state.progress
pbBinding.pbState.max = state.steps.size - 1
pbBinding.pbState.progress = state.progress
when (state.currentStep) {
OnboardingNoteStep.None -> setupNoneState(state)
OnboardingNoteStep.None -> setupNoneState()
OnboardingNoteStep.CreateWallet -> setupCreateWalletState(state)
OnboardingNoteStep.TopUpWallet -> setupTopUpWalletState(state)
OnboardingNoteStep.Done -> setupDoneState(state)
@ -97,77 +90,82 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
private fun setBalance(state: OnboardingNoteState) {
if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return
if (state.balanceCriticalError == null) {
val balanceValue = state.walletBalance.value.stripZeroPlainString()
tv_balance_value.text = balanceValue
tv_balance_currency.text = state.walletBalance.currency.currencySymbol
} else {
tv_balance_value.text = ""
tv_balance_currency.text = ""
with (binding.onboardingTopContainer.onboardingTvBalance) {
if (state.balanceCriticalError == null) {
val balanceValue = state.walletBalance.value.stripZeroPlainString()
tvBalanceValue.text = balanceValue
tvBalanceCurrency.text = state.walletBalance.currency.currencySymbol
} else {
tvBalanceValue.text = ""
tvBalanceCurrency.text = ""
}
}
}
private fun setupNoneState(state: OnboardingNoteState) {
btn_main_action.isVisible = false
btn_alternative_action.isVisible = false
private fun setupNoneState() = with(binding.onboardingActionContainer) {
btnMainAction.isVisible = false
btnAlternativeAction.isVisible = false
btnRefreshBalanceWidget.show(false)
tv_header.isVisible = false
tv_body.isVisible = false
tvHeader.isVisible = false
tvBody.isVisible = false
btn_main_action.text = ""
btn_alternative_action.text = ""
tv_header.text = ""
tv_body.text = ""
btnMainAction.text = ""
btnAlternativeAction.text = ""
tvHeader.text = ""
tvBody.text = ""
}
private fun setupCreateWalletState(state: OnboardingNoteState) {
btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet)
btn_main_action.setOnClickListener { store.dispatch(OnboardingNoteAction.CreateWallet) }
btn_alternative_action.setText(R.string.onboarding_button_what_does_it_mean)
btn_alternative_action.setOnClickListener { }
private fun setupCreateWalletState(state: OnboardingNoteState) =
with(binding.onboardingActionContainer) {
btnMainAction.setText(R.string.onboarding_create_wallet_button_create_wallet)
btnMainAction.setOnClickListener { store.dispatch(OnboardingNoteAction.CreateWallet) }
btnAlternativeAction.setText(R.string.onboarding_button_what_does_it_mean)
btnAlternativeAction.setOnClickListener { }
btnRefreshBalanceWidget.mainView.setOnClickListener(null)
btnRefreshBalanceWidget.mainView.setOnClickListener(null)
tv_header.setText(R.string.onboarding_create_wallet_header)
tv_body.setText(R.string.onboarding_create_wallet_body)
tvHeader.setText(R.string.onboarding_create_wallet_header)
tvBody.setText(R.string.onboarding_create_wallet_body)
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle))
updateConstraints(state.currentStep, R.layout.lp_onboarding_create_wallet)
binding.onboardingTopContainer.imvCardBackground.setBackgroundDrawable(
requireContext().getDrawableCompat(R.drawable.shape_circle)
)
updateConstraints(state.currentStep, R.layout.lp_onboarding_create_wallet)
btn_alternative_action.isVisible = false // temporary
}
btnAlternativeAction.isVisible = false // temporary
}
private fun setupTopUpWalletState(state: OnboardingNoteState) {
private fun setupTopUpWalletState(state: OnboardingNoteState) = with(binding.onboardingActionContainer) {
if (state.isBuyAllowed) {
btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto)
btn_main_action.setOnClickListener {
btnMainAction.setText(R.string.onboarding_top_up_button_but_crypto)
btnMainAction.setOnClickListener {
store.dispatch(OnboardingNoteAction.TopUp)
}
btn_alternative_action.isVisible = true
btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address)
btn_alternative_action.setOnClickListener {
btnAlternativeAction.isVisible = true
btnAlternativeAction.setText(R.string.onboarding_top_up_button_show_wallet_address)
btnAlternativeAction.setOnClickListener {
store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog)
}
} else {
btn_main_action.setText(R.string.onboarding_button_receive_crypto)
btn_main_action.setOnClickListener {
btnMainAction.setText(R.string.onboarding_button_receive_crypto)
btnMainAction.setOnClickListener {
store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog)
}
btn_alternative_action.isVisible = false
btnAlternativeAction.isVisible = false
}
tv_header.setText(R.string.onboarding_top_up_header)
tvHeader.setText(R.string.onboarding_top_up_header)
if (state.balanceNonCriticalError == null) {
tv_body.setText(R.string.onboarding_top_up_body)
tvBody.setText(R.string.onboarding_top_up_body)
} else {
state.walletBalance.amountToCreateAccount?.let { amount ->
val tvBodyMessage = getString(
R.string.onboarding_top_up_body_no_account_error,
amount, state.walletBalance.currency.currencySymbol
)
tv_body.text = tvBodyMessage
tvBody.text = tvBodyMessage
}
}
@ -177,38 +175,43 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
store.dispatch(OnboardingNoteAction.Balance.Update)
}
}
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
binding.onboardingTopContainer.imvCardBackground
.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
updateConstraints(state.currentStep, R.layout.lp_onboarding_topup_wallet)
}
private fun setupDoneState(state: OnboardingNoteState) {
btn_main_action.setText(R.string.onboarding_done_button_continue)
btn_main_action.setOnClickListener {
private fun setupDoneState(state: OnboardingNoteState) = with(binding.onboardingActionContainer) {
btnMainAction.setText(R.string.onboarding_done_button_continue)
btnMainAction.setOnClickListener {
showConfetti(false)
store.dispatch(OnboardingNoteAction.Done)
}
btn_alternative_action.isVisible = false
btn_alternative_action.setText("")
btn_alternative_action.setOnClickListener { }
btnAlternativeAction.isVisible = false
btnAlternativeAction.setText("")
btnAlternativeAction.setOnClickListener { }
btnRefreshBalanceWidget.mainView.setOnClickListener(null)
tv_header.setText(R.string.onboarding_done_header)
tv_body.setText(R.string.onboarding_done_body)
tvHeader.setText(R.string.onboarding_done_header)
tvBody.setText(R.string.onboarding_done_body)
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
binding.onboardingTopContainer.imvCardBackground.setBackgroundDrawable(
requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)
)
updateConstraints(state.currentStep, R.layout.lp_onboarding_done_activation)
}
private fun updateConstraints(currentStep: OnboardingNoteStep, @LayoutRes layoutId: Int) {
if (this.previousStep == currentStep) return
val constraintSet = ConstraintSet()
constraintSet.clone(requireContext(), layoutId)
constraintSet.applyTo(onboarding_main_container)
val transition = InternalNoteLayoutTransition()
transition.interpolator = OvershootInterpolator()
TransitionManager.beginDelayedTransition(onboarding_main_container, transition)
with(binding.onboardingTopContainer) {
val constraintSet = ConstraintSet()
constraintSet.clone(requireContext(), layoutId)
constraintSet.applyTo(onboardingMainContainer)
val transition = InternalNoteLayoutTransition()
transition.interpolator = OvershootInterpolator()
TransitionManager.beginDelayedTransition(onboardingMainContainer, transition)
}
}
}

View file

@ -18,18 +18,12 @@ import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOt
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.*
import kotlinx.android.synthetic.main.layout_onboarding_container_top.*
import kotlinx.android.synthetic.main.view_onboarding_progress.*
/**
[REDACTED_AUTHOR]
*/
class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCardsState>() {
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
@ -39,10 +33,10 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCards
super.onViewCreated(view, savedInstanceState)
addBackPressHandler(this)
imv_front_card.transitionName = ShareElement.imvFrontCard
binding.onboardingTopContainer.imvFrontCard.transitionName = ShareElement.imvFrontCard
startPostponedEnterTransition()
toolbar.setTitle(R.string.onboarding_title)
binding.toolbar.setTitle(R.string.onboarding_title)
store.dispatch(OnboardingOtherCardsAction.LoadCardArtwork)
store.dispatch(OnboardingOtherCardsAction.DetermineStepOfScreen)
}
@ -57,64 +51,75 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCards
}
override fun newState(state: OnboardingOtherCardsState) {
if (activity == null) return
if (activity == null || view == null) return
if (state.currentStep == OnboardingOtherCardsStep.None) return
Picasso.get()
.load(state.cardArtworkUrl)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(imv_front_card)
.load(state.cardArtworkUrl)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(binding.onboardingTopContainer.imvFrontCard)
pb_state.max = state.steps.size - 1
pb_state.progress = state.progress
pbBinding.pbState.max = state.steps.size - 1
pbBinding.pbState.progress = state.progress
when (state.currentStep) {
OnboardingOtherCardsStep.CreateWallet -> setupCreateWalletState(state)
OnboardingOtherCardsStep.Done -> setupDoneState(state)
OnboardingOtherCardsStep.CreateWallet -> setupCreateWalletState()
OnboardingOtherCardsStep.Done -> setupDoneState()
}
showConfetti(state.showConfetti)
}
private fun setupCreateWalletState(state: OnboardingOtherCardsState) {
btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet)
btn_main_action.setOnClickListener { store.dispatch(OnboardingOtherCardsAction.CreateWallet) }
btn_alternative_action.setText(R.string.onboarding_button_what_does_it_mean)
btn_alternative_action.setOnClickListener { }
private fun setupCreateWalletState() = with(binding) {
tv_header.setText(R.string.onboarding_create_wallet_header)
tv_body.setText(R.string.onboarding_create_wallet_body)
with(onboardingActionContainer) {
btnMainAction.setText(R.string.onboarding_create_wallet_button_create_wallet)
btnMainAction.setOnClickListener { store.dispatch(OnboardingOtherCardsAction.CreateWallet) }
btnAlternativeAction.setText(R.string.onboarding_button_what_does_it_mean)
btnAlternativeAction.setOnClickListener { }
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle))
tvHeader.setText(R.string.onboarding_create_wallet_header)
tvBody.setText(R.string.onboarding_create_wallet_body)
btnAlternativeAction.isVisible = false // temporary
}
binding.onboardingTopContainer.imvCardBackground.setBackgroundDrawable(
requireContext().getDrawableCompat(R.drawable.shape_circle)
)
updateConstraints(R.layout.lp_onboarding_create_wallet)
btn_alternative_action.isVisible = false // temporary
}
private fun setupDoneState(state: OnboardingOtherCardsState) {
btn_main_action.setText(R.string.onboarding_done_button_continue)
btn_main_action.setOnClickListener {
private fun setupDoneState() = with(binding.onboardingActionContainer) {
btnMainAction.setText(R.string.onboarding_done_button_continue)
btnMainAction.setOnClickListener {
showConfetti(false)
store.dispatch(OnboardingOtherCardsAction.Done)
}
btn_alternative_action.isVisible = false
btn_alternative_action.setText("")
btn_alternative_action.setOnClickListener { }
btnAlternativeAction.isVisible = false
btnAlternativeAction.setText("")
btnAlternativeAction.setOnClickListener { }
tv_header.setText(R.string.onboarding_done_header)
tv_body.setText(R.string.onboarding_done_body)
tvHeader.setText(R.string.onboarding_done_header)
tvBody.setText(R.string.onboarding_done_body)
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
binding.onboardingTopContainer.imvCardBackground?.setBackgroundDrawable(
requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)
)
updateConstraints(R.layout.lp_onboarding_done)
}
private fun updateConstraints(@LayoutRes layoutId: Int) {
val constraintSet = ConstraintSet()
constraintSet.clone(requireContext(), layoutId)
constraintSet.applyTo(onboarding_main_container)
val transition = InternalNoteLayoutTransition()
transition.interpolator = OvershootInterpolator()
TransitionManager.beginDelayedTransition(onboarding_main_container, transition)
with(binding.onboardingTopContainer) {
val constraintSet = ConstraintSet()
constraintSet.clone(requireContext(), layoutId)
constraintSet.applyTo(onboardingMainContainer)
val transition = InternalNoteLayoutTransition()
transition.interpolator = OvershootInterpolator()
TransitionManager.beginDelayedTransition(onboardingMainContainer, transition)
}
}
}

View file

@ -31,12 +31,7 @@ import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_main.*
import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.*
import kotlinx.android.synthetic.main.layout_onboarding_container_top.*
import kotlinx.android.synthetic.main.view_bg_twins_welcome.*
import kotlinx.android.synthetic.main.view_onboarding_progress.*
import kotlinx.android.synthetic.main.view_onboarding_tv_balance.*
import com.tangem.wallet.databinding.LayoutOnboardingContainerTopBinding
class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
@ -47,12 +42,11 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
private val assetReader: AssetReader by lazy {
object : AssetReader {
override fun readAssetAsString(name: String): String = requireContext().readAssetAsString(name)
override fun readAssetAsString(name: String): String =
requireContext().readAssetAsString(name)
}
}
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
@ -74,36 +68,39 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
super.onViewCreated(view, savedInstanceState)
addBackPressHandler(this)
reconfigureLayoutForTwins()
twinsWidget = TwinsCardWidget(LeapfrogWidget(cards_container)) { 285f }
btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container)
reconfigureLayoutForTwins(binding.onboardingTopContainer)
twinsWidget =
TwinsCardWidget(LeapfrogWidget(binding.onboardingTopContainer.cardsContainer)) { 285f }
btnRefreshBalanceWidget =
RefreshBalanceWidget(binding.onboardingTopContainer.onboardingMainContainer)
toolbar.title = getText(R.string.twins_recreate_toolbar)
binding.toolbar.title = getText(R.string.twins_recreate_toolbar)
Picasso.get()
.load(Artwork.TWIN_CARD_1)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(imv_twin_front_card)
.load(Artwork.TWIN_CARD_1)
.error(R.drawable.card_placeholder_black)
.placeholder(R.drawable.card_placeholder_black)
?.into(binding.onboardingTopContainer.imvTwinFrontCard)
Picasso.get()
.load(Artwork.TWIN_CARD_2)
.error(R.drawable.card_placeholder_white)
.placeholder(R.drawable.card_placeholder_white)
?.into(imv_twin_back_card)
.load(Artwork.TWIN_CARD_2)
.error(R.drawable.card_placeholder_white)
.placeholder(R.drawable.card_placeholder_white)
?.into(binding.onboardingTopContainer.imvTwinBackCard)
}
private fun reconfigureLayoutForTwins() {
imv_front_card.hide()
imv_card_background.hide()
cards_container.show()
private fun reconfigureLayoutForTwins(containerBinding: LayoutOnboardingContainerTopBinding) =
with(containerBinding) {
imvFrontCard.hide()
imvCardBackground.hide()
cardsContainer.show()
imv_twin_front_card.transitionName = ShareElement.imvFrontCard
imv_twin_back_card.transitionName = ShareElement.imvBackCard
imvTwinFrontCard.transitionName = ShareElement.imvFrontCard
imvTwinBackCard.transitionName = ShareElement.imvBackCard
// if don't this, the bg_circle_... is overflow the app_bar
app_bar.bringToFront()
}
// if don't this, the bg_circle_... is overflow the app_bar
binding.appBar.bringToFront()
}
override fun subscribeToStore() {
store.subscribe(this) { state ->
@ -115,10 +112,10 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
}
override fun newState(state: TwinCardsState) {
if (activity == null) return
if (activity == null || view == null) return
pb_state.max = state.steps.size - 1
pb_state.progress = state.progress
pbBinding.pbState.max = state.steps.size - 1
pbBinding.pbState.progress = state.progress
when (state.currentStep) {
TwinCardsStep.WelcomeOnly -> setupWelcomeOnlyState(state)
@ -139,13 +136,15 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
private fun setBalance(state: TwinCardsState) {
if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return
if (state.balanceCriticalError == null) {
val balanceValue = state.walletBalance.value.stripZeroPlainString()
tv_balance_value.text = balanceValue
tv_balance_currency.text = state.walletBalance.currency.currencySymbol
} else {
tv_balance_value.text = ""
tv_balance_currency.text = ""
with(binding.onboardingTopContainer.onboardingTvBalance) {
if (state.balanceCriticalError == null) {
val balanceValue = state.walletBalance.value.stripZeroPlainString()
tvBalanceValue.text = balanceValue
tvBalanceCurrency.text = state.walletBalance.currency.currencySymbol
} else {
tvBalanceValue.text = ""
tvBalanceCurrency.text = ""
}
}
}
@ -160,203 +159,235 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
setupWelcomeState(state) { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet)) }
}
private fun setupWelcomeState(state: TwinCardsState, mainAction: VoidCallback) {
private fun setupWelcomeState(state: TwinCardsState, mainAction: VoidCallback) =
with(binding.onboardingActionContainer) {
twinsWidget.toWelcome(false) { startPostponedEnterTransition() }
binding.onboardingTopContainer.onboardingTwinsWelcomeBg.root.show()
pbBinding.pbState.hide()
tvHeader.setText(R.string.twins_onboarding_subtitle)
tvBody.text = getString(
R.string.twins_onboarding_description_format,
state.cardNumber?.pairIndexNumber()
)
btnMainAction.setText(R.string.common_continue)
btnMainAction.setOnClickListener { mainAction() }
}
private fun setupWarningState(state: TwinCardsState) = with(binding.onboardingActionContainer) {
twinsWidget.toWelcome(false) { startPostponedEnterTransition() }
onboarding_twins_welcome_bg.show()
pb_state.hide()
binding.onboardingTopContainer.onboardingTwinsWelcomeBg.root.hide()
pbBinding.pbState.hide()
chbUnderstand.show()
tv_header.setText(R.string.twins_onboarding_subtitle)
tv_body.text = getString(R.string.twins_onboarding_description_format, state.cardNumber?.pairIndexNumber())
tvHeader.setText(R.string.common_warning)
tvBody.setText(R.string.twins_recreate_warning)
btn_main_action.setText(R.string.common_continue)
btn_main_action.setOnClickListener { mainAction() }
}
private fun setupWarningState(state: TwinCardsState) {
twinsWidget.toWelcome(false) { startPostponedEnterTransition() }
onboarding_twins_welcome_bg.hide()
pb_state.hide()
chb_understand.show()
tv_header.setText(R.string.common_warning)
tv_body.setText(R.string.twins_recreate_warning)
chb_understand.setOnCheckedChangeListener { buttonView, isChecked ->
chbUnderstand.setOnCheckedChangeListener { buttonView, isChecked ->
store.dispatch(TwinCardsAction.SetUserUnderstand(isChecked))
}
btn_main_action.isEnabled = state.userWasUnderstandIfWalletRecreate
btn_main_action.setText(R.string.common_continue)
btn_main_action.setOnClickListener {
btnMainAction.isEnabled = state.userWasUnderstandIfWalletRecreate
btnMainAction.setText(R.string.common_continue)
btnMainAction.setOnClickListener {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet))
}
}
private fun setupCreateFirstWalletState(state: TwinCardsState) {
onboarding_twins_welcome_bg.hide()
bg_circle_large.hide()
bg_circle_medium.hide()
bg_circle_min.hide()
chb_understand.hide()
pb_state.show()
private fun setupCreateFirstWalletState(state: TwinCardsState) =
with(binding.onboardingActionContainer) {
binding.onboardingTopContainer.onboardingTwinsWelcomeBg.root.hide()
binding.onboardingTopContainer.onboardingTwinsWelcomeBg.bgCircleLarge.hide()
binding.onboardingTopContainer.onboardingTwinsWelcomeBg.bgCircleMedium.hide()
binding.onboardingTopContainer.onboardingTwinsWelcomeBg.bgCircleMin.hide()
chbUnderstand.hide()
pbBinding.pbState.show()
onboarding_main_container.beginDelayedTransition()
binding.onboardingTopContainer.onboardingMainContainer.beginDelayedTransition()
when (previousStep) {
TwinCardsStep.None -> {
twinsWidget.toLeapfrog(false) {
when (state.cardNumber) {
TwinCardNumber.First -> startPostponedEnterTransition()
TwinCardNumber.Second -> {
switchToCard(state.cardNumber, false) { startPostponedEnterTransition() }
when (previousStep) {
TwinCardsStep.None -> {
twinsWidget.toLeapfrog(false) {
when (state.cardNumber) {
TwinCardNumber.First -> startPostponedEnterTransition()
TwinCardNumber.Second -> {
switchToCard(
state.cardNumber,
false
) { startPostponedEnterTransition() }
}
}
}
}
TwinCardsStep.Welcome, TwinCardsStep.Warning -> {
twinsWidget.toLeapfrog(onEnd = { switchToCard(state.cardNumber) })
}
}
TwinCardsStep.Welcome, TwinCardsStep.Warning -> {
twinsWidget.toLeapfrog(onEnd = { switchToCard(state.cardNumber) })
val twinIndexNumber = state.cardNumber?.indexNumber()
tvHeader.text = getString(R.string.twins_recreate_title_format, twinIndexNumber)
tvBody.setText(R.string.onboarding_twins_interrupt_warning)
btnMainAction.text = getString(R.string.twins_recreate_button_format, twinIndexNumber)
btnMainAction.setOnClickListener {
store.dispatch(
TwinCardsAction.Wallet.LaunchFirstStep(
Message(getString(R.string.twins_recreate_title_format, twinIndexNumber)),
assetReader
)
)
}
btnAlternativeAction.isVisible = false
btnAlternativeAction.isClickable = false
}
private fun setupCreateSecondWalletState(state: TwinCardsState) =
with(binding.onboardingActionContainer) {
switchToCard(state.cardNumber?.pairNumber())
val twinPairIndexNumber = state.cardNumber?.pairIndexNumber()
tvHeader.text = getString(R.string.twins_recreate_title_format, twinPairIndexNumber)
tvBody.setText(R.string.onboarding_twins_interrupt_warning)
btnMainAction.text =
getString(R.string.twins_recreate_button_format, twinPairIndexNumber)
btnMainAction.setOnClickListener {
store.dispatch(
TwinCardsAction.Wallet.LaunchSecondStep(
Message(
getString(
R.string.twins_recreate_title_format,
twinPairIndexNumber
)
),
Message(getString(R.string.twins_recreate_title_preparing)),
Message(getString(R.string.twins_recreate_title_creating_wallet)),
)
)
}
}
val twinIndexNumber = state.cardNumber?.indexNumber()
tv_header.text = getString(R.string.twins_recreate_title_format, twinIndexNumber)
tv_body.setText(R.string.onboarding_twins_interrupt_warning)
private fun setupCreateThirdWalletState(state: TwinCardsState) =
with(binding.onboardingActionContainer) {
switchToCard(state.cardNumber)
btn_main_action.text = getString(R.string.twins_recreate_button_format, twinIndexNumber)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep(
Message(getString(R.string.twins_recreate_title_format, twinIndexNumber)),
assetReader
))
val twinIndexNumber = state.cardNumber?.indexNumber()
tvHeader.text = getString(R.string.twins_recreate_title_format, twinIndexNumber)
tvBody.setText(R.string.onboarding_twins_interrupt_warning)
btnMainAction.text = getString(R.string.twins_recreate_button_format, twinIndexNumber)
btnMainAction.setOnClickListener {
store.dispatch(
TwinCardsAction.Wallet.LaunchThirdStep(
Message(getString(R.string.twins_recreate_title_format, twinIndexNumber))
)
)
}
}
btn_alternative_action.isVisible = false
btn_alternative_action.isClickable = false
}
private fun setupCreateSecondWalletState(state: TwinCardsState) {
switchToCard(state.cardNumber?.pairNumber())
val twinPairIndexNumber = state.cardNumber?.pairIndexNumber()
tv_header.text = getString(R.string.twins_recreate_title_format, twinPairIndexNumber)
tv_body.setText(R.string.onboarding_twins_interrupt_warning)
btn_main_action.text = getString(R.string.twins_recreate_button_format, twinPairIndexNumber)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep(
Message(getString(R.string.twins_recreate_title_format, twinPairIndexNumber)),
Message(getString(R.string.twins_recreate_title_preparing)),
Message(getString(R.string.twins_recreate_title_creating_wallet)),
))
}
}
private fun setupCreateThirdWalletState(state: TwinCardsState) {
switchToCard(state.cardNumber)
val twinIndexNumber = state.cardNumber?.indexNumber()
tv_header.text = getString(R.string.twins_recreate_title_format, twinIndexNumber)
tv_body.setText(R.string.onboarding_twins_interrupt_warning)
btn_main_action.text = getString(R.string.twins_recreate_button_format, twinIndexNumber)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep(
Message(getString(R.string.twins_recreate_title_format, twinIndexNumber))
))
}
}
private fun setupTopUpWalletState(state: TwinCardsState) {
when (previousStep) {
TwinCardsStep.None -> {
when (state.cardNumber) {
TwinCardNumber.First -> {
twinsWidget.leapfrogWidget.unfold(false) {
twinsWidget.toActivate(false) {
private fun setupTopUpWalletState(state: TwinCardsState) =
with(binding.onboardingActionContainer) {
when (previousStep) {
TwinCardsStep.None -> {
when (state.cardNumber) {
TwinCardNumber.First -> {
twinsWidget.leapfrogWidget.unfold(false) {
twinsWidget.toActivate(false) {
startPostponedEnterTransition()
}
}
}
TwinCardNumber.Second -> {
switchToCard(state.cardNumber, false) {
twinsWidget.toActivate(false)
startPostponedEnterTransition()
}
}
}
TwinCardNumber.Second -> {
switchToCard(state.cardNumber, false) {
twinsWidget.toActivate(false)
startPostponedEnterTransition()
}
}
}
TwinCardsStep.CreateThirdWallet -> {
twinsWidget.toActivate()
}
}
TwinCardsStep.CreateThirdWallet -> {
twinsWidget.toActivate()
}
}
if (state.isBuyAllowed) {
btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.TopUp)
if (state.isBuyAllowed) {
btnMainAction.setText(R.string.onboarding_top_up_button_but_crypto)
btnMainAction.setOnClickListener {
store.dispatch(TwinCardsAction.TopUp)
}
btnAlternativeAction.isVisible = true
btnAlternativeAction.setText(R.string.onboarding_top_up_button_show_wallet_address)
btnAlternativeAction.setOnClickListener {
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)
}
} else {
btnAlternativeAction.setText(R.string.onboarding_button_receive_crypto)
btnAlternativeAction.setOnClickListener {
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)
}
btnAlternativeAction.isVisible = false
}
btn_alternative_action.isVisible = true
btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address)
btn_alternative_action.setOnClickListener {
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)
tvHeader.setText(R.string.onboarding_top_up_header)
tvBody.setText(R.string.onboarding_top_up_body)
btnRefreshBalanceWidget.changeState(state.walletBalance.state)
if (btnRefreshBalanceWidget.isShowing != true) {
btnRefreshBalanceWidget.mainView.setOnClickListener {
store.dispatch(TwinCardsAction.Balance.Update)
}
}
} else {
btn_main_action.setText(R.string.onboarding_button_receive_crypto)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)
binding.onboardingTopContainer.imvCardBackground.setBackgroundDrawable(
requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)
)
updateConstraints(state.currentStep, R.layout.lp_onboarding_topup_wallet_twins)
}
private fun setupDoneState(state: TwinCardsState) =
with(binding.onboardingActionContainer) {
btnMainAction.setText(R.string.onboarding_done_button_continue)
btnMainAction.setOnClickListener {
store.dispatch(TwinCardsAction.Confetti.Hide)
store.dispatch(TwinCardsAction.Done)
}
btn_alternative_action.isVisible = false
}
btnAlternativeAction.isVisible = false
btnAlternativeAction.isClickable = false
tv_header.setText(R.string.onboarding_top_up_header)
tv_body.setText(R.string.onboarding_top_up_body)
tvHeader.setText(R.string.onboarding_done_header)
tvBody.setText(R.string.onboarding_done_body)
btnRefreshBalanceWidget.changeState(state.walletBalance.state)
if (btnRefreshBalanceWidget.isShowing != true) {
btnRefreshBalanceWidget.mainView.setOnClickListener {
store.dispatch(TwinCardsAction.Balance.Update)
val layout = when (state.mode) {
CreateTwinWalletMode.CreateWallet -> R.layout.lp_onboarding_done_activation_twins
CreateTwinWalletMode.RecreateWallet -> R.layout.lp_onboarding_done
}
updateConstraints(state.currentStep, layout)
}
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
updateConstraints(state.currentStep, R.layout.lp_onboarding_topup_wallet_twins)
}
private fun setupDoneState(state: TwinCardsState) {
btn_main_action.setText(R.string.onboarding_done_button_continue)
btn_main_action.setOnClickListener {
store.dispatch(TwinCardsAction.Confetti.Hide)
store.dispatch(TwinCardsAction.Done)
}
btn_alternative_action.isVisible = false
btn_alternative_action.isClickable = false
tv_header.setText(R.string.onboarding_done_header)
tv_body.setText(R.string.onboarding_done_body)
val layout = when (state.mode) {
CreateTwinWalletMode.CreateWallet -> R.layout.lp_onboarding_done_activation_twins
CreateTwinWalletMode.RecreateWallet -> R.layout.lp_onboarding_done
}
updateConstraints(state.currentStep, layout)
}
private fun updateConstraints(currentStep: TwinCardsStep, @LayoutRes layoutId: Int) {
if (this.previousStep == currentStep) return
val constraintSet = ConstraintSet()
constraintSet.clone(requireContext(), layoutId)
constraintSet.applyTo(onboarding_main_container)
val transition = InternalNoteLayoutTransition()
transition.interpolator = OvershootInterpolator()
TransitionManager.beginDelayedTransition(onboarding_main_container, transition)
with(binding.onboardingTopContainer) {
val constraintSet = ConstraintSet()
constraintSet.clone(requireContext(), layoutId)
constraintSet.applyTo(onboardingMainContainer)
val transition = InternalNoteLayoutTransition()
transition.interpolator = OvershootInterpolator()
TransitionManager.beginDelayedTransition(onboardingMainContainer, transition)
}
}
private fun switchToCard(cardNumber: TwinCardNumber?, animate: Boolean = true, onEnd: VoidCallback = {}) {
private fun switchToCard(
cardNumber: TwinCardNumber?,
animate: Boolean = true,
onEnd: VoidCallback = {}
) {
val position = cardNumber?.number ?: return
if (twinsWidget.leapfrogWidget.getViewPositionByIndex(1) != position - 1) {

View file

@ -1,28 +1,30 @@
package com.tangem.tap.features.onboarding.products.wallet.ui
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView
import com.tangem.tap.common.extensions.getString
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.item_backup_info_adapter.view.*
import com.tangem.wallet.databinding.ItemBackupInfoAdapterBinding
class BackupInfoAdapter : RecyclerView.Adapter<BackupInfoViewHolder>() {
private val data = backupInfoSnippets
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BackupInfoViewHolder =
BackupInfoViewHolder(LayoutInflater.from(parent.context)
.inflate(R.layout.item_backup_info_adapter, parent, false))
BackupInfoViewHolder(
ItemBackupInfoAdapterBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
)
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(holder: BackupInfoViewHolder, position: Int) =
holder.itemView.run {
tv_header.text = getString(data[position].header)
tv_body.text = getString(data[position].body)
holder.binding.run {
tvHeader.text = root.getString(data[position].header)
tvBody.text = root.getString(data[position].body)
}
}
@ -51,4 +53,5 @@ private val backupInfoSnippets = listOf(
)
class BackupInfoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
class BackupInfoViewHolder(val binding: ItemBackupInfoAdapterBinding) :
RecyclerView.ViewHolder(binding.root)

View file

@ -7,6 +7,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import androidx.transition.TransitionManager
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.tabs.TabLayoutMediator
import com.squareup.picasso.Picasso
@ -21,11 +22,8 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.*
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.AccessCodeDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_onboarding_wallet.*
import kotlinx.android.synthetic.main.layout_onboarding_buttons_add_cards.*
import kotlinx.android.synthetic.main.layout_onboarding_buttons_common.*
import kotlinx.android.synthetic.main.view_confetti.*
import kotlinx.android.synthetic.main.view_onboarding_progress.*
import com.tangem.wallet.databinding.FragmentOnboardingWalletBinding
import com.tangem.wallet.databinding.ViewOnboardingProgressBinding
import org.rekotlin.StoreSubscriber
class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
@ -33,6 +31,12 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
private var accessCodeDialog: AccessCodeDialog? = null
private lateinit var cardsWidget: WalletCardsWidget
private val binding: FragmentOnboardingWalletBinding by viewBinding(
FragmentOnboardingWalletBinding::bind
)
private val pbBinding: ViewOnboardingProgressBinding by viewBinding(
ViewOnboardingProgressBinding::bind)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -47,19 +51,22 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val leapfrog = LeapfrogWidget(fl_cards_container)
val leapfrog = LeapfrogWidget(binding.flCardsContainer)
cardsWidget = WalletCardsWidget(leapfrog) { 200f }
startPostponedEnterTransition()
view_pager_backup_info.adapter = BackupInfoAdapter()
TabLayoutMediator(tab_layout_backup_info, view_pager_backup_info) { tab, position ->
binding.viewPagerBackupInfo.adapter = BackupInfoAdapter()
TabLayoutMediator(
binding.tabLayoutBackupInfo,
binding.viewPagerBackupInfo
) { tab, position ->
//Some implementation
}.attach()
(activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
(activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar)
store.dispatch(OnboardingWalletAction.Init)
toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
binding.toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
store.dispatch(OnboardingWalletAction.LoadArtwork)
@ -81,16 +88,18 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
}
override fun newState(state: OnboardingWalletState) {
if (activity == null) return
if (activity == null || view == null) return
requireActivity().invalidateOptionsMenu()
loadImageIntoImageView(state.cardArtworkUrl, imv_front_card)
loadImageIntoImageView(state.cardArtworkUrl, imv_first_backup_card)
loadImageIntoImageView(state.cardArtworkUrl, imv_second_backup_card)
with(binding) {
loadImageIntoImageView(state.cardArtworkUrl, imvFrontCard)
loadImageIntoImageView(state.cardArtworkUrl, imvFirstBackupCard)
loadImageIntoImageView(state.cardArtworkUrl, imvSecondBackupCard)
}
pb_state.max = 6
pb_state.progress = state.getProgressStep()
pbBinding.pbState.max = 6
pbBinding.pbState.progress = state.getProgressStep()
when (state.step) {
OnboardingWalletStep.None -> {
@ -111,15 +120,15 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
?.into(view)
}
private fun setupCreateWalletState() {
btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet)
btn_main_action.setOnClickListener { store.dispatch(OnboardingWalletAction.CreateWallet) }
btn_alternative_action.hide()
private fun setupCreateWalletState() = with(binding) {
layoutButtonsCommon.btnMainAction.setText(R.string.onboarding_create_wallet_button_create_wallet)
layoutButtonsCommon.btnMainAction.setOnClickListener { store.dispatch(OnboardingWalletAction.CreateWallet) }
layoutButtonsCommon.btnAlternativeAction.hide()
toolbar.title = getText(R.string.onboarding_getting_started)
tv_header.setText(R.string.onboarding_create_wallet_header)
tv_body.setText(R.string.onboarding_create_wallet_body)
tvHeader.setText(R.string.onboarding_create_wallet_header)
tvBody.setText(R.string.onboarding_create_wallet_body)
cardsWidget.toFolded()
startPostponedEnterTransition()
@ -129,7 +138,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
private fun setBackupState(state: BackupState) {
when (state.backupStep) {
BackupStep.InitBackup -> showBackupIntro(state)
BackupStep.ScanOriginCard -> showScanOriginCard(state)
BackupStep.ScanOriginCard -> showScanOriginCard()
BackupStep.AddBackupCards -> showAddBackupCards(state)
BackupStep.SetAccessCode -> showSetAccessCode()
BackupStep.EnterAccessCode -> showEnterAccessCode(state)
@ -140,65 +149,70 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
}
}
private fun showBackupIntro(state: BackupState) {
imv_first_backup_card.show()
imv_second_backup_card.show()
private fun showBackupIntro(state: BackupState) = with(binding) {
imvFirstBackupCard.show()
imvSecondBackupCard.show()
cardsWidget.toWelcome()
tv_header.hide()
tv_body.hide()
view_pager_backup_info.show()
tab_layout_backup_info.show()
tvHeader.hide()
tvBody.hide()
viewPagerBackupInfo.show()
tabLayoutBackupInfo.show()
with(layoutButtonsCommon) {
btnMainAction.text = getText(R.string.onboarding_button_backup_now)
btnAlternativeAction.text = getText(R.string.onboarding_button_skip_backup)
btnAlternativeAction.show(state.canSkipBackup)
btnMainAction.setOnClickListener { store.dispatch(BackupAction.StartBackup) }
btnAlternativeAction.setOnClickListener { store.dispatch(BackupAction.DismissBackup) }
}
btn_main_action.text = getText(R.string.onboarding_button_backup_now)
btn_alternative_action.text = getText(R.string.onboarding_button_skip_backup)
btn_alternative_action.show(state.canSkipBackup)
btn_main_action.setOnClickListener { store.dispatch(BackupAction.StartBackup) }
btn_alternative_action.setOnClickListener { store.dispatch(BackupAction.DismissBackup) }
startPostponedEnterTransition()
}
private fun showScanOriginCard(state: BackupState) {
private fun showScanOriginCard() = with(binding) {
prepareBackupView()
cardsWidget.toFolded()
tv_header.text = getText(R.string.onboarding_title_scan_origin_card)
tv_body.text = getString(
tvHeader.text = getText(R.string.onboarding_title_scan_origin_card)
tvBody.text = getString(
R.string.onboarding_subtitle_scan_origin_card,
)
btn_main_action.text = getString(R.string.onboarding_button_scan_origin_card)
btn_alternative_action.hide()
btn_main_action.setOnClickListener { store.dispatch(BackupAction.ScanPrimaryCard) }
with(layoutButtonsCommon) {
btnMainAction.text = getString(R.string.onboarding_button_scan_origin_card)
btnAlternativeAction.hide()
btnMainAction.setOnClickListener { store.dispatch(BackupAction.ScanPrimaryCard) }
}
}
private fun prepareBackupView() {
private fun prepareBackupView() = with(binding) {
toolbar.title = getText(R.string.onboarding_navbar_title_creating_backup)
tv_header.show()
tv_body.show()
view_pager_backup_info.hide()
tab_layout_backup_info.hide()
tvHeader.show()
tvBody.show()
viewPagerBackupInfo.hide()
tabLayoutBackupInfo.hide()
imv_first_backup_card.show()
imv_second_backup_card.show()
imvSecondBackupCard.show()
imvSecondBackupCard.show()
}
private fun showAddBackupCards(state: BackupState) {
private fun showAddBackupCards(state: BackupState) = with(binding) {
prepareBackupView()
accessCodeDialog?.dismiss()
accessCodeDialog = null
layout_buttons_add_cards.show()
layout_buttons_common.hide()
btn_add_card.text = getText(R.string.onboarding_button_add_backup_card)
layoutButtonsAddCards.root.show()
layoutButtonsCommon.root.hide()
layoutButtonsAddCards.btnAddCard.text = getText(R.string.onboarding_button_add_backup_card)
if (state.backupCardsNumber < state.maxBackupCards) {
btn_add_card.setOnClickListener { store.dispatch(BackupAction.AddBackupCard) }
layoutButtonsAddCards.btnAddCard.setOnClickListener { store.dispatch(BackupAction.AddBackupCard) }
} else {
btn_add_card.isEnabled = false
layoutButtonsAddCards.btnAddCard.isEnabled = false
}
when (state.backupCardsNumber) {
@ -207,27 +221,27 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
cardsWidget.getFirstBackupCardView().animate().alpha(0.6f).setDuration(200)
cardsWidget.getSecondBackupCardView().animate().alpha(0.2f).setDuration(200)
}
tv_header.text = getText(R.string.onboarding_title_no_backup_cards)
tv_body.text = getText(R.string.onboarding_subtitle_no_backup_cards)
tvHeader.text = getText(R.string.onboarding_title_no_backup_cards)
tvBody.text = getText(R.string.onboarding_subtitle_no_backup_cards)
}
1 -> {
tv_header.text = getText(R.string.onboarding_title_one_backup_card)
tv_body.text = getText(R.string.onboarding_subtitle_one_backup_card)
tvHeader.text = getText(R.string.onboarding_title_one_backup_card)
tvBody.text = getText(R.string.onboarding_subtitle_one_backup_card)
cardsWidget.getFirstBackupCardView().animate().alpha(1f).setDuration(400)
cardsWidget.getSecondBackupCardView().alpha = 0.2f
}
2 -> {
tv_header.text = getText(R.string.onboarding_title_two_backup_cards)
tv_body.text = getText(R.string.onboarding_subtitle_two_backup_cards)
tvHeader.text = getText(R.string.onboarding_title_two_backup_cards)
tvBody.text = getText(R.string.onboarding_subtitle_two_backup_cards)
cardsWidget.getFirstBackupCardView().alpha = 1f
cardsWidget.getSecondBackupCardView().animate().alpha(1f).setDuration(400)
}
}
btn_continue.text = getText(R.string.onboarding_button_finalize_backup)
btn_continue.setOnClickListener { store.dispatch(BackupAction.FinishAddingBackupCards) }
btn_continue.isEnabled = state.backupCardsNumber != 0
layoutButtonsAddCards.btnContinue.text = getText(R.string.onboarding_button_finalize_backup)
layoutButtonsAddCards.btnContinue.setOnClickListener { store.dispatch(BackupAction.FinishAddingBackupCards) }
layoutButtonsAddCards.btnContinue.isEnabled = state.backupCardsNumber != 0
}
private fun showSetAccessCode() {
@ -258,7 +272,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
accessCodeDialog?.showError(state.accessCodeError)
}
private fun showWritePrimaryCard(state: BackupState) {
private fun showWritePrimaryCard(state: BackupState) = with(binding) {
accessCodeDialog?.dismiss()
prepareViewForFinalizeStep()
@ -270,33 +284,33 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
}
val cardIdFormatter = CardIdFormatter(CardIdDisplayFormat.LastMasked(4))
tv_header.text = getText(R.string.onboarding_title_prepare_origin)
tv_body.text = getString(
tvHeader.text = getText(R.string.onboarding_title_prepare_origin)
tvBody.text = getString(
R.string.onboarding_subtitle_scan_primary_card_format,
state.primaryCardId?.let { cardIdFormatter.getFormattedCardId(it) }
)
btn_main_action.text = getText(R.string.onboarding_button_backup_origin)
btn_main_action.setOnClickListener { store.dispatch(BackupAction.WritePrimaryCard) }
layoutButtonsCommon.btnMainAction.text = getText(R.string.onboarding_button_backup_origin)
layoutButtonsCommon.btnMainAction.setOnClickListener { store.dispatch(BackupAction.WritePrimaryCard) }
}
private fun prepareViewForFinalizeStep() {
layout_buttons_add_cards.hide()
layout_buttons_common.show()
private fun prepareViewForFinalizeStep() = with(binding) {
layoutButtonsAddCards.root.hide()
layoutButtonsCommon.root.show()
imv_first_backup_card.show()
imv_second_backup_card.show()
imvFirstBackupCard.show()
imvSecondBackupCard.show()
toolbar.title = getText(R.string.onboarding_button_finalize_backup)
imv_card_background.hide()
imvCardBackground.hide()
cardsWidget.toLeapfrog()
btn_alternative_action.hide()
layoutButtonsCommon.btnAlternativeAction.hide()
}
private fun showWriteBackupCard(state: BackupState) {
private fun showWriteBackupCard(state: BackupState) = with(binding) {
prepareViewForFinalizeStep()
cardsWidget.getSecondBackupCardView().show(state.backupCardsNumber == 2)
@ -316,46 +330,47 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
}
val cardIdFormatter = CardIdFormatter(CardIdDisplayFormat.LastMasked(4))
tv_header.text =
tvHeader.text =
getString(R.string.onboarding_title_backup_card_format, cardNumber)
tv_body.text = getString(
tvBody.text = getString(
R.string.onboarding_subtitle_scan_backup_card_format,
cardIdFormatter.getFormattedCardId(state.backupCardIds[cardNumber - 1])
)
btn_main_action.text = getString(
layoutButtonsCommon.btnMainAction.text = getString(
R.string.onboarding_button_backup_card_format,
cardNumber
)
btn_main_action.setOnClickListener { store.dispatch(BackupAction.WriteBackupCard(cardNumber)) }
layoutButtonsCommon.btnMainAction.setOnClickListener {
store.dispatch(BackupAction.WriteBackupCard(cardNumber))
}
}
private fun showSuccess() {
tv_header.text = getText(R.string.onboarding_done_header)
private fun showSuccess() = with(binding) {
tvHeader.text = getText(R.string.onboarding_done_header)
tv_header.show()
tv_body.show()
view_pager_backup_info.hide()
tab_layout_backup_info.hide()
tvHeader.show()
tvBody.show()
viewPagerBackupInfo.hide()
tabLayoutBackupInfo.hide()
tv_body.text = getText(R.string.onboarding_subtitle_success_tangem_wallet_onboarding)
btn_main_action.text = getText(R.string.onboarding_button_continue_wallet)
btn_alternative_action.hide()
btn_main_action.setOnClickListener {
lav_confetti.cancelAnimation()
lav_confetti.hide()
tvBody.text = getText(R.string.onboarding_subtitle_success_tangem_wallet_onboarding)
layoutButtonsCommon.btnMainAction.text = getText(R.string.onboarding_button_continue_wallet)
layoutButtonsCommon.btnAlternativeAction.hide()
layoutButtonsCommon.btnMainAction.setOnClickListener {
vConfetti.lavConfetti.cancelAnimation()
vConfetti.lavConfetti.hide()
store.dispatch(OnboardingWalletAction.FinishOnboarding)
}
cardsWidget.leapfrogWidget.fold {
fl_cards_container?.hide()
imv_card_background?.hide()
lav_confetti?.show()
lav_confetti?.playAnimation()
imv_success?.alpha = 0f
imv_success?.show()
flCardsContainer.hide()
imvCardBackground.hide()
vConfetti.lavConfetti.show()
vConfetti.lavConfetti.playAnimation()
imvSuccess.alpha = 0f
imvSuccess.show()
imv_success?.animate()
imvSuccess.animate()
?.alpha(1f)
?.setDuration(400)
}

View file

@ -1,8 +1,9 @@
package com.tangem.tap.features.onboarding.products.wallet.ui.dialogs
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tangem.tap.common.extensions.hide
@ -11,64 +12,69 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.AccessCodeError
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.layout_backup_access_code.*
import kotlinx.android.synthetic.main.layout_backup_access_code_info.*
import kotlinx.android.synthetic.main.layout_backup_access_code_submit.*
import com.tangem.wallet.databinding.LayoutBackupAccessCodeBinding
class AccessCodeDialog(context: Context) : BottomSheetDialog(context) {
var binding: LayoutBackupAccessCodeBinding? = null
init {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
val dialogView = LayoutInflater.from(context).inflate(R.layout.layout_backup_access_code, null)
setContentView(dialogView)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = LayoutBackupAccessCodeBinding
.inflate(LayoutInflater.from(context))
setContentView(binding!!.root)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
override fun setContentView(view: View) {
super.setContentView(view)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
override fun setOnDismissListener(listener: DialogInterface.OnDismissListener?) {
super.setOnDismissListener(listener)
binding = null
}
fun showInfoScreen() {
fun showInfoScreen() = with(binding!!) {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
access_code_title.text = context.getText(R.string.onboarding_access_code_intro_title)
layout_backup_access_code_info.show()
layout_backup_access_code_submit.hide()
btn_access_code_create.setOnClickListener {
accessCodeTitle.text = context.getText(R.string.onboarding_access_code_intro_title)
layoutBackupAccessCodeInfo.root.show()
layoutBackupAccessCodeSubmit.root.hide()
layoutBackupAccessCodeInfo.btnAccessCodeCreate.setOnClickListener {
store.dispatch(BackupAction.ShowEnterAccessCodeScreen)
}
}
fun showEnterAccessCode() {
layout_backup_access_code_info.hide()
layout_backup_access_code_submit.show()
access_code_title.text = context.getText(R.string.onboarding_access_code_intro_title)
btn_access_code_submit.text = context.getText(R.string.common_continue)
btn_access_code_submit.setOnClickListener {
store.dispatch(BackupAction.CheckAccessCode(et_access_code.text.toString()))
fun showEnterAccessCode() = with(binding!!) {
layoutBackupAccessCodeInfo.root.hide()
layoutBackupAccessCodeSubmit.root.show()
accessCodeTitle.text = context.getText(R.string.onboarding_access_code_intro_title)
with(layoutBackupAccessCodeSubmit) {
btnAccessCodeSubmit.text = context.getText(R.string.common_continue)
btnAccessCodeSubmit.setOnClickListener {
store.dispatch(BackupAction.CheckAccessCode(etAccessCode.text.toString()))
}
}
}
fun showReenterAccessCode() {
layout_backup_access_code_info.hide()
layout_backup_access_code_submit.show()
access_code_title.text = context.getText(R.string.onboarding_access_code_repeat_code_title)
et_access_code.setText("")
btn_access_code_submit.text = context.getText(R.string.common_submit)
btn_access_code_submit.setOnClickListener {
store.dispatch(BackupAction.SaveAccessCodeConfirmation(et_access_code.text.toString()))
fun showReenterAccessCode() = with(binding!!) {
layoutBackupAccessCodeInfo.root.hide()
layoutBackupAccessCodeSubmit.root.show()
accessCodeTitle.text = context.getText(R.string.onboarding_access_code_repeat_code_title)
with(layoutBackupAccessCodeSubmit) {
etAccessCode.setText("")
btnAccessCodeSubmit.text = context.getText(R.string.common_submit)
btnAccessCodeSubmit.setOnClickListener {
store.dispatch(BackupAction.SaveAccessCodeConfirmation(etAccessCode.text.toString()))
}
}
}
fun showError(error: AccessCodeError?) {
fun showError(error: AccessCodeError?) = with(binding!!.layoutBackupAccessCodeSubmit) {
when (error) {
AccessCodeError.CodeTooShort ->
til_access_code.error = context.getString(R.string.onboarding_access_code_too_short)
tilAccessCode.error = context.getString(R.string.onboarding_access_code_too_short)
AccessCodeError.CodesDoNotMatch ->
til_access_code.error = context.getString(R.string.onboarding_access_codes_doesnt_match)
null -> til_access_code.error = null
tilAccessCode.error = context.getString(R.string.onboarding_access_codes_doesnt_match)
null -> tilAccessCode.error = null
}
}
}

View file

@ -11,6 +11,7 @@ import androidx.core.view.postDelayed
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.textfield.TextInputEditText
import com.tangem.Message
import com.tangem.tangem_sdk_new.extensions.dpToPx
@ -40,10 +41,7 @@ import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
import com.tangem.tap.mainScope
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_send.*
import kotlinx.android.synthetic.main.layout_send_address_payid.*
import kotlinx.android.synthetic.main.layout_send_amount.*
import kotlinx.android.synthetic.main.layout_send_fee.*
import com.tangem.wallet.databinding.FragmentSendBinding
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
@ -62,6 +60,8 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
private val sendSubscriber = SendStateSubscriber(this)
private lateinit var keyboardObserver: KeyboardObserver
val binding: FragmentSendBinding by viewBinding(FragmentSendBinding::bind)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addBackPressHandler(this)
@ -77,7 +77,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
store.dispatch(SendActionUi.CheckIfTransactionDataWasProvided)
}
private fun initSendButtonStates() {
private fun initSendButtonStates() = with(binding) {
btnSend.setOnClickListener {
store.dispatch(SendActionUi.SendAmountToRecipient(
Message(getString(R.string.initial_message_sign_header))
@ -86,7 +86,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
sendBtn = IndeterminateProgressButtonWidget(btnSend, progress)
}
private fun setupAddressOrPayIdLayout() {
private fun setupAddressOrPayIdLayout() = with(binding.lSendAddressPayid) {
store.dispatch(SetTruncateHandler { etAddressOrPayId.truncateMiddleWith(it, "...") })
store.dispatch(CheckClipboard(requireContext().getFromClipboard()?.toString()))
@ -113,7 +113,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
}
private fun setupTransactionExtrasLayout() {
private fun setupTransactionExtrasLayout() = with(binding.lSendAddressPayid) {
etXlmMemo.inputtedTextAsFlow()
.debounce(400)
.filter {
@ -151,9 +151,9 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
// Delayed launch is needed in order for the UI to be drawn and to process the sent events.
// If do not use the delay, then etAmount error field is not displayed when
// inserting an incorrect amount by shareUri
imvQrCode.postDelayed({
binding.lSendAddressPayid.imvQrCode.postDelayed({
store.dispatch(PasteAddressPayId(scannedCode))
store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused))
store.dispatch(TruncateOrRestore(!binding.lSendAddressPayid.etAddressOrPayId.isFocused))
}, 200)
}
@ -165,7 +165,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
val decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString()
store.dispatch(AmountAction.SetDecimalSeparator(decimalSeparator))
tvAmountCurrency.setOnClickListener {
binding.lSendAmount.tvAmountCurrency.setOnClickListener {
store.dispatch(ToggleMainCurrency)
store.dispatch(ReceiptAction.RefreshReceipt)
store.dispatch(SendAction.ChangeSendButtonState(store.state.sendState.getButtonState()))
@ -223,8 +223,8 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
}
private fun setupFeeLayout() {
flExpandCollapse.setOnClickListener {
private fun setupFeeLayout() = with(binding.clNetworkFee) {
flExpandCollapse.flExpandCollapse.setOnClickListener {
store.dispatch(ToggleControlsVisibility)
}
chipGroup.check(FeeUiHelper.toId(FeeType.NORMAL))
@ -240,12 +240,12 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
}
private fun setupWarningMessages() {
private fun setupWarningMessages() = with(binding) {
warningsAdapter = WarningMessagesAdapter()
val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
rv_warning_messages.layoutManager = layoutManager
rv_warning_messages.addItemDecoration(SpacesItemDecoration(rv_warning_messages.dpToPx(16f).toInt()))
rv_warning_messages.adapter = warningsAdapter
rvWarningMessages.layoutManager = layoutManager
rvWarningMessages.addItemDecoration(SpacesItemDecoration(rvWarningMessages.dpToPx(16f).toInt()))
rvWarningMessages.adapter = warningsAdapter
store.dispatch(SendAction.Warnings.Update)
}

View file

@ -26,32 +26,30 @@ import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog
import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.btn_expand_collapse.*
import kotlinx.android.synthetic.main.fragment_send.*
import kotlinx.android.synthetic.main.fragment_send.clReceiptContainer
import kotlinx.android.synthetic.main.layout_receipt_total.*
import kotlinx.android.synthetic.main.layout_receipt_total.view.*
import kotlinx.android.synthetic.main.layout_send_address_payid.*
import kotlinx.android.synthetic.main.layout_send_amount.*
import kotlinx.android.synthetic.main.layout_send_fee.*
import kotlinx.android.synthetic.main.layout_send_receipt.*
/**
[REDACTED_AUTHOR]
*/
class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber<SendState>(fragment) {
class SendStateSubscriber(fragment: BaseStoreFragment) :
FragmentStateSubscriber<SendState>(fragment) {
private var dialog: Dialog? = null
override fun updateWithNewState(fg: BaseStoreFragment, state: SendState) {
fg.view ?: return
if (fg !is SendFragment) return
val lastChangedStates = state.lastChangedStates.toList()
state.lastChangedStates.clear()
fg.main_send_container.beginDelayedTransition()
(fg as? SendFragment)?.binding?.mainSendContainer?.beginDelayedTransition()
lastChangedStates.forEach {
when (it) {
StateId.SEND_SCREEN -> handleSendScreen(fg, state)
StateId.ADDRESS_PAY_ID -> handleAddressPayIdState(fg, state.addressPayIdState)
StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(fg, state.transactionExtrasState)
StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(
fg,
state.transactionExtrasState
)
StateId.AMOUNT -> handleAmountState(fg, state.amountState)
StateId.FEE -> handleFeeState(fg, state.feeState)
StateId.RECEIPT -> handleReceiptState(fg, state.receiptState)
@ -59,57 +57,58 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
}
}
private fun handleTransactionExtrasState(fg: BaseStoreFragment, infoState: TransactionExtrasState) {
fun showView(view: View, info: Any?) {
view.show(info != null)
}
showView(fg.xlmMemoContainer, infoState.xlmMemo)
showView(fg.xrpDestinationTagContainer, infoState.xrpDestinationTag)
showView(fg.binanceMemoContainer, infoState.binanceMemo)
private fun handleTransactionExtrasState(fg: SendFragment, infoState: TransactionExtrasState) =
with(fg.binding.lSendAddressPayid) {
infoState.xlmMemo?.let {
fg.etXlmMemo.inputType = when (it.selectedMemoType) {
XlmMemoType.TEXT -> InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
XlmMemoType.ID -> InputType.TYPE_CLASS_NUMBER
fun showView(view: View, info: Any?) {
view.show(info != null)
}
if (!it.viewFieldValue.isFromUserInput) fg.etXlmMemo.setText(it.viewFieldValue.value)
if (it.error != null) {
if (it.error == TransactionExtraError.INVALID_XLM_MEMO) {
fg.tilXlmMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
}
} else {
fg.tilXlmMemo.error = null
}
}
infoState.xrpDestinationTag?.let {
if (infoState.xrpDestinationTag.error != null) {
if (infoState.xrpDestinationTag.error == TransactionExtraError.INVALID_DESTINATION_TAG) {
fg.tilDestinationTag.error = fg.getText(R.string.send_error_invalid_destination_tag)
}
} else {
fg.tilDestinationTag.error = null
}
if (!it.viewFieldValue.isFromUserInput) {
fg.etDestinationTag.setText(it.viewFieldValue.value)
}
}
infoState.binanceMemo?.let {
if (infoState.binanceMemo.error != null) {
if (infoState.binanceMemo.error == TransactionExtraError.INVALID_BINANCE_MEMO) {
fg.tilBinanceMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
}
} else {
fg.tilBinanceMemo.error = null
}
if (!it.viewFieldValue.isFromUserInput) {
fg.etBinanceMemo.setText(it.viewFieldValue.value)
}
}
}
showView(xlmMemoContainer, infoState.xlmMemo)
showView(xrpDestinationTagContainer, infoState.xrpDestinationTag)
showView(binanceMemoContainer, infoState.binanceMemo)
private fun handleSendScreen(fg: BaseStoreFragment, state: SendState) {
val sendFragment = (fg as? SendFragment) ?: return
infoState.xlmMemo?.let {
etXlmMemo.inputType = when (it.selectedMemoType) {
XlmMemoType.TEXT -> InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
XlmMemoType.ID -> InputType.TYPE_CLASS_NUMBER
}
if (!it.viewFieldValue.isFromUserInput) etXlmMemo.setText(it.viewFieldValue.value)
if (it.error != null) {
if (it.error == TransactionExtraError.INVALID_XLM_MEMO) {
tilXlmMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
}
} else {
tilXlmMemo.error = null
}
}
infoState.xrpDestinationTag?.let {
if (infoState.xrpDestinationTag.error != null) {
if (infoState.xrpDestinationTag.error == TransactionExtraError.INVALID_DESTINATION_TAG) {
tilDestinationTag.error =
fg.getText(R.string.send_error_invalid_destination_tag)
}
} else {
tilDestinationTag.error = null
}
if (!it.viewFieldValue.isFromUserInput) {
etDestinationTag.setText(it.viewFieldValue.value)
}
}
infoState.binanceMemo?.let {
if (infoState.binanceMemo.error != null) {
if (infoState.binanceMemo.error == TransactionExtraError.INVALID_BINANCE_MEMO) {
tilBinanceMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
}
} else {
tilBinanceMemo.error = null
}
if (!it.viewFieldValue.isFromUserInput) {
etBinanceMemo.setText(it.viewFieldValue.value)
}
}
}
private fun handleSendScreen(fg: SendFragment, state: SendState) = with(fg.binding) {
when (state.dialog) {
is SendAction.Dialog.TezosWarningDialog -> {
if (dialog == null) {
@ -129,147 +128,158 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
}
}
sendFragment.sendBtn.changeState(state.sendButtonState.progressState)
sendFragment.sendBtn.mainView.isEnabled = state.sendButtonState.enabled
fg.sendBtn.changeState(state.sendButtonState.progressState)
fg.sendBtn.mainView.isEnabled = state.sendButtonState.enabled
val rv = fg.rv_warning_messages
val rv = rvWarningMessages
val adapter = rv.adapter as? WarningMessagesAdapter ?: return
adapter.submitList(state.sendWarningsList)
rv.show(state.sendWarningsList.isNotEmpty())
fg.toolbar.title = fg.getString(
toolbar.title = fg.getString(
R.string.send_title_currency_format,
state.amountState.mainCurrency.currencySymbol
)
}
private fun handleAddressPayIdState(fg: BaseStoreFragment, state: AddressPayIdState) {
fun parseError(context: Context, error: Error?): String? {
val resId = when (error) {
Error.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.send_error_payid_unsupported_by_blockchain
Error.PAY_ID_NOT_REGISTERED -> R.string.send_error_payid_not_registered
Error.PAY_ID_REQUEST_FAILED -> R.string.send_error_payid_request_failed
Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.send_validation_invalid_address
Error.ADDRESS_SAME_AS_WALLET -> R.string.send_error_address_same_as_wallet
else -> null
private fun handleAddressPayIdState(fg: SendFragment, state: AddressPayIdState) =
with(fg.binding.lSendAddressPayid) {
fun parseError(context: Context, error: Error?): String? {
val resId = when (error) {
Error.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.send_error_payid_unsupported_by_blockchain
Error.PAY_ID_NOT_REGISTERED -> R.string.send_error_payid_not_registered
Error.PAY_ID_REQUEST_FAILED -> R.string.send_error_payid_request_failed
Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.send_validation_invalid_address
Error.ADDRESS_SAME_AS_WALLET -> R.string.send_error_address_same_as_wallet
else -> null
}
return if (resId == null) null else context.getString(resId, "", "")
}
return if (resId == null) null else context.getString(resId, "", "")
imvPaste.isEnabled = state.pasteIsEnabled
val et = etAddressOrPayId
val til = tilAddressOrPayId
val parsedError = parseError(til.context, state.error)
til.isEnabled = state.inputIsEnabled
imvPaste.show(state.inputIsEnabled)
imvQrCode.show(state.inputIsEnabled)
flPaste.show(state.inputIsEnabled)
flQrCode.show(state.inputIsEnabled)
val hintResId = if (state.sendingToPayIdEnabled) {
R.string.send_destination_hint_address_payid
} else {
R.string.send_destination_hint_address
}
til.hint = til.getString(hintResId)
til.error = parsedError
til.isErrorEnabled = parsedError != null
til.helperText = state.destinationWalletAddress
til.isHelperTextEnabled = state.isPayIdState() && parsedError == null
if (!state.viewFieldValue.isFromUserInput) et.update(state.viewFieldValue.value)
}
fg.imvPaste.isEnabled = state.pasteIsEnabled
val et = fg.etAddressOrPayId
val til = fg.tilAddressOrPayId
val parsedError = parseError(til.context, state.error)
til.isEnabled = state.inputIsEnabled
fg.imvPaste.show(state.inputIsEnabled)
fg.imvQrCode.show(state.inputIsEnabled)
fg.flPaste.show(state.inputIsEnabled)
fg.flQrCode.show(state.inputIsEnabled)
val hintResId = if (state.sendingToPayIdEnabled) {
R.string.send_destination_hint_address_payid
} else {
R.string.send_destination_hint_address
}
til.hint = til.getString(hintResId)
til.error = parsedError
til.isErrorEnabled = parsedError != null
til.helperText = state.destinationWalletAddress
til.isHelperTextEnabled = state.isPayIdState() && parsedError == null
if (!state.viewFieldValue.isFromUserInput) et.update(state.viewFieldValue.value)
}
private fun handleAmountState(fg: BaseStoreFragment, state: AmountState) {
private fun handleAmountState(fg: SendFragment, state: AmountState) = with(fg.binding.lSendAmount) {
if (state.error != null) {
val context = fg.requireContext()
val message = when (state.error) {
is MultiMessageError -> {
val multiError = state.error as MultiMessageError
val messageList = multiError.assembleErrors().map { getMessageString(context, it.first, it.second) }
val messageList = multiError.assembleErrors()
.map { getMessageString(context, it.first, it.second) }
multiError.builder(messageList)
}
else -> context.getString(state.error.messageResource)
}
fg.tilAmountToSend.enableError(true, message)
tilAmountToSend.enableError(true, message)
} else {
fg.tilAmountToSend.enableError(false)
tilAmountToSend.enableError(false)
}
val filter = DecimalDigitsInputFilter(12, state.maxLengthOfAmount, state.decimalSeparator)
fg.etAmountToSend.filters = arrayOf(filter)
etAmountToSend.filters = arrayOf(filter)
val amountToSend = state.viewAmountValue
if (!amountToSend.isFromUserInput) fg.etAmountToSend.update(amountToSend.value)
if (!amountToSend.isFromUserInput) etAmountToSend.update(amountToSend.value)
fg.tvAmountCurrency.update(state.mainCurrency.currencySymbol)
tvAmountCurrency.update(state.mainCurrency.currencySymbol)
(fg as? SendFragment)?.saveMainCurrency(state.mainCurrency.type)
val balanceText = when (state.mainCurrency.type) {
MainCurrencyType.FIAT -> fg.getString(R.string.send_balance_subtitle_format,
state.viewBalanceValue, state.mainCurrency.currencySymbol).remove(":")
MainCurrencyType.CRYPTO -> fg.getString(R.string.send_balance_subtitle_format,
state.mainCurrency.currencySymbol, state.viewBalanceValue)
MainCurrencyType.FIAT -> fg.getString(
R.string.send_balance_subtitle_format,
state.viewBalanceValue, state.mainCurrency.currencySymbol
).remove(":")
MainCurrencyType.CRYPTO -> fg.getString(
R.string.send_balance_subtitle_format,
state.mainCurrency.currencySymbol, state.viewBalanceValue
)
}
fg.tvBalance.update(balanceText)
tvBalance.update(balanceText)
fg.tilAmountToSend.isEnabled = state.inputIsEnabled
tilAmountToSend.isEnabled = state.inputIsEnabled
val imageRes = if (state.inputIsEnabled) R.drawable.ic_arrows_up_down else 0
fg.tvAmountCurrency.setCompoundDrawablesWithIntrinsicBounds(0, 0, imageRes, 0)
tvAmountCurrency.setCompoundDrawablesWithIntrinsicBounds(0, 0, imageRes, 0)
val textColor = if (state.inputIsEnabled) R.color.blue else R.color.textGray
fg.tvAmountCurrency.setTextColor(fg.getColor(textColor))
tvAmountCurrency.setTextColor(fg.getColor(textColor))
}
private fun handleFeeState(fg: BaseStoreFragment, state: FeeState) {
fg.chipGroup.fitChipsByGroupWidth()
private fun handleFeeState(fg: SendFragment, state: FeeState) = with(fg.binding.clNetworkFee) {
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)
chipGroup.show(state.feeChipGroupIsVisible)
fg.imvExpandCollapse.rotation = if (state.controlsLayoutIsVisible) 0f else 180f
fg.llFeeControlsContainer.show(state.controlsLayoutIsVisible)
fg.chipGroup.show(state.feeChipGroupIsVisible)
fg.swIncludeFee.isEnabled = state.includeFeeSwitcherIsEnabled
if (fg.swIncludeFee.isChecked != state.feeIsIncluded) {
fg.swIncludeFee.isChecked = state.feeIsIncluded
swIncludeFee.isEnabled = state.includeFeeSwitcherIsEnabled
if (swIncludeFee.isChecked != state.feeIsIncluded) {
swIncludeFee.isChecked = state.feeIsIncluded
}
if (state.error == FeeAction.Error.REQUEST_FAILED) {
fg.showRetrySnackbar(fg.requireContext().getString(R.string.send_error_fee_request_failed)) {
fg.showRetrySnackbar(
fg.requireContext().getString(R.string.send_error_fee_request_failed)
) {
store.dispatch(FeeAction.RequestFee)
}
}
val chipId = FeeUiHelper.toId(state.selectedFeeType)
if (fg.chipGroup.checkedChipId != chipId && chipId != View.NO_ID) fg.chipGroup.check(chipId)
if (chipGroup.checkedChipId != chipId && chipId != View.NO_ID) chipGroup.check(chipId)
}
private fun handleReceiptState(fg: BaseStoreFragment, state: ReceiptState) {
val mainLayout = fg.clReceiptContainer as ViewGroup
val totalLayout = fg.llTotal as ViewGroup
val totalTokenLayout = fg.flTotalTokenCrypto as ViewGroup
private fun handleReceiptState(fg: SendFragment, state: ReceiptState) = with(fg.binding.clReceiptContainer) {
val mainLayout = clReceiptContainer as ViewGroup
val totalLayout = llTotalContainer.llTotal as ViewGroup
val totalTokenLayout = llTotalContainer.flTotalTokenCrypto as ViewGroup
fun getString(id: Int, vararg formatStrings: String): String =
mainLayout.context.getString(id, *formatStrings)
mainLayout.context.getString(id, *formatStrings)
val rough = getString(R.string.sign_rough)
fun roughOrEmpty(value: String): String = if (value == ReceiptReducer.EMPTY) value else "$rough $value"
fun roughOrEmpty(value: String): String =
if (value == ReceiptReducer.EMPTY) value else "$rough $value"
when (state.visibleTypeOfReceipt) {
ReceiptLayoutType.FIAT -> {
val receipt = state.fiat ?: return
llTotalContainer.tvTotalValue
totalLayout.show(true)
totalTokenLayout.show(false)
fg.tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}")
fg.tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}")
totalLayout.tvTotalValue.update("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}")
tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}")
tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}")
llTotalContainer.tvTotalValue.update("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}")
val willSent = getString(R.string.send_total_subtitle_format,
receipt.willSentCrypto, receipt.symbols.crypto)
totalLayout.tvWillBeSentValue.update(willSent)
val willSent = getString(
R.string.send_total_subtitle_format,
receipt.willSentCrypto, receipt.symbols.crypto
)
llTotalContainer.tvWillBeSentValue.update(willSent)
}
ReceiptLayoutType.CRYPTO -> {
@ -277,34 +287,34 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
totalLayout.show(true)
totalTokenLayout.show(false)
fg.tvReceiptAmountValue.update("${receipt.amountCrypto} ${receipt.symbols.crypto}")
fg.tvReceiptFeeValue.update("${receipt.feeCrypto} ${receipt.symbols.crypto}")
totalLayout.tvTotalValue.update("${receipt.totalCrypto} ${receipt.symbols.crypto}")
tvReceiptAmountValue.update("${receipt.amountCrypto} ${receipt.symbols.crypto}")
tvReceiptFeeValue.update("${receipt.feeCrypto} ${receipt.symbols.crypto}")
llTotalContainer.tvTotalValue.update("${receipt.totalCrypto} ${receipt.symbols.crypto}")
val willSent = SpannableStringBuilder()
.bold {
append(roughOrEmpty(receipt.willSentFiat)).append(" ")
append(receipt.symbols.fiat)
append(" (fee: ${receipt.feeFiat} ")
append(receipt.symbols.fiat).append(")")
}
totalLayout.tvWillBeSentValue.update(willSent.toString())
.bold {
append(roughOrEmpty(receipt.willSentFiat)).append(" ")
append(receipt.symbols.fiat)
append(" (fee: ${receipt.feeFiat} ")
append(receipt.symbols.fiat).append(")")
}
llTotalContainer.tvWillBeSentValue.update(willSent.toString())
}
ReceiptLayoutType.TOKEN_FIAT -> {
val receipt = state.tokenFiat ?: return
totalLayout.show(true)
totalTokenLayout.show(false)
fg.tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}")
fg.tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}")
totalLayout.tvTotalValue.update("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}")
tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}")
tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}")
llTotalContainer.tvTotalValue.update("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}")
val willSent = getString(
R.string.send_total_subtitle_asset_format,
receipt.symbols.token ?: "", receipt.willSentToken,
receipt.symbols.crypto, receipt.willSentFeeCoin
)
totalLayout.tvWillBeSentValue.update(willSent)
llTotalContainer.tvWillBeSentValue.update(willSent)
}
ReceiptLayoutType.TOKEN_CRYPTO -> {
val receipt = state.tokenCrypto ?: return
@ -312,15 +322,15 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
totalLayout.show(false)
totalTokenLayout.show(true)
fg.tvReceiptAmountValue.update("${receipt.amountToken} ${receipt.symbols.token}")
fg.tvReceiptFeeValue.update("${receipt.feeCoin} ${receipt.symbols.crypto}")
tvReceiptAmountValue.update("${receipt.amountToken} ${receipt.symbols.token}")
tvReceiptFeeValue.update("${receipt.feeCoin} ${receipt.symbols.crypto}")
val willSent = SpannableStringBuilder()
.bold {
append(roughOrEmpty(receipt.totalFiat)).append(" ")
append(receipt.symbols.fiat)
}
totalTokenLayout.tvTotalTokenCryptoValue.update(willSent.toString())
.bold {
append(roughOrEmpty(receipt.totalFiat)).append(" ")
append(receipt.symbols.fiat)
}
llTotalContainer.tvTotalTokenCryptoValue.update(willSent.toString())
}
}
}

View file

@ -9,6 +9,7 @@ import android.view.View.OnFocusChangeListener
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import androidx.activity.OnBackPressedCallback
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.tap.common.KeyboardObserver
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.navigation.NavigationAction
@ -18,12 +19,13 @@ import com.tangem.tap.features.shop.redux.ShopAction
import com.tangem.tap.features.shop.redux.ShopState
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_shop.*
import com.tangem.wallet.databinding.FragmentShopBinding
import org.rekotlin.StoreSubscriber
class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber<ShopState> {
private val binding: FragmentShopBinding by viewBinding(FragmentShopBinding::bind)
override fun subscribeToStore() {
store.subscribe(this) { state ->
@ -51,41 +53,41 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber<
setupProductSelection()
setupPromoCodeEditText()
toolbar.setNavigationOnClickListener {
binding.toolbar.setNavigationOnClickListener {
requireActivity().onBackPressed()
}
val keyboardObserver = KeyboardObserver(requireActivity())
keyboardObserver.registerListener { isVisible ->
fl_cards.show(!isVisible)
binding.flCards.show(!isVisible)
}
}
private fun setupCardsImages() {
imv_second.animate()
binding.imvSecond.animate()
.translationY(70f)
.scaleX(0.9f)
.scaleY(0.9f)
.start()
imv_third.animate()
binding.imvThird.animate()
.translationY(140f)
.scaleX(0.8f)
.scaleY(0.8f)
.start()
}
private fun setupProductSelection() {
chip_product_1.setOnCheckedChangeListener { _, isChecked ->
private fun setupProductSelection() = with(binding) {
chipProduct1.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) store.dispatch(ShopAction.SelectProduct(ProductType.WALLET_3_CARDS))
}
chip_product_2.setOnCheckedChangeListener { _, isChecked ->
chipProduct2.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) store.dispatch(ShopAction.SelectProduct(ProductType.WALLET_2_CARDS))
}
}
private fun setupPromoCodeEditText() {
et_promo_code.setOnEditorActionListener { view, actionId, event ->
private fun setupPromoCodeEditText() = with(binding) {
etPromoCode.setOnEditorActionListener { view, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val imm: InputMethodManager =
requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
@ -96,15 +98,15 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber<
return@setOnEditorActionListener false
}
et_promo_code.onFocusChangeListener = OnFocusChangeListener { _, hasFocus ->
etPromoCode.onFocusChangeListener = OnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
store.dispatch(ShopAction.ApplyPromoCode(et_promo_code.text.toString()))
store.dispatch(ShopAction.ApplyPromoCode(etPromoCode.text.toString()))
}
}
}
override fun newState(state: ShopState) {
if (activity == null) return
if (activity == null || view == null) return
animateProductSelection(state.selectedProduct)
handlePriceState(state)
@ -120,43 +122,43 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber<
showOrHideThirdCardWithAnimation(show)
}
private fun showOrHideThirdCardWithAnimation(show: Boolean) {
private fun showOrHideThirdCardWithAnimation(show: Boolean) = with(binding) {
val translationY = if (show) 140f else 0f
if (show) imv_third.show()
imv_third.animate()
if (show) imvThird.show()
imvThird.animate()
.translationY(translationY)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
imv_third?.show(show)
imvThird?.show(show)
}
})
}
private fun handlePriceState(state: ShopState) {
tv_total.text = state.total
tv_total_before_discount.text = state.priceBeforeDiscount
private fun handlePriceState(state: ShopState) = with(binding) {
tvTotal.text = state.total
tvTotalBeforeDiscount.text = state.priceBeforeDiscount
pb_price.show(state.total == null)
pbPrice.show(state.total == null)
}
private fun handlePromoCodeState(state: ShopState) {
if (state.promoCode == null && !et_promo_code.hasFocus()) {
et_promo_code.setText("")
private fun handlePromoCodeState(state: ShopState) = with(binding) {
if (state.promoCode == null && !etPromoCode.hasFocus()) {
etPromoCode.setText("")
}
pb_promo_code.show(state.promoCodeLoading)
pbPromoCode.show(state.promoCodeLoading)
}
private fun handleButtonsState(state: ShopState) {
btn_pay_google_pay.show(state.isGooglePayAvailable)
btn_alternative_payment.show(state.isGooglePayAvailable)
btn_main_action.show(!state.isGooglePayAvailable)
private fun handleButtonsState(state: ShopState) = with(binding) {
btnPayGooglePay.root.show(state.isGooglePayAvailable)
btnAlternativePayment.show(state.isGooglePayAvailable)
btnMainAction.show(!state.isGooglePayAvailable)
if (state.total != null) {
btn_alternative_payment.setOnClickListener { store.dispatch(ShopAction.StartWebCheckout) }
btn_main_action.setOnClickListener { store.dispatch(ShopAction.StartWebCheckout) }
btn_pay_google_pay.setOnClickListener { store.dispatch(ShopAction.BuyWithGooglePay) }
btnAlternativePayment.setOnClickListener { store.dispatch(ShopAction.StartWebCheckout) }
btnMainAction.setOnClickListener { store.dispatch(ShopAction.StartWebCheckout) }
btnPayGooglePay.root.setOnClickListener { store.dispatch(ShopAction.BuyWithGooglePay) }
}
}

View file

@ -11,6 +11,7 @@ import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.tokens.redux.TokensAction
@ -19,7 +20,7 @@ import com.tangem.tap.features.tokens.ui.adapters.CurrenciesAdapter
import com.tangem.tap.mainScope
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_add_tokens.*
import com.tangem.wallet.databinding.FragmentAddTokensBinding
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import org.rekotlin.StoreSubscriber
@ -29,6 +30,7 @@ class AddTokensFragment : Fragment(R.layout.fragment_add_tokens),
StoreSubscriber<TokensState> {
private lateinit var viewAdapter: CurrenciesAdapter
private val binding: FragmentAddTokensBinding by viewBinding(FragmentAddTokensBinding::bind)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -57,29 +59,29 @@ class AddTokensFragment : Fragment(R.layout.fragment_add_tokens),
store.unsubscribe(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) = with(binding) {
super.onViewCreated(view, savedInstanceState)
(activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
setupPopularTokensRecyclerView()
btn_tokens_save_changes.setOnClickListener {
btnTokensSaveChanges.setOnClickListener {
store.dispatch(TokensAction.SaveChanges(viewAdapter.getAddedItems()))
}
}
private fun setupPopularTokensRecyclerView() {
private fun setupPopularTokensRecyclerView() = with(binding) {
viewAdapter = CurrenciesAdapter()
viewAdapter.setOnItemAddListener {
btn_tokens_save_changes.isEnabled = viewAdapter.getAddedItems().isNotEmpty()
btnTokensSaveChanges.isEnabled = viewAdapter.getAddedItems().isNotEmpty()
}
rv_popular_tokens.layoutManager = LinearLayoutManager(context)
rv_popular_tokens.adapter = viewAdapter
rvPopularTokens.layoutManager = LinearLayoutManager(context)
rvPopularTokens.adapter = viewAdapter
}
override fun newState(state: TokensState) {
if (activity == null) return
if (activity == null || view == null) return
viewAdapter.addedCurrencies = state.addedCurrencies
viewAdapter.submitUnfilteredList(state.shownCurrencies)

View file

@ -1,7 +1,6 @@
package com.tangem.tap.features.tokens.ui.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
@ -19,8 +18,8 @@ import com.tangem.tap.features.tokens.redux.CurrencyListItem
import com.tangem.tap.features.tokens.redux.TokensAction
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.item_currency_subtitle.view.*
import kotlinx.android.synthetic.main.item_popular_token.view.*
import com.tangem.wallet.databinding.ItemCurrencySubtitleBinding
import com.tangem.wallet.databinding.ItemPopularTokenBinding
import java.util.*
class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>(DiffUtilCallback) {
@ -61,18 +60,18 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
0 -> TitleViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_currency_subtitle, parent, false)
binding = ItemCurrencySubtitleBinding
.inflate(LayoutInflater.from(parent.context), parent, false)
)
1 -> CurrenciesViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_popular_token, parent, false),
vhOnItemAddListener
binding = ItemPopularTokenBinding
.inflate(LayoutInflater.from(parent.context), parent, false),
onItemAddListener = vhOnItemAddListener
)
else -> CurrenciesViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_popular_token, parent, false),
vhOnItemAddListener
binding = ItemPopularTokenBinding
.inflate(LayoutInflater.from(parent.context), parent, false),
onItemAddListener = vhOnItemAddListener
)
}
}
@ -108,15 +107,15 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
is CurrencyListItem.BlockchainListItem -> {
element.blockchain.currency.toLowerCase(Locale.US)
.contains(queryNormalized) ||
element.blockchain.fullName.toLowerCase(Locale.US)
.contains(queryNormalized)
element.blockchain.fullName.toLowerCase(Locale.US)
.contains(queryNormalized)
}
is CurrencyListItem.TokenListItem -> {
element.token.name.toLowerCase(Locale.US)
.contains(queryNormalized) ||
element.token.symbol.toLowerCase(Locale.US)
.contains(queryNormalized)
element.token.symbol.toLowerCase(Locale.US)
.contains(queryNormalized)
}
is CurrencyListItem.TitleListItem -> true
}
@ -128,9 +127,9 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
}
class CurrenciesViewHolder(
private val view: View,
private val binding: ItemPopularTokenBinding,
private val onItemAddListener: ((CurrencyListItem) -> Unit),
) : RecyclerView.ViewHolder(view) {
) : RecyclerView.ViewHolder(binding.root) {
fun bind(
currency: CurrencyListItem,
@ -140,12 +139,14 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
when (currency) {
is CurrencyListItem.BlockchainListItem -> {
val addedBlockchains = addedCurrencies?.blockchains ?: listOf()
val currentlyAdded = currentlyAddedItems.filterIsInstance<CurrencyListItem.BlockchainListItem>()
val currentlyAdded =
currentlyAddedItems.filterIsInstance<CurrencyListItem.BlockchainListItem>()
bindBlockchain(currency, addedBlockchains, currentlyAdded)
}
is CurrencyListItem.TokenListItem -> {
val addedTokens = addedCurrencies?.tokens ?: listOf()
val currentlyAdded = currentlyAddedItems.filterIsInstance<CurrencyListItem.TokenListItem>()
val currentlyAdded =
currentlyAddedItems.filterIsInstance<CurrencyListItem.TokenListItem>()
bindToken(currency, addedTokens, currentlyAdded)
}
}
@ -155,13 +156,13 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
currency: CurrencyListItem.BlockchainListItem,
addedBlockchains: List<Blockchain>,
currentlyAdded: List<CurrencyListItem.BlockchainListItem>
) {
) = with(binding) {
val blockchain = currency.blockchain
Picasso.get().loadCurrenciesIcon(view.iv_currency, view.tv_token_letter, null, blockchain)
Picasso.get().loadCurrenciesIcon(ivCurrency, tvTokenLetter, null, blockchain)
view.tv_currency_name.text = blockchain.fullName
view.tv_currency_symbol.text = blockchain.currency
view.btn_add_token.setOnClickListener {
tvCurrencyName.text = blockchain.fullName
tvCurrencySymbol.text = blockchain.currency
btnAddToken.setOnClickListener {
onItemAddListener.invoke(currency)
currency.isAdded = true
modifyAddTokenButton(currency)
@ -177,16 +178,17 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
currency: CurrencyListItem.TokenListItem,
addedTokens: List<Token>,
currentlyAdded: List<CurrencyListItem.TokenListItem>
) {
) = with(binding) {
val token = currency.token
Picasso.get().loadCurrenciesIcon(view.iv_currency, view.tv_token_letter, token, token.blockchain)
Picasso.get()
.loadCurrenciesIcon(ivCurrency, tvTokenLetter, token, token.blockchain)
view.tv_currency_name.text = token.name
view.tv_currency_symbol.text = view.context.getString(
tvCurrencyName.text = token.name
tvCurrencySymbol.text = tvCurrencySymbol.getString(
R.string.token_symbol_address_format,
token.symbol, token.contractAddress
)
view.btn_add_token.setOnClickListener {
btnAddToken.setOnClickListener {
onItemAddListener.invoke(currency)
currency.isAdded = true
modifyAddTokenButton(currency)
@ -199,48 +201,51 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
modifyAddTokenButton(currency)
}
private fun modifyAddTokenButton(currency: CurrencyListItem) {
private fun modifyAddTokenButton(currency: CurrencyListItem) = with(binding) {
if (currency.isLocked) {
view.btn_add_token.setText(R.string.common_add)
view.btn_add_token.isEnabled = false
btnAddToken.setText(R.string.common_add)
btnAddToken.isEnabled = false
} else {
val text = if (currency.isAdded) R.string.add_token_added else R.string.common_add
view.btn_add_token.setText(text)
view.btn_add_token.isEnabled = !currency.isAdded
btnAddToken.setText(text)
btnAddToken.isEnabled = !currency.isAdded
}
}
}
class TitleViewHolder(val view: View) :
RecyclerView.ViewHolder(view) {
fun bind(title: CurrencyListItem.TitleListItem) {
view.tv_subtitle.text = view.getString(title.titleResId).uppercase()
class TitleViewHolder(val binding: ItemCurrencySubtitleBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(title: CurrencyListItem.TitleListItem) = with(binding) {
tvSubtitle.text = tvSubtitle.getString(title.titleResId).uppercase()
if (title.blockchain != null) {
view.cl_subtitle_container.setOnClickListener {
clSubtitleContainer.setOnClickListener {
val rotation = if (title.isContentShown) -90f else 90f
store.dispatch(TokensAction.ToggleShowTokensForBlockchain(
title.isContentShown, title.blockchain
))
view.iv_toggle_sublist_visibility.animate().rotation(rotation).withEndAction {
showArrow(view, title.isContentShown)
store.dispatch(
TokensAction.ToggleShowTokensForBlockchain(
title.isContentShown, title.blockchain
)
)
ivToggleSublistVisibility.animate().rotation(rotation).withEndAction {
showArrow(title.isContentShown)
}
}
showArrow(view, title.isContentShown)
showArrow(title.isContentShown)
} else {
view.iv_toggle_sublist_visibility.hide()
ivToggleSublistVisibility.hide()
}
}
private fun showArrow(view: View, isContentShown: Boolean) {
view.iv_toggle_sublist_visibility.show()
private fun showArrow(isContentShown: Boolean) = with(binding) {
ivToggleSublistVisibility.show()
val drawable = if (isContentShown) {
R.drawable.ic_arrow_angle_down
} else {
R.drawable.ic_arrow_angle_right
}
view.iv_toggle_sublist_visibility.setImageResource(drawable)
view.iv_toggle_sublist_visibility.rotation = 0f
ivToggleSublistVisibility.setImageResource(drawable)
ivToggleSublistVisibility.rotation = 0f
}
}
}

View file

@ -1,13 +1,10 @@
package com.tangem.tap.features.wallet.ui
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.card_balance.*
import kotlinx.android.synthetic.main.layout_balance.*
import kotlinx.android.synthetic.main.layout_balance_error.*
import com.tangem.wallet.databinding.CardBalanceBinding
import java.math.BigDecimal
enum class BalanceStatus {
@ -34,30 +31,34 @@ data class BalanceWidgetData(
)
data class TokenData(
val amount: String,
val tokenSymbol: String,
val fiatAmount: String? = null,
val fiatRateString: String? = null,
val fiatRate: BigDecimal? = null,
val amount: String,
val tokenSymbol: String,
val fiatAmount: String? = null,
val fiatRateString: String? = null,
val fiatRate: BigDecimal? = null,
)
class BalanceWidget(
val fragment: Fragment,
val data: BalanceWidgetData,
private val isTwinCard: Boolean,
private val binding: CardBalanceBinding,
private val fragment: WalletFragment,
private val data: BalanceWidgetData,
private val isTwinCard: Boolean,
) {
fun setup() {
when (data.status) {
BalanceStatus.Loading -> {
fragment.l_balance.show()
fragment.l_balance_error.hide()
fragment.tv_fiat_amount.hide()
with(binding) {
lBalance.root.show()
lBalanceError.root.hide()
lBalance.tvFiatAmount.hide()
lBalance.tvCurrency.text = data.currency
lBalance.tvAmount.text = ""
}
fragment.tv_currency.text = data.currency
fragment.tv_amount.text = ""
showStatus(R.id.tv_status_loading)
@ -67,18 +68,18 @@ class BalanceWidget(
showBalanceWithoutToken(data, false)
}
}
BalanceStatus.VerifiedOnline, BalanceStatus.TransactionInProgress -> {
fragment.l_balance.show()
fragment.l_balance_error.hide()
BalanceStatus.VerifiedOnline, BalanceStatus.TransactionInProgress -> with(binding.lBalance) {
root.show()
binding.lBalanceError.root.hide()
val statusView = if (data.status == BalanceStatus.VerifiedOnline) {
R.id.tv_status_verified
} else {
fragment.tv_status_error.text =
fragment.getText(R.string.wallet_balance_tx_in_progress)
tvStatusError.text =
fragment.getText(R.string.wallet_balance_tx_in_progress)
R.id.group_error
}
showStatus(statusView)
fragment.tv_status_error_message.hide()
tvStatusErrorMessage.hide()
if (data.token != null) {
showBalanceWithToken(data, true)
@ -86,79 +87,83 @@ class BalanceWidget(
showBalanceWithoutToken(data, true)
}
}
BalanceStatus.Unreachable -> {
fragment.l_balance.show()
fragment.l_balance_error.hide()
fragment.tv_fiat_amount.hide()
fragment.group_base_currency.hide()
BalanceStatus.Unreachable -> with(binding.lBalance) {
root.show()
binding.lBalanceError.root.hide()
tvFiatAmount.hide()
groupBaseCurrency.hide()
val currency = if (data.token != null) data.token.tokenSymbol else data.currency
fragment.tv_currency.text = currency
fragment.tv_amount.text = ""
tvCurrency.text = currency
tvAmount.text = ""
fragment.tv_status_error_message.text = data.errorMessage
fragment.tv_status_error.text =
fragment.getString(R.string.wallet_balance_blockchain_unreachable)
tvStatusErrorMessage.text = data.errorMessage
tvStatusError.text =
fragment.getString(R.string.wallet_balance_blockchain_unreachable)
showStatus(R.id.group_error)
fragment.tv_status_error_message.show(!data.errorMessage.isNullOrBlank())
tvStatusErrorMessage.show(!data.errorMessage.isNullOrBlank())
}
BalanceStatus.EmptyCard -> {
fragment.l_balance.hide()
fragment.l_balance_error.show()
BalanceStatus.EmptyCard -> with(binding.lBalanceError) {
binding.lBalance.root.hide()
binding.lBalanceError.root.show()
if (isTwinCard) {
fragment.tv_error_title.text = fragment.getText(R.string.wallet_error_empty_twin_card)
fragment.tv_error_descriptions.text = fragment.getText(R.string.wallet_error_empty_twin_card_subtitle)
tvErrorTitle.text = fragment.getText(R.string.wallet_error_empty_twin_card)
tvErrorDescriptions.text =
fragment.getText(R.string.wallet_error_empty_twin_card_subtitle)
} else {
fragment.tv_error_title.text = fragment.getText(R.string.wallet_error_empty_card)
fragment.tv_error_descriptions.text = fragment.getText(R.string.wallet_error_empty_card_subtitle)
tvErrorTitle.text = fragment.getText(R.string.wallet_error_empty_card)
tvErrorDescriptions.text =
fragment.getText(R.string.wallet_error_empty_card_subtitle)
}
}
BalanceStatus.NoAccount -> {
fragment.l_balance.hide()
fragment.l_balance_error.show()
fragment.tv_error_title.text = fragment.getText(R.string.wallet_error_no_account)
fragment.tv_error_descriptions.text =
fragment.getString(
R.string.wallet_error_no_account_subtitle_format,
data.amountToCreateAccount, data.currencySymbol
)
BalanceStatus.NoAccount -> with(binding.lBalanceError) {
binding.lBalance.root.hide()
binding.lBalanceError.root.show()
tvErrorTitle.text = fragment.getText(R.string.wallet_error_no_account)
tvErrorDescriptions.text =
fragment.getString(
R.string.wallet_error_no_account_subtitle_format,
data.amountToCreateAccount, data.currencySymbol
)
}
BalanceStatus.UnknownBlockchain -> {
fragment.l_balance.hide()
fragment.l_balance_error.show()
fragment.tv_error_title.text = fragment.getText(R.string.wallet_error_unsupported_blockchain)
fragment.tv_error_descriptions.text =
fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle)
BalanceStatus.UnknownBlockchain -> with(binding.lBalanceError) {
binding.lBalance.root.hide()
binding.lBalanceError.root.show()
tvErrorTitle.text =
fragment.getText(R.string.wallet_error_unsupported_blockchain)
tvErrorDescriptions.text =
fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle)
}
}
}
private fun showStatus(@IdRes viewRes: Int) {
fragment.group_error.show(viewRes == R.id.group_error)
fragment.tv_status_loading.show(viewRes == R.id.tv_status_loading)
fragment.tv_status_verified.show(viewRes == R.id.tv_status_verified)
private fun showStatus(@IdRes viewRes: Int) = with(binding.lBalance) {
groupError.show(viewRes == R.id.group_error)
tvStatusLoading.show(viewRes == R.id.tv_status_loading)
tvStatusVerified.show(viewRes == R.id.tv_status_verified)
}
private fun showBalanceWithToken(data: BalanceWidgetData, showAmount: Boolean) {
fragment.group_base_currency.show()
fragment.tv_currency.text = data.token?.tokenSymbol
fragment.tv_base_currency.text = data.currency
fragment.tv_amount.text = if (showAmount) data.token?.amount else ""
fragment.tv_base_amount.text = if (showAmount) data.amount else ""
private fun showBalanceWithToken(data: BalanceWidgetData, showAmount: Boolean) = with(binding.lBalance) {
groupBaseCurrency.show()
tvCurrency.text = data.token?.tokenSymbol
tvBaseCurrency.text = data.currency
tvAmount.text = if (showAmount) data.token?.amount else ""
tvBaseAmount.text = if (showAmount) data.amount else ""
if (showAmount) {
fragment.tv_fiat_amount.show()
fragment.tv_fiat_amount.text = data.token?.fiatAmount
tvFiatAmount.show()
tvFiatAmount.text = data.token?.fiatAmount
}
}
private fun showBalanceWithoutToken(data: BalanceWidgetData, showAmount: Boolean) {
fragment.group_base_currency.hide()
fragment.tv_currency.text = data.currency
fragment.tv_amount.text = if (showAmount) data.amount else ""
if (showAmount) {
fragment.tv_fiat_amount.show()
fragment.tv_fiat_amount.text = data.fiatAmountFormatted
private fun showBalanceWithoutToken(data: BalanceWidgetData, showAmount: Boolean) =
with(binding.lBalance) {
groupBaseCurrency.hide()
tvCurrency.text = data.currency
tvAmount.text = if (showAmount) data.amount else ""
if (showAmount) {
tvFiatAmount.show()
tvFiatAmount.text = data.fiatAmountFormatted
}
}
}
}

View file

@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.squareup.picasso.Picasso
import com.tangem.common.extensions.guard
import com.tangem.tap.common.SnackbarHandler
@ -22,19 +23,17 @@ import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_wallet_details.*
import kotlinx.android.synthetic.main.layout_balance_error.*
import kotlinx.android.synthetic.main.layout_balance_wallet_details.*
import kotlinx.android.synthetic.main.layout_wallet_details.*
import kotlinx.android.synthetic.main.layout_wallet_details.card_balance
import kotlinx.android.synthetic.main.layout_wallet_details_rent.*
import com.tangem.wallet.databinding.FragmentWalletDetailsBinding
import org.rekotlin.StoreSubscriber
class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreSubscriber<WalletState> {
class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
StoreSubscriber<WalletState> {
private lateinit var pendingTransactionAdapter: PendingTransactionsAdapter
private var dialog: Dialog? = null
private val binding: FragmentWalletDetailsBinding by viewBinding(FragmentWalletDetailsBinding::bind)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
@ -65,8 +64,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
(activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar)
binding.toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
setupTransactionsRecyclerView()
@ -74,91 +73,98 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
}
private fun setupTransactionsRecyclerView() {
private fun setupTransactionsRecyclerView() = with(binding) {
pendingTransactionAdapter = PendingTransactionsAdapter()
rv_pending_transaction.layoutManager = LinearLayoutManager(requireContext())
rv_pending_transaction.adapter = pendingTransactionAdapter
rvPendingTransaction.layoutManager = LinearLayoutManager(requireContext())
rvPendingTransaction.adapter = pendingTransactionAdapter
}
private fun setupButtons() {
btn_confirm.text = getString(R.string.wallet_button_send)
btn_confirm.setOnClickListener { store.dispatch(WalletAction.Send()) }
private fun setupButtons() = with(binding) {
btnConfirm.text = getString(R.string.wallet_button_send)
btnConfirm.setOnClickListener { store.dispatch(WalletAction.Send()) }
btn_share.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) }
binding.lWalletDetails.btnShare.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) }
btn_trade.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Buy) }
btnTrade.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Buy) }
btn_sell.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Sell) }
btnSell.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Sell) }
}
override fun newState(state: WalletState) {
if (activity == null) return
if (activity == null || view == null) return
if (state.selectedWallet == null) return
val selectedWallet = state.getSelectedWalletData() ?: return
tv_currency_title.text = selectedWallet.currencyData.currency
val currency = selectedWallet.currency
if (currency is Currency.Token) {
tv_currency_subtitle.text = currency.blockchain.tokenDisplayName()
tv_currency_subtitle.show()
} else {
tv_currency_subtitle.hide()
}
showPendingTransactionsIfPresent(selectedWallet.pendingTransactions)
setupCurrency(selectedWallet.currencyData, selectedWallet.currency)
setupAddressCard(selectedWallet)
setupNoInternetHandling(state)
setupBalanceData(selectedWallet.currencyData)
btn_confirm.isEnabled = selectedWallet.mainButton.enabled
btn_copy.setOnClickListener {
selectedWallet.walletAddresses?.selectedAddress?.address?.let { addressString ->
store.dispatch(WalletAction.CopyAddress(addressString, requireContext()))
}
}
btn_share.setOnClickListener {
selectedWallet.walletAddresses?.selectedAddress?.address?.let { addressString ->
store.dispatch(WalletAction.ShareAddress(addressString, requireContext()))
}
}
setupButtons(selectedWallet)
handleDialogs(state.walletDialog)
handleCurrencyIcon(selectedWallet)
handleWalletRent(selectedWallet.walletRent)
srl_wallet_details.setOnRefreshListener {
binding.srlWalletDetails.setOnRefreshListener {
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
store.dispatch(WalletAction.LoadWallet(
blockchain = selectedWallet.currency.blockchain
))
store.dispatch(
WalletAction.LoadWallet(
blockchain = selectedWallet.currency.blockchain
)
)
}
}
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
srl_wallet_details.isRefreshing = false
binding.srlWalletDetails.isRefreshing = false
}
btn_trade.isEnabled = selectedWallet.tradeCryptoState.buyingAllowed
btn_sell.show(selectedWallet.tradeCryptoState.sellingAllowed)
}
private fun handleWalletRent(rent: WalletRent?) {
private fun setupCurrency(currencyData: BalanceWidgetData, currency: Currency) = with(binding) {
tvCurrencyTitle.text = currencyData.currency
if (currency is Currency.Token) {
binding.tvCurrencySubtitle.text = currency.blockchain.tokenDisplayName()
binding.tvCurrencySubtitle.show()
} else {
binding.tvCurrencySubtitle.hide()
}
}
private fun setupButtons(selectedWallet: WalletData) = with(binding) {
btnConfirm.isEnabled = selectedWallet.mainButton.enabled
lWalletDetails.btnCopy.setOnClickListener {
selectedWallet.walletAddresses?.selectedAddress?.address?.let { addressString ->
store.dispatch(WalletAction.CopyAddress(addressString, requireContext()))
}
}
lWalletDetails.btnShare.setOnClickListener {
selectedWallet.walletAddresses?.selectedAddress?.address?.let { addressString ->
store.dispatch(WalletAction.ShareAddress(addressString, requireContext()))
}
}
btnTrade.isEnabled = selectedWallet.tradeCryptoState.buyingAllowed
btnSell.show(selectedWallet.tradeCryptoState.sellingAllowed)
}
private fun handleWalletRent(rent: WalletRent?) = with(binding) {
val rent = rent.guard {
l_rent_warning.hide()
lRentWarning.root.hide()
return
}
val warningMessage = requireContext().getString(
R.string.solana_rent_warning, rent.minRentValue, rent.rentExemptValue)
tv_rent_warning_message.text = warningMessage
l_rent_warning.show()
R.string.solana_rent_warning, rent.minRentValue, rent.rentExemptValue
)
lRentWarning.tvRentWarningMessage.text = warningMessage
lRentWarning.root.show()
}
private fun handleCurrencyIcon(wallet: WalletData) {
private fun handleCurrencyIcon(wallet: WalletData) = with(binding.lWalletDetails.lBalance) {
Picasso.get().loadCurrenciesIcon(
imageView = iv_currency,
textView = tv_token_letter,
imageView = ivCurrency,
textView = tvTokenLetter,
blockchain = wallet.currency.blockchain,
token = (wallet.currency as? Currency.Token)?.token
)
@ -167,42 +173,48 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
private fun showPendingTransactionsIfPresent(pendingTransactions: List<PendingTransaction>) {
pendingTransactionAdapter.submitList(pendingTransactions)
rv_pending_transaction?.show(pendingTransactions.isNotEmpty())
binding.rvPendingTransaction?.show(pendingTransactions.isNotEmpty())
}
private fun setupAddressCard(state: WalletData) {
private fun setupAddressCard(state: WalletData) = with(binding.lWalletDetails) {
if (state.walletAddresses != null) {
if (state.shouldShowMultipleAddress() && state.currency is Currency.Blockchain) {
(card_balance as? ViewGroup)?.beginDelayedTransition()
chip_group_address_type.show()
chip_group_address_type.fitChipsByGroupWidth()
(cardBalance as? ViewGroup)?.beginDelayedTransition()
chipGroupAddressType.show()
chipGroupAddressType.fitChipsByGroupWidth()
val checkedId = MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type)
if (checkedId != View.NO_ID) chip_group_address_type.check(checkedId)
val checkedId =
MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type)
if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
chip_group_address_type.setOnCheckedChangeListener { group, checkedId ->
chipGroupAddressType.setOnCheckedChangeListener { group, checkedId ->
if (checkedId == -1) return@setOnCheckedChangeListener
val type = MultipleAddressUiHelper.idToType(checkedId, state.currency.blockchain)
val type =
MultipleAddressUiHelper.idToType(checkedId, state.currency.blockchain)
type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) }
}
} else {
chip_group_address_type.hide()
chipGroupAddressType.hide()
}
tv_address.text = state.walletAddresses.selectedAddress.address
tv_explore?.setOnClickListener {
store.dispatch(WalletAction.ExploreAddress(
state.walletAddresses.selectedAddress.exploreUrl,
requireContext()))
tvAddress.text = state.walletAddresses.selectedAddress.address
tvExplore?.setOnClickListener {
store.dispatch(
WalletAction.ExploreAddress(
state.walletAddresses.selectedAddress.exploreUrl,
requireContext()
)
)
}
iv_qr_code.setImageBitmap(state.walletAddresses.selectedAddress.shareUrl.toQrCode())
tv_receive_message.text = getQRReceiveMessage(tv_receive_message.context, state.currency)
ivQrCode.setImageBitmap(state.walletAddresses.selectedAddress.shareUrl.toQrCode())
tvReceiveMessage.text =
getQRReceiveMessage(tvReceiveMessage.context, state.currency)
}
}
private fun setupNoInternetHandling(state: WalletState) {
if (state.state == ProgressState.Error) {
if (state.error == ErrorType.NoInternetConnection) {
srl_wallet_details?.isRefreshing = false
binding.srlWalletDetails.isRefreshing = false
(activity as? SnackbarHandler)?.showSnackbar(
text = R.string.wallet_notification_no_internet,
buttonTitle = R.string.common_retry
@ -213,73 +225,77 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
}
}
private fun setupBalanceData(data: BalanceWidgetData) {
private fun setupBalanceData(data: BalanceWidgetData) = with(binding.lWalletDetails) {
when (data.status) {
BalanceStatus.Loading -> {
l_balance.show()
l_balance_error.hide()
tv_fiat_amount.hide()
tv_amount.text = ""
lBalance.root.show()
lBalanceError.root.hide()
lBalance.tvFiatAmount.hide()
lBalance.tvAmount.text = ""
showStatus(R.id.tv_status_loading)
showBalanceWithoutToken(data, false)
}
BalanceStatus.VerifiedOnline, BalanceStatus.SameCurrencyTransactionInProgress,
BalanceStatus.TransactionInProgress -> {
l_balance.show()
l_balance_error.hide()
lBalance.root.show()
lBalanceError.root.hide()
val statusView = if (data.status == BalanceStatus.VerifiedOnline ||
data.status == BalanceStatus.SameCurrencyTransactionInProgress) {
data.status == BalanceStatus.SameCurrencyTransactionInProgress
) {
R.id.tv_status_verified
} else {
tv_status_error.text =
getText(R.string.wallet_balance_tx_in_progress)
lBalance.tvStatusError.text =
getText(R.string.wallet_balance_tx_in_progress)
R.id.group_error
}
showStatus(statusView)
tv_status_error_message.hide()
lBalance.tvStatusErrorMessage.hide()
showBalanceWithoutToken(data, true)
}
BalanceStatus.Unreachable -> {
l_balance.show()
l_balance_error.hide()
tv_fiat_amount.hide()
lBalance.root.show()
lBalanceError.root.hide()
lBalance.tvFiatAmount.hide()
tv_amount.text = ""
lBalance.tvAmount.text = ""
tv_status_error_message.text = data.errorMessage
tv_status_error.text =
getString(R.string.wallet_balance_blockchain_unreachable)
lBalance.tvStatusErrorMessage.text = data.errorMessage
lBalance.tvStatusError.text =
getString(R.string.wallet_balance_blockchain_unreachable)
showStatus(R.id.group_error)
tv_status_error_message.show(!data.errorMessage.isNullOrBlank())
lBalance.tvStatusErrorMessage.show(!data.errorMessage.isNullOrBlank())
}
BalanceStatus.NoAccount -> {
l_balance.hide()
l_balance_error.show()
tv_error_title.text = getText(R.string.wallet_error_no_account)
tv_error_descriptions.text =
getString(
R.string.wallet_error_no_account_subtitle_format,
data.amountToCreateAccount, data.currencySymbol
)
lBalance.root.hide()
lBalanceError.root.show()
lBalanceError.tvErrorTitle.text = getText(R.string.wallet_error_no_account)
lBalanceError.tvErrorDescriptions.text =
getString(
R.string.wallet_error_no_account_subtitle_format,
data.amountToCreateAccount, data.currencySymbol
)
}
}
card_pending_transaction_warning.show(data.status == BalanceStatus.SameCurrencyTransactionInProgress)
binding.cardPendingTransactionWarning.show(data.status == BalanceStatus.SameCurrencyTransactionInProgress)
}
private fun showStatus(@IdRes viewRes: Int) {
group_error.show(viewRes == R.id.group_error)
tv_status_loading.show(viewRes == R.id.tv_status_loading)
tv_status_verified.show(viewRes == R.id.tv_status_verified)
with(binding.lWalletDetails.lBalance) {
groupError.show(viewRes == R.id.group_error)
tvStatusLoading.show(viewRes == R.id.tv_status_loading)
tvStatusVerified.show(viewRes == R.id.tv_status_verified)
}
}
private fun showBalanceWithoutToken(data: BalanceWidgetData, showAmount: Boolean) {
tv_amount.text = if (showAmount) data.amount else ""
if (showAmount) {
tv_fiat_amount.show()
tv_fiat_amount.text = data.fiatAmountFormatted
with(binding.lWalletDetails.lBalance) {
tvAmount.text = if (showAmount) data.amount else ""
if (showAmount) {
tvFiatAmount.show()
tvFiatAmount.text = data.fiatAmountFormatted
}
}
}

View file

@ -11,6 +11,7 @@ import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.squareup.picasso.Picasso
import com.tangem.tangem_sdk_new.extensions.dpToPx
import com.tangem.tap.MainActivity
@ -29,9 +30,7 @@ import com.tangem.tap.features.wallet.ui.wallet.SingleWalletView
import com.tangem.tap.features.wallet.ui.wallet.WalletView
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_wallet.*
import kotlinx.android.synthetic.main.fragment_wallet.toolbar
import kotlinx.android.synthetic.main.fragment_wallet_details.*
import com.tangem.wallet.databinding.FragmentWalletBinding
import org.rekotlin.StoreSubscriber
@ -39,6 +38,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
private lateinit var warningsAdapter: WarningMessagesAdapter
private val binding: FragmentWalletBinding by viewBinding(FragmentWalletBinding::bind)
private var walletView: WalletView = SingleWalletView()
override fun onCreate(savedInstanceState: Bundle?) {
@ -61,7 +62,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
oldState.walletState == newState.walletState
}.select { it.walletState }
}
walletView.setFragment(this)
walletView.setFragment(this, binding)
store.dispatch(WalletAction.UpdateWallet())
}
@ -73,43 +74,45 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
(activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar)
toolbar.setNavigationOnClickListener {
binding.toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
setupWarningsRecyclerView()
walletView.changeWalletView(this)
walletView.changeWalletView(this, binding)
}
private fun setupWarningsRecyclerView() {
warningsAdapter = WarningMessagesAdapter()
val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
rv_warning_messages.layoutManager = layoutManager
rv_warning_messages.addItemDecoration(SpacesItemDecoration(rv_warning_messages.dpToPx(16f).toInt()))
rv_warning_messages.adapter = warningsAdapter
with(binding) {
rvWarningMessages.layoutManager = layoutManager
rvWarningMessages.addItemDecoration(SpacesItemDecoration(rvWarningMessages.dpToPx(16f).toInt()))
rvWarningMessages.adapter = warningsAdapter
}
}
override fun newState(state: WalletState) {
if (activity == null) return
if (activity == null || view == null) return
if (state.isMultiwalletAllowed &&
state.primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
walletView is SingleWalletView
) {
walletView = MultiWalletView()
walletView.changeWalletView(this)
walletView.changeWalletView(this, binding)
} else if (!state.isMultiwalletAllowed && walletView is MultiWalletView) {
walletView = SingleWalletView()
walletView.changeWalletView(this)
walletView.changeWalletView(this, binding)
}
walletView.onNewState(state)
if (!state.shouldShowDetails) {
toolbar.menu.removeItem(R.id.details_menu)
} else if (toolbar.menu.findItem(R.id.details_menu) == null) {
toolbar.inflateMenu(R.menu.wallet)
binding.toolbar.menu.removeItem(R.id.details_menu)
} else if (binding.toolbar.menu.findItem(R.id.details_menu) == null) {
binding.toolbar.inflateMenu(R.menu.wallet)
}
setupNoInternetHandling(state)
@ -117,26 +120,26 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
showWarningsIfPresent(state.mainWarningsList)
srl_wallet.setOnRefreshListener {
binding.srlWallet.setOnRefreshListener {
if (state.state != ProgressState.Loading) {
store.dispatch(WalletAction.LoadData)
}
}
if (state.state != ProgressState.Loading) {
srl_wallet.isRefreshing = false
binding.srlWallet.isRefreshing = false
}
}
private fun showWarningsIfPresent(warnings: List<WarningMessage>) {
warningsAdapter.submitList(warnings)
rv_warning_messages.show(warnings.isNotEmpty())
binding.rvWarningMessages.show(warnings.isNotEmpty())
}
private fun setupNoInternetHandling(state: WalletState) {
if (state.state == ProgressState.Error) {
if (state.error == ErrorType.NoInternetConnection) {
srl_wallet_details?.isRefreshing = false
binding.srlWallet.isRefreshing = false
(activity as? MainActivity)?.showSnackbar(
text = R.string.wallet_notification_no_internet,
buttonTitle = R.string.common_retry
@ -152,7 +155,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
.load(cardImage?.artworkId)
.placeholder(R.drawable.card_placeholder_black)
?.error(R.drawable.card_placeholder_black)
?.into(iv_card)
?.into(binding.ivCard)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {

View file

@ -1,25 +1,26 @@
package com.tangem.tap.features.wallet.ui.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.item_pending_transaction.view.*
import com.tangem.wallet.databinding.ItemPendingTransactionBinding
class PendingTransactionsAdapter
: ListAdapter<PendingTransaction, PendingTransactionsAdapter.TransactionsViewHolder>(DiffUtilCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionsViewHolder {
val layout = LayoutInflater.from(parent.context)
.inflate(R.layout.item_pending_transaction, parent, false)
return TransactionsViewHolder(layout)
val binding = ItemPendingTransactionBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return TransactionsViewHolder(binding)
}
override fun onBindViewHolder(holder: TransactionsViewHolder, position: Int) {
@ -36,13 +37,13 @@ class PendingTransactionsAdapter
) = oldItem == newItem
}
class TransactionsViewHolder(val view: View) :
RecyclerView.ViewHolder(view) {
class TransactionsViewHolder(val binding: ItemPendingTransactionBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(transaction: PendingTransaction) {
if (transaction.type == PendingTransactionType.Unknown) {
view.hide()
binding.root.hide()
}
val transactionDescriptionRes = when (transaction.type) {
@ -60,17 +61,16 @@ class PendingTransactionsAdapter
PendingTransactionType.Outgoing -> R.drawable.ic_arrow_right_20
PendingTransactionType.Unknown -> return
}
view.tv_pending_transaction.text = view.context.getString(transactionDescriptionRes)
binding.tvPendingTransaction.text = binding.root.getString(transactionDescriptionRes)
transaction.amount?.let { view.tv_pending_transaction_amount.text = "$it " }
view.tv_pending_transaction_currency.text = "${transaction.currency}"
transaction.amount?.let { binding.tvPendingTransactionAmount.text = "$it " }
binding.tvPendingTransactionCurrency.text = "${transaction.currency}"
if (transaction.address != null) {
view.tv_pending_transaction_address.text =
view.context.getString(transactionAddressRes, transaction.address)
binding.tvPendingTransactionAddress.text =
binding.root.getString(transactionAddressRes, transaction.address)
}
view.iv_pending_transaction.setImageDrawable(view.context.getDrawableCompat(image))
binding.ivPendingTransaction.setImageDrawable(binding.root.context.getDrawableCompat(image))
}
}
}

View file

@ -1,7 +1,6 @@
package com.tangem.tap.features.wallet.ui.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
@ -9,6 +8,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.loadCurrenciesIcon
import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.wallet.redux.Currency
@ -17,7 +17,7 @@ import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.item_currency_wallet.view.*
import com.tangem.wallet.databinding.ItemCurrencyWalletBinding
class WalletAdapter
: ListAdapter<WalletData, WalletAdapter.WalletsViewHolder>(DiffUtilCallback) {
@ -32,8 +32,9 @@ class WalletAdapter
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WalletsViewHolder {
val layout = LayoutInflater.from(parent.context)
.inflate(R.layout.item_currency_wallet, parent, false)
val layout = ItemCurrencyWalletBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return WalletsViewHolder(layout)
}
@ -51,24 +52,24 @@ class WalletAdapter
) = oldItem == newItem
}
class WalletsViewHolder(val view: View) :
RecyclerView.ViewHolder(view) {
class WalletsViewHolder(val binding: ItemCurrencyWalletBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(wallet: WalletData) {
view.tv_currency.text = wallet.currencyData.currency
view.tv_amount.text = wallet.currencyData.amount?.takeWhile { !it.isWhitespace() }
view.tv_currency_symbol.text = wallet.currencyData.amount?.takeLastWhile { !it.isWhitespace() }
view.tv_amount_fiat.text = wallet.currencyData.fiatAmountFormatted
view.tv_exchange_rate.text = wallet.fiatRateString
view.card_wallet.setOnClickListener {
fun bind(wallet: WalletData) = with(binding) {
tvCurrency.text = wallet.currencyData.currency
tvAmount.text = wallet.currencyData.amount?.takeWhile { !it.isWhitespace() }
tvCurrencySymbol.text = wallet.currencyData.amount?.takeLastWhile { !it.isWhitespace() }
tvAmountFiat.text = wallet.currencyData.fiatAmountFormatted
tvExchangeRate.text = wallet.fiatRateString
cardWallet.setOnClickListener {
store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet))
}
val blockchain = wallet.currency.blockchain
val token = (wallet.currency as? Currency.Token)?.token
Picasso.get().loadCurrenciesIcon(
imageView = view.iv_currency,
textView = view.tv_token_letter,
imageView = ivCurrency,
textView = tvTokenLetter,
token = token, blockchain = blockchain,
)
@ -77,16 +78,16 @@ class WalletAdapter
BalanceStatus.Loading -> {
hideWarning()
if (wallet.currencyData.amount == null) {
view.tv_exchange_rate.text = view.context.getText(R.string.wallet_balance_loading)
tvExchangeRate.text = root.getString(R.string.wallet_balance_loading)
}
}
BalanceStatus.TransactionInProgress ->
showWarning(view.context.getString(R.string.wallet_balance_tx_in_progress))
showWarning(root.getString(R.string.wallet_balance_tx_in_progress))
BalanceStatus.Unreachable ->
showWarning(view.context.getString(R.string.wallet_balance_blockchain_unreachable))
showWarning(root.getString(R.string.wallet_balance_blockchain_unreachable))
BalanceStatus.NoAccount ->
showWarning(view.context.getString(R.string.wallet_error_no_account))
showWarning(root.getString(R.string.wallet_error_no_account))
else -> {
}
}
@ -94,7 +95,7 @@ class WalletAdapter
private fun showWarning(message: String) {
toggleWarning(true)
view.tv_status_error_message.text = message
binding.tvStatusErrorMessage.text = message
}
private fun hideWarning() {
@ -102,8 +103,8 @@ class WalletAdapter
}
private fun toggleWarning(show: Boolean) {
view.tv_exchange_rate.show(!show)
view.tv_status_error_message.show(show)
binding.tvExchangeRate.show(!show)
binding.tvStatusErrorMessage.show(show)
}
}
}

View file

@ -10,25 +10,23 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
import com.google.android.play.core.review.ReviewManagerFactory
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.extensions.getActivity
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.features.feedback.RateCanBeBetterEmail
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.layout_warning.view.*
import com.tangem.wallet.databinding.LayoutWarningBinding
import timber.log.Timber
class WarningMessagesAdapter : ListAdapter<WarningMessage, WarningMessageVH>(DiffUtilCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WarningMessageVH {
val inflater = LayoutInflater.from(parent.context)
val layout = inflater.inflate(R.layout.layout_warning, parent, false)
return WarningMessageVH(layout)
val binding = LayoutWarningBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return WarningMessageVH(binding)
}
override fun onBindViewHolder(holder: WarningMessageVH, position: Int) {
@ -44,7 +42,7 @@ class WarningMessagesAdapter : ListAdapter<WarningMessage, WarningMessageVH>(Dif
}
}
class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) {
class WarningMessageVH(val binding: LayoutWarningBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(warning: WarningMessage) {
setBgColor(warning.priority)
@ -52,12 +50,12 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) {
setupControlButtons(warning)
}
private fun setText(warning: WarningMessage) {
private fun setText(warning: WarningMessage) = with(binding) {
fun getString(resId: Int?, default: String, formatArgs: String? = null) =
if (resId == null) default else view.context.getString(resId, formatArgs)
if (resId == null) default else root.getString(resId, formatArgs)
view.tv_title.text = getString(warning.titleResId, warning.title)
view.tv_message.text = getString(
tvTitle.text = getString(warning.titleResId, warning.title)
tvMessage.text = getString(
resId = warning.messageResId, default = warning.message, formatArgs = warning.messageFormatArg
)
}
@ -68,17 +66,17 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) {
WarningMessage.Priority.Warning -> R.color.warning_warning
WarningMessage.Priority.Critical -> R.color.warning_critical
}
view.card_view.setCardBackgroundColor(view.context.resources.getColor(color))
binding.cardView.setCardBackgroundColor(binding.root.getColor(color))
}
private fun setupControlButtons(warning: WarningMessage) = when (warning.type) {
WarningMessage.Type.Permanent, WarningMessage.Type.TestCard -> {
view.group_controls_temporary.hide()
view.group_controls_rating.hide()
binding.groupControlsTemporary.hide()
binding.groupControlsRating.hide()
}
WarningMessage.Type.Temporary -> {
view.group_controls_rating.hide()
view.group_controls_temporary.show()
binding.groupControlsRating.hide()
binding.groupControlsTemporary.show()
val buttonAction =
when (warning.titleResId) {
R.string.warning_important_security_info -> {
@ -92,29 +90,29 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) {
}
}
}
val buttonTitle = view.getString(
val buttonTitle = binding.root.getString(
warning.buttonTextId ?: R.string.how_to_got_it_button
)
view.btn_got_it.setOnClickListener (buttonAction)
view.btn_got_it.text = buttonTitle
binding.btnGotIt.setOnClickListener (buttonAction)
binding.btnGotIt.text = buttonTitle
}
WarningMessage.Type.AppRating -> {
view.group_controls_temporary.hide()
view.group_controls_rating.show()
binding.groupControlsTemporary.hide()
binding.groupControlsRating.show()
val analyticsHandler = store.state.globalState.analyticsHandlers
view.btn_close.setOnClickListener {
binding.btnClose.setOnClickListener {
analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_DISMISS)
store.dispatch(GlobalAction.HideWarningMessage(warning))
store.dispatch(WalletAction.Warnings.AppRating.RemindLater)
}
view.btn_can_be_better.setOnClickListener {
binding.btnCanBeBetter.setOnClickListener {
analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_NEGATIVE)
store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow)
store.dispatch(GlobalAction.HideWarningMessage(warning))
store.dispatch(GlobalAction.SendFeedback(RateCanBeBetterEmail()))
}
view.btn_really_cool.setOnClickListener {
val activity = view.context.getActivity() ?: return@setOnClickListener
binding.btnReallyCool.setOnClickListener {
val activity = binding.root.context.getActivity() ?: return@setOnClickListener
analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_POSITIVE)
store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow)

View file

@ -1,8 +1,9 @@
package com.tangem.tap.features.wallet.ui.dialogs
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.view.ContextThemeWrapper
import androidx.recyclerview.widget.*
@ -12,14 +13,22 @@ import com.tangem.tap.common.extensions.toFormattedString
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.dialog_wallet_send.*
import kotlinx.android.synthetic.main.item_wallet_amount_to_send.view.*
import com.tangem.wallet.databinding.DialogWalletSendBinding
import com.tangem.wallet.databinding.ItemWalletAmountToSendBinding
class AmountToSendDialog(context: Context) : BottomSheetDialog(context) {
init {
this.setContentView(R.layout.dialog_wallet_send)
var binding: DialogWalletSendBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DialogWalletSendBinding.inflate(LayoutInflater.from(context))
setContentView(binding!!.root)
}
override fun setOnCancelListener(listener: DialogInterface.OnCancelListener?) {
super.setOnCancelListener(listener)
binding = null
}
fun show(amounts: List<Amount>?) {
@ -27,15 +36,15 @@ class AmountToSendDialog(context: Context) : BottomSheetDialog(context) {
store.dispatch(WalletAction.Send.Cancel)
}
rv_amounts_to_send.layoutManager = LinearLayoutManager(context)
binding!!.rvAmountsToSend.layoutManager = LinearLayoutManager(context)
val dividerItemDecoration = DividerItemDecoration(
ContextThemeWrapper(rv_amounts_to_send.context, R.style.AppTheme),
ContextThemeWrapper(binding!!.root.context, R.style.AppTheme),
DividerItemDecoration.VERTICAL
)
rv_amounts_to_send.addItemDecoration(dividerItemDecoration)
binding!!.rvAmountsToSend.addItemDecoration(dividerItemDecoration)
val viewAdapter = ChooseAmountAdapter()
rv_amounts_to_send.adapter = viewAdapter
binding!!.rvAmountsToSend.adapter = viewAdapter
viewAdapter.submitList(amounts)
@ -48,9 +57,10 @@ class ChooseAmountAdapter
: ListAdapter<Amount, ChooseAmountAdapter.AmountViewHolder>(DiffUtilCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AmountViewHolder {
val layout = LayoutInflater.from(parent.context)
.inflate(R.layout.item_wallet_amount_to_send, parent, false)
return AmountViewHolder(layout)
val binding = ItemWalletAmountToSendBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return AmountViewHolder(binding)
}
override fun onBindViewHolder(holder: AmountViewHolder, position: Int) {
@ -67,13 +77,13 @@ class ChooseAmountAdapter
) = oldItem == newItem
}
class AmountViewHolder(val view: View) :
RecyclerView.ViewHolder(view) {
class AmountViewHolder(val binding: ItemWalletAmountToSendBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(amount: Amount) {
view.tv_currency_symbol.text = amount.currencySymbol
view.tv_amount.text = amount.value?.toFormattedString(amount.decimals)
view.setOnClickListener {
fun bind(amount: Amount) = with(binding) {
tvCurrencySymbol.text = amount.currencySymbol
tvAmount.text = amount.value?.toFormattedString(amount.decimals)
root.setOnClickListener {
store.dispatch(WalletAction.Send.Cancel)
store.dispatch(WalletAction.Send(amount))
}

View file

@ -1,30 +1,36 @@
package com.tangem.tap.features.wallet.ui.dialogs
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.dialog_wallet_trade.*
import com.tangem.wallet.databinding.DialogWalletTradeBinding
class ChooseTradeActionDialog(context: Context) : BottomSheetDialog(context) {
init {
this.setContentView(R.layout.dialog_wallet_trade)
var binding: DialogWalletTradeBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DialogWalletTradeBinding.inflate(LayoutInflater.from(context))
setContentView(binding!!.root)
}
override fun show() {
super.show()
this.setOnDismissListener {
binding = null
store.dispatch(WalletAction.HideDialog)
}
dialog_btn_buy.setOnClickListener {
binding!!.dialogBtnBuy.setOnClickListener {
store.dispatch(WalletAction.TradeCryptoAction.Buy)
dismiss()
}
dialog_btn_sell.setOnClickListener {
binding!!.dialogBtnSell.setOnClickListener {
store.dispatch(WalletAction.TradeCryptoAction.Sell)
dismiss()
}

View file

@ -18,64 +18,64 @@ import com.tangem.tap.features.wallet.ui.adapters.WalletAdapter
import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.card_balance.*
import kotlinx.android.synthetic.main.fragment_wallet.*
import kotlinx.android.synthetic.main.layout_balance_error.*
import kotlinx.android.synthetic.main.layout_wallet_long_buttons.*
import com.tangem.wallet.databinding.FragmentWalletBinding
class MultiWalletView : WalletView {
private var fragment: WalletFragment? = null
private var binding: FragmentWalletBinding? = null
private var dialog: Dialog? = null
private lateinit var walletsAdapter: WalletAdapter
override fun changeWalletView(fragment: WalletFragment) {
setFragment(fragment)
override fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding) {
setFragment(fragment, binding)
onViewCreated()
showMultiWalletView(fragment)
setupButtons(fragment)
showMultiWalletView(binding)
setupButtons(binding)
}
private fun showMultiWalletView(fragment: WalletFragment) = with(fragment) {
tv_twin_card_number.hide()
rv_pending_transaction.hide()
l_card_balance.hide()
l_address.hide()
l_buttons_short.hide()
l_buttons_long.hide()
btn_scan_multiwallet?.show()
rv_multiwallet.show()
btn_add_token.show()
setupWalletCardNumber(fragment)
private fun showMultiWalletView(binding: FragmentWalletBinding) = with(binding) {
tvTwinCardNumber.hide()
rvPendingTransaction.hide()
lCardBalance.root.hide()
lAddress.root.hide()
lButtonsShort.root.hide()
lButtonsLong.root.hide()
btnScanMultiwallet.show()
rvMultiwallet.show()
btnAddToken.show()
setupWalletCardNumber(binding)
}
private fun setupWalletCardNumber(fragment: WalletFragment) = with(fragment) {
private fun setupWalletCardNumber(binding: FragmentWalletBinding) = with(binding) {
val card = store.state.globalState.scanResponse?.card
if (card?.backupStatus is Card.BackupStatus.Active) {
val cardCount = (card.backupStatus as Card.BackupStatus.Active).cardCount + 1
tv_twin_card_number.show()
tv_twin_card_number.text =
getString(R.string.wallet_twins_chip_format, 1, cardCount)
tvTwinCardNumber.show()
tvTwinCardNumber.text =
fragment?.getString(R.string.wallet_twins_chip_format, 1, cardCount)
} else {
tv_twin_card_number.hide()
tvTwinCardNumber.hide()
}
}
private fun setupButtons(fragment: WalletFragment) = with(fragment) {
btn_scan_multiwallet?.setOnClickListener { store.dispatch(WalletAction.Scan) }
private fun setupButtons(binding: FragmentWalletBinding) = with(binding) {
btnScanMultiwallet.setOnClickListener { store.dispatch(WalletAction.Scan) }
}
override fun setFragment(fragment: WalletFragment) {
override fun setFragment(fragment: WalletFragment, binding: FragmentWalletBinding) {
this.fragment = fragment
this.binding = binding
}
override fun removeFragment() {
this.fragment = null
this.binding = null
}
override fun onViewCreated() {
@ -86,37 +86,42 @@ class MultiWalletView : WalletView {
val fragment = fragment ?: return
walletsAdapter = WalletAdapter()
walletsAdapter.setHasStableIds(true)
fragment.rv_multiwallet.layoutManager = LinearLayoutManager(fragment.requireContext())
fragment.rv_multiwallet.adapter = walletsAdapter
binding?.rvMultiwallet?.layoutManager = LinearLayoutManager(fragment.requireContext())
binding?.rvMultiwallet?.adapter = walletsAdapter
}
override fun onNewState(state: WalletState) {
val fragment = fragment ?: return
val binding = binding ?: return
walletsAdapter.submitList(state.wallets, state.primaryBlockchain, state.primaryToken)
fragment.btn_add_token?.setOnClickListener {
binding.btnAddToken.setOnClickListener {
store.dispatch(TokensAction.LoadCurrencies)
store.dispatch(TokensAction.SetAddedCurrencies(state.wallets))
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))
}
handleErrorStates(state, fragment)
handleErrorStates(state = state, binding = binding, fragment = fragment)
handleDialogs(state.walletDialog)
}
private fun handleErrorStates(state: WalletState, fragment: WalletFragment) {
private fun handleErrorStates(
state: WalletState,
binding: FragmentWalletBinding,
fragment: WalletFragment
) {
when (state.primaryWallet?.currencyData?.status) {
BalanceStatus.EmptyCard -> {
showErrorState(
fragment,
binding,
fragment.getText(R.string.wallet_error_empty_card),
fragment.getString(R.string.wallet_error_empty_card_subtitle)
)
configureButtonsForEmptyWalletState(fragment)
configureButtonsForEmptyWalletState(binding)
}
BalanceStatus.UnknownBlockchain -> {
showErrorState(
fragment,
binding,
fragment.getText(R.string.wallet_error_unsupported_blockchain),
fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle)
)
@ -125,24 +130,28 @@ class MultiWalletView : WalletView {
}
private fun showErrorState(
fragment: WalletFragment, errorTitle: CharSequence, errorDescription: CharSequence,
) = with(fragment) {
l_card_balance.show()
l_balance.hide()
l_balance_error.show()
rv_multiwallet.hide()
btn_add_token.hide()
tv_error_title.text = errorTitle
tv_error_descriptions.text = errorDescription
binding: FragmentWalletBinding, errorTitle: CharSequence, errorDescription: CharSequence,
) = with(binding) {
lCardBalance.root.show()
with(lCardBalance) {
lBalance.root.hide()
lBalanceError.root.show()
rvMultiwallet.show()
btnAddToken.hide()
lBalanceError.tvErrorTitle.text = errorTitle
lBalanceError.tvErrorDescriptions.text = errorDescription
}
}
private fun configureButtonsForEmptyWalletState(fragment: WalletFragment) = with(fragment) {
btn_scan_multiwallet.hide()
l_buttons_long.show()
btn_scan_long.setOnClickListener { store.dispatch(WalletAction.Scan) }
btn_confirm_long.setOnClickListener { store.dispatch(WalletAction.CreateWallet) }
btn_confirm_long.text = fragment.getText(R.string.wallet_button_create_wallet)
}
private fun configureButtonsForEmptyWalletState(binding: FragmentWalletBinding) =
with(binding) {
btnScanMultiwallet.hide()
lButtonsLong.root.show()
lButtonsLong.btnScanLong.setOnClickListener { store.dispatch(WalletAction.Scan) }
lButtonsLong.btnConfirmLong.setOnClickListener { store.dispatch(WalletAction.CreateWallet) }
lButtonsLong.btnConfirmLong.text =
fragment?.getText(R.string.wallet_button_create_wallet)
}
private fun handleDialogs(walletDialog: StateDialog?) {
val fragment = fragment ?: return

View file

@ -5,7 +5,10 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.recyclerview.widget.LinearLayoutManager
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.fitChipsByGroupWidth
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
@ -15,42 +18,43 @@ import com.tangem.tap.features.wallet.ui.BalanceWidget
import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper
import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
import com.tangem.tap.features.wallet.ui.dialogs.*
import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog
import com.tangem.tap.features.wallet.ui.dialogs.ChooseTradeActionDialog
import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.card_balance.*
import kotlinx.android.synthetic.main.fragment_wallet.*
import kotlinx.android.synthetic.main.layout_address.*
import kotlinx.android.synthetic.main.layout_wallet_long_buttons.*
import kotlinx.android.synthetic.main.layout_wallet_short_buttons.*
import com.tangem.wallet.databinding.FragmentWalletBinding
class SingleWalletView : WalletView {
private lateinit var pendingTransactionAdapter: PendingTransactionsAdapter
private var fragment: WalletFragment? = null
private var binding: FragmentWalletBinding? = null
private var dialog: Dialog? = null
override fun setFragment(fragment: WalletFragment) {
override fun setFragment(fragment: WalletFragment, binding: FragmentWalletBinding) {
this.fragment = fragment
this.binding = binding
}
override fun removeFragment() {
fragment = null
binding = null
}
override fun changeWalletView(fragment: WalletFragment) {
setFragment(fragment)
override fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding) {
setFragment(fragment, binding)
onViewCreated()
showSingleWalletView(fragment)
showSingleWalletView(binding)
}
private fun showSingleWalletView(fragment: WalletFragment) = with(fragment) {
rv_multiwallet.hide()
btn_add_token.hide()
btn_scan_multiwallet?.hide()
rv_pending_transaction.hide()
l_card_balance.show()
l_address.show()
private fun showSingleWalletView(binding: FragmentWalletBinding) = with(binding) {
rvMultiwallet.hide()
btnAddToken.hide()
btnScanMultiwallet.hide()
rvPendingTransaction.hide()
lCardBalance.root.show()
lAddress.root.show()
}
override fun onViewCreated() {
@ -61,17 +65,19 @@ class SingleWalletView : WalletView {
private fun setupTransactionsRecyclerView() {
val fragment = fragment ?: return
pendingTransactionAdapter = PendingTransactionsAdapter()
fragment.rv_pending_transaction.layoutManager = LinearLayoutManager(fragment.requireContext())
fragment.rv_pending_transaction.adapter = pendingTransactionAdapter
binding?.rvPendingTransaction?.layoutManager =
LinearLayoutManager(fragment.requireContext())
binding?.rvPendingTransaction?.adapter = pendingTransactionAdapter
}
override fun onNewState(state: WalletState) {
val fragment = fragment ?: return
val binding = binding ?: return
state.primaryWallet ?: return
setupTwinCards(state.twinCardsState, fragment)
setupButtons(state.primaryWallet, state.isTangemTwins, fragment)
setupAddressCard(state.primaryWallet, fragment)
setupTwinCards(state.twinCardsState, binding)
setupButtons(state.primaryWallet, state.isTangemTwins, binding)
setupAddressCard(state.primaryWallet, binding)
showPendingTransactionsIfPresent(state.primaryWallet.pendingTransactions)
setupBalance(state, state.primaryWallet)
handleDialogs(state.walletDialog, fragment)
@ -79,75 +85,81 @@ class SingleWalletView : WalletView {
private fun showPendingTransactionsIfPresent(pendingTransactions: List<PendingTransaction>) {
pendingTransactionAdapter.submitList(pendingTransactions)
fragment?.rv_pending_transaction?.show(pendingTransactions.isNotEmpty())
binding?.rvPendingTransaction?.show(pendingTransactions.isNotEmpty())
}
private fun setupBalance(state: WalletState, primaryWallet: WalletData) {
fragment?.apply {
this.l_balance.show()
BalanceWidget(this, primaryWallet.currencyData, state.isTangemTwins).setup()
val fragment = fragment ?: return
binding?.apply {
lCardBalance.lBalance.root.show()
BalanceWidget(
binding = this.lCardBalance,
fragment = fragment,
data = primaryWallet.currencyData,
isTwinCard = state.isTangemTwins
).setup()
}
}
private fun setupTwinCards(
twinCardsState: TwinCardsState?, fragment: WalletFragment
) = with(fragment) {
twinCardsState: TwinCardsState?, binding: FragmentWalletBinding
) = with(binding) {
twinCardsState?.cardNumber?.let { cardNumber ->
tv_twin_card_number.show()
tvTwinCardNumber.show()
val number = when (cardNumber) {
TwinCardNumber.First -> 1
TwinCardNumber.Second -> 2
}
tv_twin_card_number.text =
this.getString(R.string.wallet_twins_chip_format, number, 2)
tvTwinCardNumber.text =
fragment?.getString(R.string.wallet_twins_chip_format, number, 2)
}
if (twinCardsState?.cardNumber == null) {
tv_twin_card_number.hide()
tvTwinCardNumber.hide()
}
}
private fun setupButtons(
state: WalletData, isTwinsWallet: Boolean, fragment: WalletFragment
) = with(fragment){
state: WalletData, isTwinsWallet: Boolean, binding: FragmentWalletBinding
) = with(binding) {
setupButtonsType(state, fragment)
setupButtonsType(state, binding)
val btnConfirm = if (state.tradeCryptoState.sellingAllowed ||
state.tradeCryptoState.buyingAllowed
) {
btn_confirm_short
lButtonsShort.btnConfirmShort
} else {
btn_confirm_long
lButtonsLong.btnConfirmLong
}
val btnScan = if (state.tradeCryptoState.sellingAllowed ||
state.tradeCryptoState.buyingAllowed
) {
btn_scan_short
lButtonsShort.btnScanShort
} else {
btn_scan_long
lButtonsLong.btnScanLong
}
setupConfirmButton(state, btnConfirm, fragment, isTwinsWallet)
setupConfirmButton(state, btnConfirm, isTwinsWallet)
btnScan.setOnClickListener {
store.dispatch(WalletAction.Scan)
}
btn_copy.setOnClickListener {
lAddress.btnCopy.setOnClickListener {
state.walletAddresses?.selectedAddress?.address?.let { addressString ->
store.dispatch(WalletAction.CopyAddress(addressString, fragment.requireContext()))
store.dispatch(WalletAction.CopyAddress(addressString, fragment!!.requireContext()))
}
}
btn_show_qr.setOnClickListener {
lAddress.btnShowQr.setOnClickListener {
store.dispatch(WalletAction.ShowDialog.QrCode)
}
setupTradeButton(fragment, state.tradeCryptoState)
setupTradeButton(binding, state.tradeCryptoState)
}
private fun setupTradeButton(fragment: WalletFragment, tradeCryptoState: TradeCryptoState) {
private fun setupTradeButton(binding: FragmentWalletBinding, tradeCryptoState: TradeCryptoState) {
val allowedToBuy = tradeCryptoState.buyingAllowed
val allowedToSell = tradeCryptoState.sellingAllowed
val action = when {
@ -160,7 +172,7 @@ class SingleWalletView : WalletView {
allowedToBuy && !allowedToSell -> R.string.wallet_button_buy
!allowedToBuy && allowedToSell -> R.string.wallet_button_sell
allowedToBuy && allowedToSell -> R.string.wallet_button_trade
else -> R.string.wallet_button_trade
else -> R.string.wallet_button_trade
}
val icon = when {
allowedToBuy && !allowedToSell -> R.drawable.ic_arrow_up_short_btn
@ -168,27 +180,27 @@ class SingleWalletView : WalletView {
allowedToBuy && allowedToSell -> R.drawable.ic_arrows_up_down_short_btn
else -> null
}
with(fragment) {
btn_trade.text = getText(text)
btn_trade.setCompoundDrawablesWithIntrinsicBounds(0, icon ?: 0, 0, 0)
btn_trade.setOnClickListener { if (action != null) store.dispatch(action) }
with(binding) {
lButtonsShort.btnTrade.text = fragment?.getText(text)
lButtonsShort.btnTrade.setCompoundDrawablesWithIntrinsicBounds(0, icon ?: 0, 0, 0)
lButtonsShort.btnTrade.setOnClickListener { if (action != null) store.dispatch(action) }
}
}
private fun setupButtonsType(state: WalletData, fragment: WalletFragment) = with(fragment) {
private fun setupButtonsType(state: WalletData, binding: FragmentWalletBinding) = with(binding) {
if (state.tradeCryptoState.sellingAllowed ||
state.tradeCryptoState.buyingAllowed
) {
l_buttons_long.hide()
l_buttons_short.show()
lButtonsLong.root.hide()
lButtonsShort.root.show()
} else {
l_buttons_long.show()
l_buttons_short.hide()
lButtonsLong.root.show()
lButtonsShort.root.hide()
}
}
private fun setupConfirmButton(
state: WalletData, btnConfirm: Button, fragment: WalletFragment, isTwinsWallet: Boolean,
state: WalletData, btnConfirm: Button, isTwinsWallet: Boolean,
) {
val buttonTitle = when (state.mainButton) {
is WalletMainButton.SendButton -> R.string.wallet_button_send
@ -200,7 +212,7 @@ class SingleWalletView : WalletView {
}
}
}
btnConfirm.text = fragment.getString(buttonTitle)
btnConfirm.text = fragment?.getString(buttonTitle)
btnConfirm.isEnabled = state.mainButton.enabled
btnConfirm.setOnClickListener {
when (state.mainButton) {
@ -211,33 +223,38 @@ class SingleWalletView : WalletView {
}
private fun setupAddressCard(state: WalletData, fragment: WalletFragment) = with(fragment) {
private fun setupAddressCard(state: WalletData, binding: FragmentWalletBinding) = with(binding.lAddress) {
if (state.walletAddresses != null && state.currency is Currency.Blockchain) {
l_address?.show()
binding.lAddress.root.show()
if (state.shouldShowMultipleAddress()) {
(l_address as? ViewGroup)?.beginDelayedTransition()
chip_group_address_type.show()
chip_group_address_type.fitChipsByGroupWidth()
(binding.lAddress.root as? ViewGroup)?.beginDelayedTransition()
chipGroupAddressType.show()
chipGroupAddressType.fitChipsByGroupWidth()
val checkedId = MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type)
if (checkedId != View.NO_ID) chip_group_address_type.check(checkedId)
val checkedId =
MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type)
if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
chip_group_address_type.setOnCheckedChangeListener { group, checkedId ->
chipGroupAddressType.setOnCheckedChangeListener { group, checkedId ->
if (checkedId == -1) return@setOnCheckedChangeListener
val type = MultipleAddressUiHelper.idToType(checkedId, state.currency.blockchain)
val type =
MultipleAddressUiHelper.idToType(checkedId, state.currency.blockchain)
type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) }
}
} else {
chip_group_address_type.hide()
chipGroupAddressType.hide()
}
tv_address.text = state.walletAddresses.selectedAddress.address
tv_explore?.setOnClickListener {
store.dispatch(WalletAction.ExploreAddress(
tvAddress.text = state.walletAddresses.selectedAddress.address
tvExplore.setOnClickListener {
store.dispatch(
WalletAction.ExploreAddress(
state.walletAddresses.selectedAddress.exploreUrl,
fragment.requireContext()))
fragment!!.requireContext()
)
)
}
} else {
l_address?.hide()
binding.lAddress.root.hide()
}
}

View file

@ -2,14 +2,15 @@ package com.tangem.tap.features.wallet.ui.wallet
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.wallet.databinding.FragmentWalletBinding
interface WalletView {
fun setFragment(fragment: WalletFragment)
fun setFragment(fragment: WalletFragment, binding: FragmentWalletBinding)
fun removeFragment()
fun changeWalletView(fragment: WalletFragment)
fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding)
fun onViewCreated()

View file

@ -33,8 +33,9 @@
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<FrameLayout
<include
android:id="@+id/onboarding_top_container"
layout="@layout/layout_onboarding_container_top"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.2"
@ -50,6 +51,7 @@
</androidx.appcompat.widget.LinearLayoutCompat>
<include
android:id="@+id/v_confetti"
layout="@layout/view_confetti"
android:visibility="gone" />

View file

@ -225,6 +225,7 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<include
android:id="@+id/v_confetti"
layout="@layout/view_confetti"
android:visibility="gone" />

View file

@ -39,12 +39,14 @@
android:orientation="vertical">
<include
android:id="@+id/l_send_address_payid"
layout="@layout/layout_send_address_payid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp" />
<include
android:id="@+id/l_send_amount"
layout="@layout/layout_send_amount"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View file

@ -12,6 +12,7 @@
android:visibility="gone" />
<include
android:id="@+id/v_onboarding_progress"
layout="@layout/view_onboarding_progress"
android:layout_width="0dp"
android:layout_height="wrap_content"