Updated on 2026-08-14
This commit is contained in:
commit
9dda19d990
108 changed files with 2712 additions and 2263 deletions
184
Gemfile.lock
Normal file
184
Gemfile.lock
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.4)
|
||||
rexml
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.519.0)
|
||||
aws-sdk-core (3.121.3)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.50.0)
|
||||
aws-sdk-core (~> 3, >= 3.121.2)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.104.0)
|
||||
aws-sdk-core (~> 3, >= 3.121.2)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.4.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.0.3)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander-fastlane (4.4.6)
|
||||
highline (~> 1.7.2)
|
||||
declarative (0.0.20)
|
||||
declarative-option (0.1.0)
|
||||
digest-crc (0.6.4)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.6)
|
||||
emoji_regex (1.0.1)
|
||||
excon (0.87.0)
|
||||
faraday (0.17.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday_middleware (0.13.1)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
fastimage (2.2.5)
|
||||
fastlane (2.146.1)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.2, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 2.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 0.17)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 0.13.1)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-api-client (>= 0.29.2, < 0.37.0)
|
||||
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
json (< 3.0.0)
|
||||
jwt (~> 2.1.0)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multi_xml (~> 0.5)
|
||||
multipart-post (~> 2.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
public_suffix (~> 2.0.0)
|
||||
rubyzip (>= 1.3.0, < 2.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fastlane-plugin-firebase_app_distribution (0.3.1)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.32.1)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.5, < 0.10.0)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.10)
|
||||
google-cloud-core (1.3.2)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-env (1.2.1)
|
||||
faraday (~> 0.11)
|
||||
google-cloud-storage (1.21.1)
|
||||
addressable (~> 2.5)
|
||||
digest-crc (~> 0.4)
|
||||
google-api-client (~> 0.26)
|
||||
google-cloud-core (~> 1.2)
|
||||
googleauth (>= 0.6.2, < 0.10.0)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (0.9.0)
|
||||
faraday (~> 0.12)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.7)
|
||||
highline (1.7.10)
|
||||
http-cookie (1.0.4)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.4.0)
|
||||
json (2.6.1)
|
||||
jwt (2.1.0)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.2)
|
||||
multi_json (1.15.0)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.1)
|
||||
os (1.1.1)
|
||||
plist (3.6.0)
|
||||
public_suffix (2.0.5)
|
||||
rake (13.0.6)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.5)
|
||||
rouge (2.0.7)
|
||||
rubyzip (1.3.0)
|
||||
security (0.1.3)
|
||||
signet (0.11.0)
|
||||
addressable (~> 2.3)
|
||||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.8)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.4.0)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8)
|
||||
unicode-display_width (1.8.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.21.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
universal-darwin-18
|
||||
|
||||
DEPENDENCIES
|
||||
fastlane
|
||||
fastlane-plugin-firebase_app_distribution
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.16
|
||||
|
|
@ -13,6 +13,8 @@ import com.tangem.tap.common.DialogManager
|
|||
import com.tangem.tap.common.IntentHandler
|
||||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.redux.NotificationsHandler
|
||||
import com.tangem.tap.common.redux.global.AndroidResources
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
|
|
@ -66,9 +68,19 @@ class MainActivity : AppCompatActivity(), SnackbarHandler {
|
|||
tangemSdk = TangemSdk.init(this, TangemSdkManager.config)
|
||||
tangemSdkManager = TangemSdkManager(tangemSdk, this)
|
||||
|
||||
store.dispatch(GlobalAction.SetResources(getAndroidResources()))
|
||||
store.dispatch(WalletConnectAction.RestoreSessions)
|
||||
}
|
||||
|
||||
private fun getAndroidResources(): AndroidResources {
|
||||
return AndroidResources(
|
||||
AndroidResources.RString(
|
||||
R.string.copy_toast_msg,
|
||||
R.string.details_notification_erase_wallet_not_possible
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun systemActions() {
|
||||
// makes the status bar text dark
|
||||
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.tangem.tap.common.redux.global.GlobalState
|
|||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
|
||||
import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.ui.dialog.CreateWalletInterruptDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -43,24 +45,25 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
dialog = when (state.dialog) {
|
||||
is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context)
|
||||
is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
|
||||
is TwinCardsAction.Wallet.ShowInterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context)
|
||||
is WalletConnectDialog.UnsupportedCard ->
|
||||
SimpleAlertDialog.create(
|
||||
titleRes = R.string.wallet_connect,
|
||||
messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet,
|
||||
context = context
|
||||
titleRes = R.string.wallet_connect,
|
||||
messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet,
|
||||
context = context
|
||||
)
|
||||
is WalletConnectDialog.OpeningSessionRejected -> {
|
||||
SimpleAlertDialog.create(
|
||||
titleRes = R.string.wallet_connect,
|
||||
messageRes = R.string.wallet_connect_same_wcuri,
|
||||
context = context
|
||||
titleRes = R.string.wallet_connect,
|
||||
messageRes = R.string.wallet_connect_same_wcuri,
|
||||
context = context
|
||||
)
|
||||
}
|
||||
is WalletConnectDialog.SessionTimeout -> {
|
||||
SimpleAlertDialog.create(
|
||||
titleRes = R.string.wallet_connect,
|
||||
messageRes = R.string.wallet_connect_error_timeout,
|
||||
context = context
|
||||
titleRes = R.string.wallet_connect,
|
||||
messageRes = R.string.wallet_connect_error_timeout,
|
||||
context = context
|
||||
)
|
||||
}
|
||||
is WalletConnectDialog.ApproveWcSession ->
|
||||
|
|
|
|||
|
|
@ -10,26 +10,24 @@ import com.tangem.tap.common.redux.navigation.FragmentShareTransition
|
|||
import com.tangem.tap.features.details.ui.DetailsConfirmFragment
|
||||
import com.tangem.tap.features.details.ui.DetailsFragment
|
||||
import com.tangem.tap.features.details.ui.DetailsSecurityFragment
|
||||
import com.tangem.tap.features.details.ui.twins.CreateTwinWalletFragment
|
||||
import com.tangem.tap.features.details.ui.twins.TwinWalletWarningFragment
|
||||
import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment
|
||||
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectSessionsFragment
|
||||
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
|
||||
import com.tangem.tap.features.home.HomeFragment
|
||||
import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment
|
||||
import com.tangem.tap.features.onboarding.products.twins.ui.TwinsCardsFragment
|
||||
import com.tangem.tap.features.onboarding.products.wallet.OnboardingWalletFragment
|
||||
import com.tangem.tap.features.send.ui.SendFragment
|
||||
import com.tangem.tap.features.tokens.ui.AddTokensFragment
|
||||
import com.tangem.tap.features.wallet.ui.OnboardingTwinsFragment
|
||||
import com.tangem.tap.features.wallet.ui.WalletDetailsFragment
|
||||
import com.tangem.tap.features.wallet.ui.WalletFragment
|
||||
import com.tangem.wallet.R
|
||||
|
||||
fun FragmentActivity.openFragment(
|
||||
screen: AppScreen,
|
||||
addToBackstack: Boolean,
|
||||
fgShareTransition: FragmentShareTransition? = null
|
||||
screen: AppScreen,
|
||||
addToBackstack: Boolean,
|
||||
fgShareTransition: FragmentShareTransition? = null
|
||||
) {
|
||||
val transaction = this.supportFragmentManager.beginTransaction()
|
||||
val fragment = fragmentFactory(screen)
|
||||
|
|
@ -39,7 +37,7 @@ fun FragmentActivity.openFragment(
|
|||
transaction.setReorderingAllowed(true)
|
||||
shareElements.forEach { shareElement ->
|
||||
shareElement.wView.get()?.let { view ->
|
||||
transaction.addSharedElement(view, shareElement.name)
|
||||
transaction.addSharedElement(view, shareElement.elementName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -60,8 +58,8 @@ fun FragmentActivity.getPreviousScreen(): AppScreen? {
|
|||
}
|
||||
|
||||
fun FragmentActivity.addOnBackPressedDispatcher(
|
||||
isEnabled: Boolean = true,
|
||||
onBackPressed: VoidCallback
|
||||
isEnabled: Boolean = true,
|
||||
onBackPressed: VoidCallback
|
||||
): OnBackPressedCallback = (object : OnBackPressedCallback(isEnabled) {
|
||||
override fun handleOnBackPressed() {
|
||||
onBackPressed()
|
||||
|
|
@ -70,10 +68,11 @@ fun FragmentActivity.addOnBackPressedDispatcher(
|
|||
|
||||
private fun fragmentFactory(screen: AppScreen): Fragment {
|
||||
return when (screen) {
|
||||
// AppScreen.Home -> TestLeapfrogFragment()
|
||||
AppScreen.Home -> HomeFragment()
|
||||
AppScreen.OnboardingNote -> OnboardingNoteFragment()
|
||||
AppScreen.OnboardingWallet -> OnboardingWalletFragment()
|
||||
AppScreen.OnboardingTwins -> OnboardingTwinsFragment()
|
||||
AppScreen.OnboardingTwins -> TwinsCardsFragment()
|
||||
AppScreen.OnboardingOther -> OnboardingOtherCardsFragment()
|
||||
AppScreen.Wallet -> WalletFragment()
|
||||
AppScreen.Send -> SendFragment()
|
||||
|
|
@ -81,8 +80,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
AppScreen.DetailsConfirm -> DetailsConfirmFragment()
|
||||
AppScreen.DetailsSecurity -> DetailsSecurityFragment()
|
||||
AppScreen.Disclaimer -> DisclaimerFragment()
|
||||
AppScreen.CreateTwinWalletWarning -> TwinWalletWarningFragment()
|
||||
AppScreen.CreateTwinWallet -> CreateTwinWalletFragment()
|
||||
AppScreen.AddTokens -> AddTokensFragment()
|
||||
AppScreen.WalletDetails -> WalletDetailsFragment()
|
||||
AppScreen.WalletConnectSessions -> WalletConnectSessionsFragment()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.tap.common.redux.StateDialog
|
|||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -17,6 +18,10 @@ fun Store<*>.dispatchOnMain(action: Action) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun Store<*>.onCardScanned(scanResponse: ScanResponse) {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(scanResponse)
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchOpenUrl(url: String) {
|
||||
store.dispatch(NavigationAction.OpenUrl(url))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import android.view.animation.AccelerateDecelerateInterpolator
|
|||
import android.widget.FrameLayout
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.view.children
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tangem_sdk_new.extensions.dpToPx
|
||||
|
||||
/**
|
||||
|
|
@ -15,9 +16,9 @@ class LeapfrogWidget(
|
|||
private val parentContainer: FrameLayout,
|
||||
private val calculator: PropertyCalculator = PropertyCalculator(),
|
||||
) {
|
||||
private val foldAnimationDuration = 500L
|
||||
private val leapAnimationDuration = 800L
|
||||
private val leapBackAnimationDuration = 800L
|
||||
private val foldAnimationDuration = 300L
|
||||
private val leapAnimationDuration = 500L
|
||||
private val leapBackAnimationDuration = 500L
|
||||
|
||||
private val leapViews = mutableListOf<LeapView>()
|
||||
|
||||
|
|
@ -25,9 +26,6 @@ class LeapfrogWidget(
|
|||
private var leapProgress: Int = 100
|
||||
private var leapBackProgress: Int = 100
|
||||
|
||||
var isFolded: Boolean = true
|
||||
private set
|
||||
|
||||
init {
|
||||
calculator.setTranslationConverter { parentContainer.dpToPx(it) }
|
||||
if (viewIsFullFledged()) {
|
||||
|
|
@ -43,12 +41,11 @@ class LeapfrogWidget(
|
|||
* Apply initial properties to the views
|
||||
*/
|
||||
fun initViews() {
|
||||
isFolded = true
|
||||
leapViews.forEach { it.initView() }
|
||||
}
|
||||
|
||||
fun fold(onEnd: () -> Unit = {}) {
|
||||
val animator = foldAnimator()
|
||||
fun fold(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
val animator = foldAnimator(animate)
|
||||
if (animator == null) {
|
||||
onEnd()
|
||||
} else {
|
||||
|
|
@ -57,8 +54,8 @@ class LeapfrogWidget(
|
|||
}
|
||||
}
|
||||
|
||||
fun unfold(onEnd: () -> Unit = {}) {
|
||||
val animator = unfoldAnimator()
|
||||
fun unfold(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
val animator = unfoldAnimator(animate)
|
||||
if (animator == null) {
|
||||
onEnd()
|
||||
} else {
|
||||
|
|
@ -67,33 +64,31 @@ class LeapfrogWidget(
|
|||
}
|
||||
}
|
||||
|
||||
fun foldAnimator(): Animator? {
|
||||
if (!canFoldUnfold() || isFolded) return null
|
||||
fun foldAnimator(animate: Boolean = true): Animator? {
|
||||
if (!canFoldUnfold()) return null
|
||||
|
||||
return createFoldUnfoldAnimator(true, foldAnimationDuration)
|
||||
return createFoldUnfoldAnimator(true, animate, foldAnimationDuration)
|
||||
}
|
||||
|
||||
fun unfoldAnimator(): Animator? {
|
||||
if (!canFoldUnfold() || !isFolded) return null
|
||||
fun unfoldAnimator(animate: Boolean = true): Animator? {
|
||||
if (!canFoldUnfold()) return null
|
||||
|
||||
return createFoldUnfoldAnimator(false, foldAnimationDuration)
|
||||
return createFoldUnfoldAnimator(false, animate, foldAnimationDuration)
|
||||
}
|
||||
|
||||
private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, duration: Long): Animator {
|
||||
private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, animate: Boolean, duration: Long): Animator {
|
||||
val animatorsList = mutableListOf<Animator>()
|
||||
val animateDuration = if (animate) duration else 0
|
||||
leapViews.forEach {
|
||||
if (isFoldAnimation) {
|
||||
it.fold()
|
||||
animatorsList.add(it.view.foldAnimation(duration, it.state.properties))
|
||||
animatorsList.add(it.view.foldAnimation(animateDuration, it.state.properties))
|
||||
} else {
|
||||
it.unfold()
|
||||
animatorsList.add(it.view.unfoldAnimation(duration, it.state.properties))
|
||||
animatorsList.add(it.view.unfoldAnimation(animateDuration, it.state.properties))
|
||||
}
|
||||
}
|
||||
animatorsList.add(createProgressListener(duration) {
|
||||
foldUnfoldProgress = it
|
||||
if (it == 100) isFolded = isFoldAnimation
|
||||
})
|
||||
animatorsList.add(createProgressListener(animateDuration) { foldUnfoldProgress = it })
|
||||
|
||||
val animator = AnimatorSet()
|
||||
animator.interpolator = AccelerateDecelerateInterpolator()
|
||||
|
|
@ -101,13 +96,13 @@ class LeapfrogWidget(
|
|||
return animator
|
||||
}
|
||||
|
||||
fun leap(onEnd: () -> Unit = {}) {
|
||||
fun leap(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
if (!canLeap()) {
|
||||
onEnd()
|
||||
return
|
||||
}
|
||||
|
||||
val duration = leapAnimationDuration
|
||||
val duration = if (animate) leapAnimationDuration else 0
|
||||
val animatorsList = mutableListOf<Animator>()
|
||||
leapViews.forEach {
|
||||
when (it.leap()) {
|
||||
|
|
@ -128,13 +123,13 @@ class LeapfrogWidget(
|
|||
animator.start()
|
||||
}
|
||||
|
||||
fun leapBack(onEnd: () -> Unit = {}) {
|
||||
fun leapBack(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
if (!canLeapBack()) {
|
||||
onEnd()
|
||||
return
|
||||
}
|
||||
|
||||
val duration = leapBackAnimationDuration
|
||||
val duration = if (animate) leapBackAnimationDuration else 0
|
||||
val animatorsList = mutableListOf<Animator>()
|
||||
leapViews.forEach {
|
||||
when (it.leapBack()) {
|
||||
|
|
@ -160,12 +155,11 @@ class LeapfrogWidget(
|
|||
|
||||
fun getViewByPosition(position: Int): LeapView = leapViews.first { it.state.currentPosition == position }
|
||||
|
||||
fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(isFolded, leapViews.map { it.state })
|
||||
fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(leapViews.map { it.state })
|
||||
|
||||
fun applyState(state: LeapfrogWidgetState) {
|
||||
isFolded = state.isFolded
|
||||
state.leapViewStates.forEach { state ->
|
||||
leapViews.firstOrNull { it.index == state.index }?.applyState(state)
|
||||
state.leapViewStates.forEach { viewState ->
|
||||
leapViews.firstOrNull { it.index == viewState.index }?.applyState(viewState)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +177,6 @@ class LeapfrogWidget(
|
|||
private fun canLeap(): Boolean {
|
||||
return when {
|
||||
!viewIsFullFledged() -> false
|
||||
isFolded -> false
|
||||
leapBackInProgress() -> false
|
||||
leapProgress < 70 -> false
|
||||
else -> true
|
||||
|
|
@ -193,7 +186,6 @@ class LeapfrogWidget(
|
|||
private fun canLeapBack(): Boolean {
|
||||
return when {
|
||||
!viewIsFullFledged() -> false
|
||||
isFolded -> false
|
||||
leapInProgress() -> false
|
||||
leapBackProgress < 70 -> false
|
||||
else -> true
|
||||
|
|
@ -206,14 +198,13 @@ class LeapfrogWidget(
|
|||
}
|
||||
|
||||
data class LeapfrogWidgetState(
|
||||
val isFolded: Boolean,
|
||||
val leapViewStates: List<LeapViewState>
|
||||
)
|
||||
|
||||
class PropertyCalculator(
|
||||
val elevationFactor: Float = 1f,
|
||||
val decreaseScaleFactor: Float = 0.1f,
|
||||
val yTranslationFactor: Float = 20f,
|
||||
val decreaseScaleFactor: Float = 0.15f,
|
||||
val yTranslationFactor: Float = 35f,
|
||||
val decreaseOverLiftingFactor: Float = 0.85f,
|
||||
) {
|
||||
// 0 view it is view on the top of stack, but it is last in parent.children
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer
|
|||
import com.tangem.tap.features.home.redux.HomeReducer
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsReducer
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsReducer
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer
|
||||
import com.tangem.tap.features.send.redux.reducers.SendScreenReducer
|
||||
import com.tangem.tap.features.tokens.redux.TokensReducer
|
||||
|
|
@ -19,18 +20,19 @@ fun appReducer(action: Action, state: AppState?): AppState {
|
|||
if (action is AppAction.RestoreState) return action.state
|
||||
|
||||
return AppState(
|
||||
navigationState = NavigationReducer.reduce(action, state),
|
||||
globalState = globalReducer(action, state),
|
||||
homeState = HomeReducer.reduce(action, state),
|
||||
onboardingNoteState = OnboardingNoteReducer.reduce(action, state),
|
||||
onboardingWalletState = OnboardingWalletReducer.reduce(action, state),
|
||||
onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state),
|
||||
walletState = WalletReducer.reduce(action, state),
|
||||
sendState = SendScreenReducer.reduce(action, state.sendState),
|
||||
detailsState = DetailsReducer.reduce(action, state),
|
||||
disclaimerState = DisclaimerReducer.reduce(action, state),
|
||||
tokensState = TokensReducer.reduce(action, state),
|
||||
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState)
|
||||
navigationState = NavigationReducer.reduce(action, state),
|
||||
globalState = globalReducer(action, state),
|
||||
homeState = HomeReducer.reduce(action, state),
|
||||
onboardingNoteState = OnboardingNoteReducer.reduce(action, state),
|
||||
onboardingWalletState = OnboardingWalletReducer.reduce(action, state),
|
||||
onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state),
|
||||
walletState = WalletReducer.reduce(action, state),
|
||||
twinCardsState = TwinCardsReducer.reduce(action, state),
|
||||
sendState = SendScreenReducer.reduce(action, state.sendState),
|
||||
detailsState = DetailsReducer.reduce(action, state),
|
||||
disclaimerState = DisclaimerReducer.reduce(action, state),
|
||||
tokensState = TokensReducer.reduce(action, state),
|
||||
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMidd
|
|||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsMiddleware
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsMiddleware
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
|
||||
import com.tangem.tap.features.send.redux.middlewares.SendMiddleware
|
||||
|
|
@ -35,6 +37,7 @@ data class AppState(
|
|||
val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(),
|
||||
val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(),
|
||||
val walletState: WalletState = WalletState(),
|
||||
val twinCardsState: TwinCardsState = TwinCardsState(),
|
||||
val sendState: SendState = SendState(),
|
||||
val detailsState: DetailsState = DetailsState(),
|
||||
val disclaimerState: DisclaimerState = DisclaimerState(),
|
||||
|
|
@ -45,18 +48,19 @@ data class AppState(
|
|||
companion object {
|
||||
fun getMiddleware(): List<Middleware<AppState>> {
|
||||
return listOf(
|
||||
logMiddleware, navigationMiddleware, notificationsMiddleware,
|
||||
GlobalMiddleware.handler,
|
||||
HomeMiddleware.handler,
|
||||
OnboardingNoteMiddleware.handler,
|
||||
OnboardingWalletMiddleware.handler,
|
||||
OnboardingOtherCardsMiddleware.handler,
|
||||
WalletMiddleware().walletMiddleware,
|
||||
SendMiddleware().sendMiddleware,
|
||||
DetailsMiddleware().detailsMiddleware,
|
||||
DisclaimerMiddleware().disclaimerMiddleware,
|
||||
TokensMiddleware().tokensMiddleware,
|
||||
WalletConnectMiddleware().walletConnectMiddleware
|
||||
logMiddleware, navigationMiddleware, notificationsMiddleware,
|
||||
GlobalMiddleware.handler,
|
||||
HomeMiddleware.handler,
|
||||
OnboardingNoteMiddleware.handler,
|
||||
OnboardingWalletMiddleware.handler,
|
||||
OnboardingOtherCardsMiddleware.handler,
|
||||
WalletMiddleware().walletMiddleware,
|
||||
TwinCardsMiddleware.handler,
|
||||
SendMiddleware().sendMiddleware,
|
||||
DetailsMiddleware().detailsMiddleware,
|
||||
DisclaimerMiddleware().disclaimerMiddleware,
|
||||
TokensMiddleware().tokensMiddleware,
|
||||
WalletConnectMiddleware().walletConnectMiddleware
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.common.redux
|
||||
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
|
||||
/**
|
||||
|
|
@ -11,7 +12,7 @@ sealed class AppDialog : StateDialog {
|
|||
object ScanFailsDialog : AppDialog()
|
||||
data class AddressInfoDialog(
|
||||
val addressData: AddressData,
|
||||
val onCopyAddress: () -> Unit,
|
||||
val onExploreAddress: () -> Unit
|
||||
val onCopyAddress: VoidCallback,
|
||||
val onExploreAddress: VoidCallback
|
||||
) : AppDialog()
|
||||
}
|
||||
|
|
@ -23,6 +23,9 @@ sealed class GlobalAction : Action {
|
|||
data class ShowErrorNotification(override val error: TapError) : GlobalAction(), ErrorAction
|
||||
data class DebugShowErrorNotification(override val error: TapError) : GlobalAction(), DebugErrorAction
|
||||
|
||||
// android resources
|
||||
data class SetResources(val resources: AndroidResources) : GlobalAction()
|
||||
|
||||
// dialogs
|
||||
data class ShowDialog(val stateDialog: StateDialog) : GlobalAction()
|
||||
object HideDialog : GlobalAction()
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState
|
|||
}
|
||||
is GlobalAction.RestoreAppCurrency -> {
|
||||
store.dispatch(GlobalAction.RestoreAppCurrency.Success(
|
||||
preferencesStorage.getAppCurrency()
|
||||
preferencesStorage.getAppCurrency()
|
||||
))
|
||||
}
|
||||
is GlobalAction.HideWarningMessage -> {
|
||||
|
|
@ -81,7 +81,7 @@ private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState
|
|||
val userStatusResponse = MoonpayService().getUserStatus(apiKey)
|
||||
if (userStatusResponse is Result.Success) {
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data)
|
||||
GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState
|
|||
withMainContext {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card)
|
||||
tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data)
|
||||
action.onSuccess?.invoke(result.data)
|
||||
}
|
||||
is CompletionResult.Failure -> action.onFailure?.invoke(result.error)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
|
|||
val globalState = state.globalState
|
||||
|
||||
return when (action) {
|
||||
is GlobalAction.SetResources -> {
|
||||
globalState.copy(resources = action.resources)
|
||||
}
|
||||
is GlobalAction.Onboarding.Start -> {
|
||||
val usedCardsPrefStorage = preferencesStorage.usedCardsPrefStorage
|
||||
val onboardingState = OnboardingManager(action.scanResponse, usedCardsPrefStorage)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,17 @@ data class GlobalState(
|
|||
val scanCardFailsCounter: Int = 0,
|
||||
val dialog: StateDialog? = null,
|
||||
val moonPayUserStatus: MoonPayUserStatus? = null,
|
||||
val resources: AndroidResources = AndroidResources(),
|
||||
) : StateType
|
||||
|
||||
|
||||
data class AndroidResources(
|
||||
val strings: RString = RString()
|
||||
) {
|
||||
data class RString(
|
||||
val addressWasCopied: Int = -1,
|
||||
val walletIsNotEmpty: Int = -1
|
||||
)
|
||||
}
|
||||
typealias CryptoCurrencyName = String
|
||||
typealias FiatCurrencyName = String
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.tap.common.redux.navigation
|
||||
|
||||
import android.view.View
|
||||
import androidx.transition.TransitionSet
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class FragmentShareTransition(
|
||||
val shareElements: List<ShareElement>,
|
||||
val enterTransitionSet: TransitionSet,
|
||||
val exitTransitionSet: TransitionSet
|
||||
)
|
||||
|
||||
/**
|
||||
* For ease of use, the name is used as transitionName\name into the FragmentTransaction.addSharedElement
|
||||
*/
|
||||
class ShareElement(view: View, name: String? = null) {
|
||||
|
||||
val wView: WeakReference<View>
|
||||
val elementName: String
|
||||
|
||||
init {
|
||||
name?.let { view.transitionName = it }
|
||||
elementName = view.transitionName
|
||||
?: throw UnsupportedOperationException("ShareElement require the name")
|
||||
wView = WeakReference(view)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val imvFrontCard = "imv_front_card"
|
||||
val imvBackCard = "imv_back_card"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
package com.tangem.tap.common.redux.navigation
|
||||
|
||||
import android.view.View
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.transition.TransitionSet
|
||||
import org.rekotlin.Action
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
|
|
@ -19,12 +17,4 @@ sealed class NavigationAction : Action {
|
|||
|
||||
data class ActivityCreated(val activity: WeakReference<FragmentActivity>) : NavigationAction()
|
||||
object ActivityDestroyed : NavigationAction()
|
||||
}
|
||||
|
||||
data class FragmentShareTransition(
|
||||
val shareElements: List<ShareElement>,
|
||||
val enterTransitionSet: TransitionSet,
|
||||
val exitTransitionSet: TransitionSet
|
||||
)
|
||||
|
||||
data class ShareElement(val wView: WeakReference<View>, val name: String)
|
||||
}
|
||||
|
|
@ -20,8 +20,7 @@ private fun internalReduce(action: Action, state: AppState): NavigationState {
|
|||
navState.copy(backStack = navState.backStack + navigationAction.screen)
|
||||
}
|
||||
is NavigationAction.PopBackTo -> {
|
||||
val screen =
|
||||
navigationAction.screen ?: navState.activity?.get()?.getPreviousScreen()
|
||||
val screen = navigationAction.screen ?: navState.activity?.get()?.getPreviousScreen()
|
||||
val index = navState.backStack.lastIndexOf(screen) + 1
|
||||
state.navigationState.copy(backStack = navState.backStack.subList(0, index))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import org.rekotlin.StateType
|
|||
import java.lang.ref.WeakReference
|
||||
|
||||
data class NavigationState(
|
||||
val backStack: List<AppScreen> = listOf(AppScreen.Home),
|
||||
val activity: WeakReference<FragmentActivity>? = null
|
||||
val backStack: List<AppScreen> = listOf(AppScreen.Home),
|
||||
val activity: WeakReference<FragmentActivity>? = null
|
||||
) : StateType
|
||||
|
||||
enum class AppScreen {
|
||||
|
|
@ -16,7 +16,6 @@ enum class AppScreen {
|
|||
Wallet, WalletDetails,
|
||||
Send,
|
||||
Details, DetailsConfirm, DetailsSecurity,
|
||||
CreateTwinWalletWarning, CreateTwinWallet,
|
||||
AddTokens,
|
||||
WalletConnectSessions,
|
||||
QrScan
|
||||
|
|
|
|||
|
|
@ -41,30 +41,31 @@ class RefreshBalanceWidget(
|
|||
val progressState = state as? ProgressState ?: return
|
||||
|
||||
val currentStateByView = getState()
|
||||
|
||||
if (progressState == currentStateByView) return
|
||||
when {
|
||||
currentStateByView == progressState -> return
|
||||
currentStateByView == ProgressState.Done -> if (progressState == ProgressState.Error) return
|
||||
}
|
||||
|
||||
animateState(progressState)
|
||||
viewSwitcher.showNext()
|
||||
|
||||
}
|
||||
|
||||
private fun animateState(state: ProgressState) {
|
||||
when (state) {
|
||||
ProgressState.Done, ProgressState.Error -> {
|
||||
progressViewAnimation?.cancel()
|
||||
progressViewAnimation = null
|
||||
}
|
||||
ProgressState.Loading -> {
|
||||
val animation = RotateAnimation(
|
||||
0f, 360f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f
|
||||
progressViewAnimation = RotateAnimation(
|
||||
0f, 360f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f
|
||||
)
|
||||
animation.duration = 700
|
||||
animation.interpolator = AccelerateInterpolator()
|
||||
animation.repeatCount = -1
|
||||
arrowView.startAnimation(animation)
|
||||
progressViewAnimation = animation
|
||||
progressViewAnimation?.duration = 700
|
||||
progressViewAnimation?.interpolator = AccelerateInterpolator()
|
||||
progressViewAnimation?.repeatCount = -1
|
||||
progressViewAnimation?.let { arrowView.startAnimation(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -88,10 +89,10 @@ class RefreshBalanceWidget(
|
|||
class ShowAnimation : AnimationSet(true) {
|
||||
init {
|
||||
addAnimation(ScaleAnimation(
|
||||
0f, 1f,
|
||||
0f, 1f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f)
|
||||
0f, 1f,
|
||||
0f, 1f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f)
|
||||
)
|
||||
addAnimation(AlphaAnimation(0f, 1f))
|
||||
duration = 300
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@ package com.tangem.tap.domain
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
enum class ProductType {
|
||||
Note, Wallet, Twin, Other
|
||||
Note, Wallet, Twins, Other
|
||||
}
|
||||
|
|
@ -23,7 +23,6 @@ import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
|||
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanProductTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.twins.isTangemTwin
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -45,9 +44,9 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
|
||||
suspend fun createProductWallet(scanResponse: ScanResponse): CompletionResult<Card> {
|
||||
return runTaskAsync(
|
||||
CreateProductWalletAndRescanTask(scanResponse.productType),
|
||||
scanResponse.card.cardId,
|
||||
Message(context.getString(R.string.initial_message_create_wallet_body))
|
||||
CreateProductWalletAndRescanTask(scanResponse.productType),
|
||||
scanResponse.card.cardId,
|
||||
Message(context.getString(R.string.initial_message_create_wallet_body))
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -105,13 +104,13 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
suspend fun <T : CommandResponse> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>, cardId: String? = null, initialMessage: Message? = null,
|
||||
): CompletionResult<T> =
|
||||
withContext(Dispatchers.Main) {
|
||||
suspendCoroutine { continuation ->
|
||||
tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result ->
|
||||
continuation.resume(result)
|
||||
withContext(Dispatchers.Main) {
|
||||
suspendCoroutine { continuation ->
|
||||
tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage) { result ->
|
||||
continuation.resume(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T : CommandResponse> runTaskAsyncReturnOnMain(
|
||||
runnable: CardSessionRunnable<T>, cardId: String? = null, initialMessage: Message? = null,
|
||||
|
|
@ -120,8 +119,8 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
|
|||
return withContext(Dispatchers.Main) { result }
|
||||
}
|
||||
|
||||
fun changeDisplayedCardIdNumbersCount(card: Card) {
|
||||
tangemSdk.config.cardIdDisplayedNumbersCount = if (card.isTangemTwin()) 4 else null
|
||||
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse) {
|
||||
tangemSdk.config.cardIdDisplayedNumbersCount = if (scanResponse.isTangemTwins()) 4 else null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ import com.tangem.tap.domain.configurable.config.ConfigManager
|
|||
import com.tangem.tap.domain.extensions.*
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.tokens.CardCurrencies
|
||||
import com.tangem.tap.domain.twins.getTwinCardNumber
|
||||
import com.tangem.tap.domain.twins.isTangemTwin
|
||||
import com.tangem.tap.features.tokens.redux.TokensAction
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
|
|
@ -59,8 +57,8 @@ class TapWalletManager {
|
|||
suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName, wallet: Wallet) {
|
||||
Timber.d(wallet.getTokens().toString())
|
||||
val currencies = wallet.getTokens()
|
||||
.map { Currency.Token(it) }
|
||||
.plus(Currency.Blockchain(wallet.blockchain))
|
||||
.map { Currency.Token(it) }
|
||||
.plus(Currency.Blockchain(wallet.blockchain))
|
||||
loadFiatRate(fiatCurrency, currencies)
|
||||
}
|
||||
|
||||
|
|
@ -89,11 +87,6 @@ class TapWalletManager {
|
|||
store.dispatch(GlobalAction.SaveScanNoteResponse(data))
|
||||
store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard))
|
||||
store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed))
|
||||
if (data.card.isTangemTwin()) {
|
||||
data.card.getTwinCardNumber()?.let {
|
||||
store.dispatch(WalletAction.TwinsAction.SetTwinCard(it, true))
|
||||
}
|
||||
}
|
||||
|
||||
val blockchain = data.getBlockchain()
|
||||
if (blockchain == Blockchain.Ethereum ||
|
||||
|
|
@ -122,56 +115,47 @@ class TapWalletManager {
|
|||
|
||||
suspend fun loadData(data: ScanResponse) {
|
||||
withContext(Dispatchers.Main) {
|
||||
|
||||
store.dispatch(WalletAction.LoadCardInfo(data.card))
|
||||
getActionIfUnknownBlockchainOrEmptyWallet(data)?.let {
|
||||
store.dispatch(it)
|
||||
return@withContext
|
||||
}
|
||||
|
||||
when {
|
||||
data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
|
||||
store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain))
|
||||
val config = store.state.globalState.configManager?.config ?: return@withContext
|
||||
val blockchain = data.getBlockchain()
|
||||
val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data)
|
||||
|
||||
if (blockchain != Blockchain.Unknown && primaryWalletManager != null) {
|
||||
val primaryToken = data.getPrimaryToken()
|
||||
|
||||
store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain))
|
||||
if (primaryToken != null) {
|
||||
primaryWalletManager.addToken(primaryToken)
|
||||
store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken))
|
||||
}
|
||||
data.card.wallets.isEmpty() ||
|
||||
(data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> {
|
||||
store.dispatch(WalletAction.EmptyWallet)
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data.card, blockchain, primaryWalletManager)
|
||||
} else {
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(blockchain)))
|
||||
}
|
||||
else -> {
|
||||
val config = store.state.globalState.configManager?.config ?: return@withContext
|
||||
|
||||
val blockchain = data.getBlockchain()
|
||||
val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data)
|
||||
|
||||
if (blockchain != Blockchain.Unknown && primaryWalletManager != null) {
|
||||
val primaryToken = data.getPrimaryToken()
|
||||
|
||||
store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain))
|
||||
if (primaryToken != null) {
|
||||
primaryWalletManager.addToken(primaryToken)
|
||||
store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken))
|
||||
}
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data.card, blockchain, primaryWalletManager)
|
||||
} else {
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(blockchain)))
|
||||
}
|
||||
|
||||
} else {
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data.card, blockchain, null)
|
||||
}
|
||||
}
|
||||
val moonPayUserStatus = store.state.globalState.moonPayUserStatus
|
||||
store.dispatch(WalletAction.LoadWallet(
|
||||
allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true,
|
||||
allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true,
|
||||
))
|
||||
store.dispatch(WalletAction.LoadFiatRate())
|
||||
} else {
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data.card, blockchain, null)
|
||||
}
|
||||
}
|
||||
val moonPayUserStatus = store.state.globalState.moonPayUserStatus
|
||||
store.dispatch(WalletAction.LoadWallet(
|
||||
allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true,
|
||||
allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true,
|
||||
))
|
||||
store.dispatch(WalletAction.LoadFiatRate())
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadMultiWalletData(
|
||||
card: Card, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager?
|
||||
card: Card, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager?
|
||||
) {
|
||||
val primaryTokens = primaryWalletManager?.cardTokens ?: emptySet()
|
||||
val savedCurrencies = currenciesRepository.loadCardCurrencies(card.cardId)
|
||||
|
|
@ -179,9 +163,9 @@ class TapWalletManager {
|
|||
if (savedCurrencies == null) {
|
||||
if (primaryBlockchain != null && primaryWalletManager != null) {
|
||||
store.dispatch(WalletAction.MultiWallet.SaveCurrencies(
|
||||
CardCurrencies(
|
||||
blockchains = setOf(primaryBlockchain), tokens = primaryTokens
|
||||
)))
|
||||
CardCurrencies(
|
||||
blockchains = setOf(primaryBlockchain), tokens = primaryTokens
|
||||
)))
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain)))
|
||||
store.dispatch(WalletAction.MultiWallet.AddTokens(primaryTokens.toList()))
|
||||
|
|
@ -191,7 +175,7 @@ class TapWalletManager {
|
|||
CardCurrencies(blockchains = blockchains, tokens = emptySet())
|
||||
))
|
||||
val walletManagers =
|
||||
walletManagerFactory.makeWalletManagersForApp(card, blockchains.toList())
|
||||
walletManagerFactory.makeWalletManagersForApp(card, blockchains.toList())
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManagers))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(blockchains.toList()))
|
||||
}
|
||||
|
|
@ -200,12 +184,12 @@ class TapWalletManager {
|
|||
} else {
|
||||
val blockchains = savedCurrencies.blockchains.toList()
|
||||
val walletManagers = if (
|
||||
primaryTokens.isNotEmpty() &&
|
||||
primaryWalletManager != null && primaryBlockchain != null
|
||||
primaryTokens.isNotEmpty() &&
|
||||
primaryWalletManager != null && primaryBlockchain != null
|
||||
) {
|
||||
val blockchainsWithoutPrimary = blockchains.filterNot { it == primaryBlockchain }
|
||||
walletManagerFactory.makeWalletManagersForApp(card, blockchainsWithoutPrimary)
|
||||
.plus(primaryWalletManager)
|
||||
.plus(primaryWalletManager)
|
||||
} else {
|
||||
walletManagerFactory.makeWalletManagersForApp(card, blockchains)
|
||||
}
|
||||
|
|
@ -218,28 +202,38 @@ class TapWalletManager {
|
|||
|
||||
suspend fun reloadData(data: ScanResponse) {
|
||||
withContext(Dispatchers.Main) {
|
||||
when {
|
||||
data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
|
||||
store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain))
|
||||
}
|
||||
data.card.wallets.isEmpty() ||
|
||||
(data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> {
|
||||
store.dispatch(WalletAction.EmptyWallet)
|
||||
}
|
||||
else -> {
|
||||
if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) {
|
||||
store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection))
|
||||
return@withContext
|
||||
}
|
||||
val config = store.state.globalState.configManager?.config ?: return@withContext
|
||||
val moonpayUserStatus = store.state.globalState.moonPayUserStatus
|
||||
store.dispatch(WalletAction.LoadWallet(
|
||||
allowToBuy = config.isTopUpEnabled && moonpayUserStatus?.isBuyAllowed == true,
|
||||
allowToSell = config.isTopUpEnabled && moonpayUserStatus?.isSellAllowed == true,
|
||||
))
|
||||
store.dispatch(WalletAction.LoadFiatRate())
|
||||
}
|
||||
getActionIfUnknownBlockchainOrEmptyWallet(data)?.let {
|
||||
store.dispatch(it)
|
||||
return@withContext
|
||||
}
|
||||
if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) {
|
||||
store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection))
|
||||
return@withContext
|
||||
}
|
||||
|
||||
val config = store.state.globalState.configManager?.config ?: return@withContext
|
||||
val moonpayUserStatus = store.state.globalState.moonPayUserStatus
|
||||
store.dispatch(WalletAction.LoadWallet(
|
||||
allowToBuy = config.isTopUpEnabled && moonpayUserStatus?.isBuyAllowed == true,
|
||||
allowToSell = config.isTopUpEnabled && moonpayUserStatus?.isSellAllowed == true,
|
||||
))
|
||||
store.dispatch(WalletAction.LoadFiatRate())
|
||||
}
|
||||
}
|
||||
|
||||
private fun getActionIfUnknownBlockchainOrEmptyWallet(data: ScanResponse): WalletAction? {
|
||||
return when {
|
||||
// check order is important
|
||||
data.isTangemTwins() && !data.twinsIsTwinned() -> {
|
||||
WalletAction.EmptyWallet
|
||||
}
|
||||
data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> {
|
||||
WalletAction.LoadData.Failure(TapError.UnknownBlockchain)
|
||||
}
|
||||
data.card.wallets.isEmpty() -> {
|
||||
WalletAction.EmptyWallet
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,10 +28,13 @@ object TapWorkarounds {
|
|||
return excludedBatch || excludedIssuerName
|
||||
}
|
||||
|
||||
fun Card.isTangemNote(): Boolean = tangemNoteBatches.contains(batchId)
|
||||
fun Card.isTangemWallet(): Boolean = tangemWalletBatches.contains(batchId)
|
||||
@Deprecated("Use ScanResponse.isTangemNote")
|
||||
fun isTangemNote(card: Card): Boolean = tangemNoteBatches.contains(card.batchId)
|
||||
|
||||
fun Card.getTangemNoteBlockchain(): Blockchain? = tangemNoteBatches[batchId]
|
||||
@Deprecated("Use ScanResponse.isTangemWallet")
|
||||
fun isTangemWallet(card: Card): Boolean = tangemWalletBatches.contains(card.batchId)
|
||||
|
||||
fun getTangemNoteBlockchain(card: Card): Blockchain? = tangemNoteBatches[card.batchId]
|
||||
|
||||
private const val START_2_COIN_ISSUER = "start2coin"
|
||||
private const val TEST_CARD_BATCH = "99FF"
|
||||
|
|
@ -55,7 +58,7 @@ object TapWorkarounds {
|
|||
"AB02" to Blockchain.Ethereum,
|
||||
"AB03" to Blockchain.CardanoShelley,
|
||||
"AB04" to Blockchain.Dogecoin,
|
||||
"AB05" to Blockchain.Binance,
|
||||
"AB05" to Blockchain.BSC,
|
||||
"AB06" to Blockchain.XRP,
|
||||
)
|
||||
}
|
||||
|
|
@ -64,7 +67,7 @@ val DELAY_SDK_DIALOG_CLOSE = 1400L
|
|||
|
||||
val Card.isMultiwalletAllowed: Boolean
|
||||
get() {
|
||||
return !isTangemTwin() && !isStart2Coin && !isTangemNote()
|
||||
return !isTangemTwin() && !isStart2Coin && !isTangemNote(this)
|
||||
&& (firmwareVersion >= FirmwareVersion.MultiWalletAvailable ||
|
||||
getSingleWallet()?.curve == EllipticCurve.Secp256k1)
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ data class Config(
|
|||
val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(),
|
||||
val isSendingToPayIdEnabled: Boolean = true,
|
||||
val isTopUpEnabled: Boolean = false,
|
||||
@Deprecated("Not relevant since version 3.23")
|
||||
val isCreatingTwinCardsAllowed: Boolean = false
|
||||
)
|
||||
|
||||
|
|
@ -50,10 +51,10 @@ class ConfigManager(
|
|||
fun resetToDefault(name: String) {
|
||||
when (name) {
|
||||
isSendingToPayIdEnabled -> config =
|
||||
config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled)
|
||||
config.copy(isSendingToPayIdEnabled = defaultConfig.isSendingToPayIdEnabled)
|
||||
isTopUpEnabled -> config = config.copy(isTopUpEnabled = defaultConfig.isTopUpEnabled)
|
||||
isCreatingTwinCardsAllowed -> config =
|
||||
config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed)
|
||||
config.copy(isCreatingTwinCardsAllowed = defaultConfig.isCreatingTwinCardsAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ fun Blockchain.isNoAccountError(exception: Throwable): Boolean {
|
|||
}
|
||||
}
|
||||
|
||||
fun Blockchain.amountToCreateAccount(token: Token? = null): Double? {
|
||||
fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? {
|
||||
return when (this) {
|
||||
Blockchain.Stellar -> if (token?.symbol == NODL) 1.5 else 1.toDouble()
|
||||
Blockchain.XRP -> 10.toDouble()
|
||||
Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(1.5) else BigDecimal.ZERO
|
||||
Blockchain.XRP -> BigDecimal(10)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import com.tangem.common.card.EllipticCurve
|
|||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.tap.domain.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.twins.isTangemTwin
|
||||
|
||||
fun WalletManagerFactory.makeWalletManagerForApp(
|
||||
card: Card,
|
||||
|
|
@ -65,7 +64,7 @@ fun WalletManagerFactory.makePrimaryWalletManager(
|
|||
val publicKey = wallet?.publicKey ?: return null
|
||||
val curveToUse = wallet.curve
|
||||
|
||||
return if (card.isTangemTwin() && data.secondTwinPublicKey != null) {
|
||||
return if (data.isTangemTwins() && data.secondTwinPublicKey != null) {
|
||||
makeMultisigWalletManager(
|
||||
cardId = card.cardId,
|
||||
walletPublicKey = publicKey, pairPublicKey = data.secondTwinPublicKey.hexToBytes(),
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
package com.tangem.tap.domain.tasks
|
||||
|
||||
import com.tangem.*
|
||||
import com.tangem.blockchain.common.BlockchainSdkConfig
|
||||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.core.CardSession
|
||||
import com.tangem.common.core.CardSessionRunnable
|
||||
import com.tangem.common.core.TangemError
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
|
||||
import com.tangem.tap.domain.ProductType
|
||||
import com.tangem.tap.domain.TapSdkError
|
||||
import com.tangem.tap.domain.TapWorkarounds.isExcluded
|
||||
import com.tangem.tap.domain.TapWorkarounds.isTangemNote
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.domain.twins.isTangemTwin
|
||||
import com.tangem.tap.store
|
||||
|
||||
class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanResponse> {
|
||||
|
||||
override fun run(
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit,
|
||||
) {
|
||||
ScanTask().run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
|
||||
is CompletionResult.Success -> {
|
||||
val card = this.card ?: result.data
|
||||
|
||||
val error = getErrorIfExcludedCard(card)
|
||||
if (error != null) {
|
||||
callback(CompletionResult.Failure(error))
|
||||
return@run
|
||||
}
|
||||
|
||||
if (card.isTangemTwin()) {
|
||||
dealWithTwinCard(card, session, callback)
|
||||
} else if (!card.isTangemNote() && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) {
|
||||
createMissingWalletsIfNeeded(card, session, callback)
|
||||
} else {
|
||||
callback(CompletionResult.Success(
|
||||
ScanResponse(card, ProductType.Other, session.environment.walletData)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMissingWalletsIfNeeded(
|
||||
card: Card, session: CardSession,
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit,
|
||||
) {
|
||||
val walletData = session.environment.walletData
|
||||
if (card.wallets.isEmpty()) {
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData)))
|
||||
return
|
||||
}
|
||||
|
||||
val curvesPresent = card.wallets.map { it.curve }
|
||||
// val curvesToCreate = EllipticCurve.values().subtract(curvesPresent)
|
||||
val curvesToCreate = card.supportedCurves.subtract(curvesPresent)
|
||||
|
||||
if (curvesToCreate.isEmpty()) {
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData)))
|
||||
return
|
||||
}
|
||||
|
||||
CreateWalletsTask(curvesToCreate.toList()).run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success ->
|
||||
callback(CompletionResult.Success(ScanResponse(result.data, ProductType.Other, walletData)))
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun dealWithTwinCard(
|
||||
card: Card, session: CardSession,
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit,
|
||||
) {
|
||||
ReadIssuerDataCommand().run(session) { readDataResult ->
|
||||
when (readDataResult) {
|
||||
is CompletionResult.Success -> {
|
||||
val publicKey = card.getSingleWallet()?.publicKey
|
||||
if (publicKey == null) {
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null)))
|
||||
return@run
|
||||
}
|
||||
val verified = TwinCardsManager.verifyTwinPublicKey(
|
||||
readDataResult.data.issuerData, publicKey
|
||||
)
|
||||
if (verified) {
|
||||
val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65)
|
||||
callback(CompletionResult.Success(
|
||||
ScanResponse(
|
||||
card = card,
|
||||
ProductType.Other,
|
||||
walletData = session.environment.walletData,
|
||||
secondTwinPublicKey = twinPublicKey.toHexString())
|
||||
))
|
||||
return@run
|
||||
} else {
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null)))
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure ->
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getErrorIfExcludedCard(card: Card): TangemError? {
|
||||
if (card.isExcluded()) return TapSdkError.CardForDifferentApp
|
||||
// Disable new cards on the old version of the app // TODO: remove when cards are supported
|
||||
// Disable new multi-currency HD wallet cards on the old version of the app
|
||||
// if (card.isMultiCurrencyWallet()) return UpdateAppToUseThisCard()
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getWalletManagerFactory(): WalletManagerFactory {
|
||||
val blockchainSdkConfig = store.state.globalState.configManager?.config
|
||||
?.blockchainSdkConfig ?: BlockchainSdkConfig()
|
||||
return WalletManagerFactory(blockchainSdkConfig)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ private class CreateProductWalletTask(
|
|||
override fun run(session: CardSession, callback: (result: CompletionResult<CreateWalletResponse>) -> Unit) {
|
||||
val commandProcessor = when (type) {
|
||||
ProductType.Note -> CreateWalletTangemNote()
|
||||
ProductType.Twin -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet")
|
||||
ProductType.Twins -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet")
|
||||
ProductType.Wallet -> CreateWalletTangemWallet()
|
||||
else -> CreateWalletOtherCards()
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ private class CreateWalletTangemNote : ProductCommandProcessor<CreateWalletRespo
|
|||
return
|
||||
}
|
||||
|
||||
val curvesSupportedByBlockchain = card.getTangemNoteBlockchain()?.getSupportedCurves()
|
||||
val curvesSupportedByBlockchain = getTangemNoteBlockchain(card)?.getSupportedCurves()
|
||||
if (curvesSupportedByBlockchain == null || curvesSupportedByBlockchain.isEmpty()) {
|
||||
callback(CompletionResult.Failure(TangemSdkError.CardError()))
|
||||
return
|
||||
|
|
|
|||
|
|
@ -19,13 +19,11 @@ import com.tangem.operations.ScanTask
|
|||
import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand
|
||||
import com.tangem.tap.domain.ProductType
|
||||
import com.tangem.tap.domain.TapSdkError
|
||||
import com.tangem.tap.domain.TapWorkarounds
|
||||
import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain
|
||||
import com.tangem.tap.domain.TapWorkarounds.isExcluded
|
||||
import com.tangem.tap.domain.TapWorkarounds.isTangemNote
|
||||
import com.tangem.tap.domain.TapWorkarounds.isTangemWallet
|
||||
import com.tangem.tap.domain.extensions.getSingleWallet
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.domain.twins.isTangemTwin
|
||||
import com.tangem.tap.domain.twins.TwinsHelper
|
||||
|
||||
data class ScanResponse(
|
||||
val card: Card,
|
||||
|
|
@ -35,7 +33,7 @@ data class ScanResponse(
|
|||
) : CommandResponse {
|
||||
|
||||
fun getBlockchain(): Blockchain {
|
||||
if (productType == ProductType.Note) return card.getTangemNoteBlockchain() ?: return Blockchain.Unknown
|
||||
if (productType == ProductType.Note) return getTangemNoteBlockchain(card) ?: return Blockchain.Unknown
|
||||
val blockchainName: String = walletData?.blockchain ?: return Blockchain.Unknown
|
||||
return Blockchain.fromId(blockchainName)
|
||||
}
|
||||
|
|
@ -43,13 +41,20 @@ data class ScanResponse(
|
|||
fun getPrimaryToken(): Token? {
|
||||
val cardToken = walletData?.token ?: return null
|
||||
return Token(
|
||||
cardToken.name,
|
||||
cardToken.symbol,
|
||||
cardToken.contractAddress,
|
||||
cardToken.decimals,
|
||||
Blockchain.fromId(walletData.blockchain)
|
||||
cardToken.name,
|
||||
cardToken.symbol,
|
||||
cardToken.contractAddress,
|
||||
cardToken.decimals,
|
||||
Blockchain.fromId(walletData.blockchain)
|
||||
)
|
||||
}
|
||||
|
||||
fun isTangemNote(): Boolean = productType == ProductType.Note
|
||||
fun isTangemWallet(): Boolean = productType == ProductType.Wallet
|
||||
fun isTangemTwins(): Boolean = productType == ProductType.Twins
|
||||
fun isTangemOtherCards(): Boolean = productType == ProductType.Other
|
||||
|
||||
fun twinsIsTwinned(): Boolean = card.isTangemTwins() && walletData != null && secondTwinPublicKey != null
|
||||
}
|
||||
|
||||
class ScanProductTask(val card: Card? = null) : CardSessionRunnable<ScanResponse> {
|
||||
|
|
@ -70,9 +75,9 @@ class ScanProductTask(val card: Card? = null) : CardSessionRunnable<ScanResponse
|
|||
}
|
||||
|
||||
val commandProcessor = when {
|
||||
card.isTangemNote() -> ScanNoteProcessor()
|
||||
card.isTangemTwin() -> ScanTwinProcessor()
|
||||
card.isTangemWallet() -> ScanWalletProcessor()
|
||||
TapWorkarounds.isTangemNote(card) -> ScanNoteProcessor()
|
||||
card.isTangemTwins() -> ScanTwinProcessor()
|
||||
TapWorkarounds.isTangemWallet(card) -> ScanWalletProcessor()
|
||||
else -> ScanOtherCardsProcessor()
|
||||
}
|
||||
commandProcessor.proceed(card, session, callback)
|
||||
|
|
@ -89,6 +94,8 @@ class ScanProductTask(val card: Card? = null) : CardSessionRunnable<ScanResponse
|
|||
}
|
||||
}
|
||||
|
||||
private fun Card.isTangemTwins(): Boolean = TwinsHelper.getTwinCardNumber(cardId) != null
|
||||
|
||||
private class ScanNoteProcessor : ProductCommandProcessor<ScanResponse> {
|
||||
override fun proceed(
|
||||
card: Card,
|
||||
|
|
@ -120,21 +127,21 @@ private class ScanTwinProcessor : ProductCommandProcessor<ScanResponse> {
|
|||
is CompletionResult.Success -> {
|
||||
val publicKey = card.getSingleWallet()?.publicKey
|
||||
if (publicKey == null) {
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null)))
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Twins, null)))
|
||||
return@run
|
||||
}
|
||||
val verified = TwinCardsManager.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey)
|
||||
val verified = TwinsHelper.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey)
|
||||
if (verified) {
|
||||
val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65)
|
||||
val walletData = session.environment.walletData
|
||||
val response = ScanResponse(card, ProductType.Twin, walletData, twinPublicKey.toHexString())
|
||||
val response = ScanResponse(card, ProductType.Twins, walletData, twinPublicKey.toHexString())
|
||||
callback(CompletionResult.Success(response))
|
||||
} else {
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null)))
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Twins, null)))
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure ->
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null)))
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Twins, null)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,63 +1,52 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.Message
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.KeyPair
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.crypto.CryptoUtils
|
||||
import com.tangem.operations.wallet.CreateWalletResponse
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.readAssetAsString
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.network.createMoshi
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
|
||||
class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) {
|
||||
class TwinCardsManager(private val card: Card, assetReader: AssetReader) {
|
||||
|
||||
private val currentCardId: String = scanResponse.card.cardId
|
||||
private val currentCardId: String = card.cardId
|
||||
|
||||
private var currentCardPublicKey: String? = null
|
||||
private var secondCardPublicKey: String? = null
|
||||
|
||||
private val issuerKeyPair: KeyPair = getIssuerKeys(
|
||||
context, scanResponse.card.issuer.publicKey.toHexString()
|
||||
)
|
||||
private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, card.issuer.publicKey.toHexString())
|
||||
|
||||
suspend fun createFirstWallet(message: Message): SimpleResult {
|
||||
val response = tangemSdkManager.runTaskAsync(
|
||||
CreateFirstTwinWalletTask(), currentCardId, message
|
||||
)
|
||||
suspend fun createFirstWallet(message: Message): CompletionResult<CreateWalletResponse> {
|
||||
val response = tangemSdkManager.runTaskAsync(CreateFirstTwinWalletTask(), currentCardId, message)
|
||||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
currentCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
return SimpleResult.Success
|
||||
}
|
||||
is CompletionResult.Success -> currentCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
is CompletionResult.Failure -> {
|
||||
(response.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
|
||||
card = scanResponse.card
|
||||
card = card
|
||||
)
|
||||
}
|
||||
return SimpleResult.failure(response.error)
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
|
||||
suspend fun createSecondWallet(
|
||||
initialMessage: Message,
|
||||
preparingMessage: Message,
|
||||
creatingWalletMessage: Message,
|
||||
): SimpleResult {
|
||||
): CompletionResult<CreateWalletResponse> {
|
||||
val task = CreateSecondTwinWalletTask(
|
||||
firstPublicKey = currentCardPublicKey!!,
|
||||
firstCardId = currentCardId,
|
||||
|
|
@ -69,20 +58,18 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context)
|
|||
when (response) {
|
||||
is CompletionResult.Success -> {
|
||||
secondCardPublicKey = response.data.wallet.publicKey.toHexString()
|
||||
return SimpleResult.Success
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(response.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
|
||||
card = scanResponse.card
|
||||
card = card
|
||||
)
|
||||
}
|
||||
return SimpleResult.failure(response.error)
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
suspend fun complete(message: Message): Result<ScanResponse> {
|
||||
|
|
@ -97,7 +84,7 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context)
|
|||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData,
|
||||
card = scanResponse.card
|
||||
card = card
|
||||
)
|
||||
}
|
||||
Result.failure(response.error)
|
||||
|
|
@ -106,16 +93,8 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context)
|
|||
}
|
||||
|
||||
companion object {
|
||||
fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean {
|
||||
if (issuerData.size < 65 || cardWalletPublicKey == null) return false
|
||||
|
||||
val publicKey = issuerData.sliceArray(0 until 65)
|
||||
val signedKey = issuerData.sliceArray(65 until issuerData.size)
|
||||
return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey)
|
||||
}
|
||||
|
||||
private fun getIssuerKeys(context: Context, publicKey: String): KeyPair {
|
||||
val issuer = getIssuers(context).first { it.publicKey == publicKey }
|
||||
private fun getIssuerKeys(reader: AssetReader, publicKey: String): KeyPair {
|
||||
val issuer = getIssuers(reader).first { it.publicKey == publicKey }
|
||||
return KeyPair(
|
||||
publicKey = issuer.publicKey.hexToBytes(),
|
||||
privateKey = issuer.privateKey.hexToBytes()
|
||||
|
|
@ -128,13 +107,17 @@ class TwinCardsManager(private val scanResponse: ScanResponse, context: Context)
|
|||
)
|
||||
}
|
||||
|
||||
private fun getIssuers(context: Context): List<Issuer> {
|
||||
val file = context.readAssetAsString("tangem-app-config/issuers")
|
||||
private fun getIssuers(reader: AssetReader): List<Issuer> {
|
||||
val file = reader.readAssetAsString("tangem-app-config/issuers")
|
||||
return getAdapter().fromJson(file)!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AssetReader {
|
||||
fun readAssetAsString(name: String): String
|
||||
}
|
||||
|
||||
private class Issuer(
|
||||
val id: String,
|
||||
val privateKey: String,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.animation.ObjectAnimator
|
|||
import android.animation.PropertyValuesHolder
|
||||
import android.view.View
|
||||
import androidx.core.animation.doOnEnd
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.leapfrogWidget.LeapView
|
||||
import com.tangem.tap.common.leapfrogWidget.LeapViewState
|
||||
import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget
|
||||
|
|
@ -17,47 +18,60 @@ class TwinsCardWidget(
|
|||
val getTopOfAnchorViewForActivateState: () -> Float
|
||||
) {
|
||||
|
||||
var state: TwinCardsWidgetState? = null
|
||||
private set
|
||||
|
||||
private val animationDuration = 300L
|
||||
|
||||
init {
|
||||
if (leapfrogWidget.getViewsCount() != 2) throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
fun toWelcome(animate: Boolean = true, onEnd: () -> Unit = {}) {
|
||||
val animator = createAnimator(animate, onEnd)
|
||||
fun toWelcome(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
if (state == TwinCardsWidgetState.Welcome) return
|
||||
|
||||
state = TwinCardsWidgetState.Welcome
|
||||
val animator = createAnimator(animate)
|
||||
animator.playTogether(
|
||||
createAnimator(TwinCardNumber.First, createWelcomeProperties(TwinCardNumber.First)),
|
||||
createAnimator(TwinCardNumber.Second, createWelcomeProperties(TwinCardNumber.Second))
|
||||
)
|
||||
leapfrogWidget.fold { animator.start() }
|
||||
animator.doOnEnd { onEnd() }
|
||||
leapfrogWidget.fold(animate) { animator.start() }
|
||||
}
|
||||
|
||||
fun toLeapfrog(animate: Boolean = true, onEnd: () -> Unit = {}) {
|
||||
val animator = createAnimator(animate, onEnd)
|
||||
fun toLeapfrog(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
if (state == TwinCardsWidgetState.Leapfrog) return
|
||||
|
||||
state = TwinCardsWidgetState.Leapfrog
|
||||
val animator = createAnimator(animate)
|
||||
animator.playTogether(
|
||||
createAnimator(TwinCardNumber.First, createLeapfrogProperties(TwinCardNumber.First)),
|
||||
createAnimator(TwinCardNumber.Second, createLeapfrogProperties(TwinCardNumber.Second))
|
||||
)
|
||||
leapfrogWidget.fold {
|
||||
animator.doOnEnd {
|
||||
leapfrogWidget.initViews()
|
||||
leapfrogWidget.unfold()
|
||||
}
|
||||
animator.start()
|
||||
animator.doOnEnd {
|
||||
leapfrogWidget.initViews()
|
||||
leapfrogWidget.unfold(animate, onEnd)
|
||||
}
|
||||
animator.start()
|
||||
}
|
||||
|
||||
fun toActivate(animate: Boolean = true, onEnd: () -> Unit = {}) {
|
||||
val animator = createAnimator(animate, onEnd)
|
||||
fun toActivate(animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
if (state == TwinCardsWidgetState.Activate) return
|
||||
|
||||
state = TwinCardsWidgetState.Activate
|
||||
val animator = createAnimator(animate)
|
||||
animator.playTogether(
|
||||
createAnimator(TwinCardNumber.First, createActivateProperties(TwinCardNumber.First)),
|
||||
createAnimator(TwinCardNumber.Second, createActivateProperties(TwinCardNumber.Second))
|
||||
)
|
||||
animator.doOnEnd { onEnd() }
|
||||
animator.start()
|
||||
}
|
||||
|
||||
private fun createAnimator(animate: Boolean, onEnd: () -> Unit): AnimatorSet {
|
||||
private fun createAnimator(animate: Boolean): AnimatorSet {
|
||||
return AnimatorSet().apply {
|
||||
duration = if (animate) 400 else 0
|
||||
doOnEnd { onEnd() }
|
||||
duration = if (animate) animationDuration else 0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,20 +86,20 @@ class TwinsCardWidget(
|
|||
return when (cardNumber) {
|
||||
TwinCardNumber.First -> {
|
||||
TwinsCardProperties(
|
||||
xTranslation = -180f,
|
||||
yTranslation = -350f,
|
||||
xTranslation = -120f,
|
||||
yTranslation = -220f,
|
||||
rotation = -3f,
|
||||
elevation = 1f,
|
||||
scale = 1.1f,
|
||||
scale = 1f,
|
||||
)
|
||||
}
|
||||
TwinCardNumber.Second -> {
|
||||
TwinsCardProperties(
|
||||
xTranslation = 180f,
|
||||
yTranslation = 10f,
|
||||
xTranslation = 170f,
|
||||
yTranslation = 170f,
|
||||
rotation = -3f,
|
||||
elevation = 0f,
|
||||
scale = 1.1f,
|
||||
scale = 1f,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -108,7 +122,7 @@ class TwinsCardWidget(
|
|||
xTranslation = twinProperties.xTranslation,
|
||||
yTranslation = twinProperties.yTranslation - topOfAnchorView,
|
||||
rotation = 0f,
|
||||
scale = twinProperties.scale - 0.4f,
|
||||
scale = twinProperties.scale - 0.5f,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -120,6 +134,10 @@ class TwinsCardWidget(
|
|||
}
|
||||
}
|
||||
|
||||
enum class TwinCardsWidgetState {
|
||||
Welcome, Leapfrog, Activate
|
||||
}
|
||||
|
||||
private data class TwinsCardProperties(
|
||||
val xTranslation: Float = 0f,
|
||||
val yTranslation: Float = 0f,
|
||||
|
|
@ -1,11 +1,20 @@
|
|||
package com.tangem.tap.domain.twins
|
||||
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.crypto.CryptoUtils
|
||||
|
||||
class TwinsHelper {
|
||||
companion object {
|
||||
const val TWIN_FILE_NAME = "TwinPublicKey"
|
||||
|
||||
fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean {
|
||||
if (issuerData.size < 65 || cardWalletPublicKey == null) return false
|
||||
|
||||
val publicKey = issuerData.sliceArray(0 until 65)
|
||||
val signedKey = issuerData.sliceArray(65 until issuerData.size)
|
||||
return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey)
|
||||
}
|
||||
|
||||
fun getTwinCardNumber(cardId: String): TwinCardNumber? {
|
||||
return when {
|
||||
firstCardSeries.map { cardId.startsWith(it) }.contains(true) -> {
|
||||
|
|
@ -55,6 +64,7 @@ enum class TwinCardNumber(val number: Int) {
|
|||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use ScanResponse.isTangemTwin")
|
||||
fun Card.isTangemTwin(): Boolean {
|
||||
return TwinsHelper.getTwinCardNumber(cardId) != null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
package com.tangem.tap.features.send
|
||||
package com.tangem.tap.features
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fragment_wallet.*
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) {
|
||||
|
||||
abstract fun subscribeToStore()
|
||||
abstract class BaseFragment(layoutId: Int) : Fragment(layoutId), FragmentOnBackPressedHandler {
|
||||
|
||||
protected lateinit var mainView: View
|
||||
protected val storeSubscribersList = mutableListOf<StoreSubscriber<*>>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -38,28 +38,29 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) {
|
|||
return mainView
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
toolbar?.setNavigationOnClickListener { activity?.onBackPressed() }
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
subscribeToStore()
|
||||
}
|
||||
|
||||
|
||||
override fun onStop() {
|
||||
storeSubscribersList.forEach { store.unsubscribe(it) }
|
||||
storeSubscribersList.clear()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
fun showRetrySnackbar(message: String, action: () -> Unit) {
|
||||
fun showRetrySnackbar(message: String, action: VoidCallback) {
|
||||
val snackbar = Snackbar.make(mainView, message, Snackbar.LENGTH_INDEFINITE)
|
||||
snackbar.setAction(getString(R.string.common_retry)) {
|
||||
snackbar.dismiss()
|
||||
action()
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
|
||||
interface FragmentOnBackPressedHandler {
|
||||
fun handleOnBackPressed()
|
||||
}
|
||||
|
||||
@SuppressLint("FragmentBackPressedCallback")
|
||||
fun Fragment.addBackPressHandler(handler: FragmentOnBackPressedHandler) {
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
handler.handleOnBackPressed()
|
||||
}
|
||||
})
|
||||
view?.findViewById<Toolbar>(R.id.toolbar)?.setNavigationOnClickListener { activity?.onBackPressed() }
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.tap.features
|
||||
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class BaseStoreFragment(layoutId: Int) : BaseFragment(layoutId) {
|
||||
|
||||
abstract fun subscribeToStore()
|
||||
protected val storeSubscribersList = mutableListOf<StoreSubscriber<*>>()
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
subscribeToStore()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
storeSubscribersList.forEach { store.unsubscribe(it) }
|
||||
storeSubscribersList.clear()
|
||||
super.onStop()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,12 @@
|
|||
package com.tangem.tap.features.details.redux
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.Message
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.operations.pins.CheckUserCodesResponse
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.termsOfUse.CardTou
|
||||
import com.tangem.tap.domain.twins.TwinCardNumber
|
||||
import com.tangem.tap.features.details.redux.twins.CreateTwinWallet
|
||||
import com.tangem.tap.network.coinmarketcap.FiatCurrency
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
|
|
@ -18,17 +14,15 @@ import org.rekotlin.Action
|
|||
sealed class DetailsAction : Action {
|
||||
|
||||
data class PrepareScreen(
|
||||
val card: Card,
|
||||
val scanResponse: ScanResponse,
|
||||
val wallets: List<Wallet>,
|
||||
val isCreatingTwinWalletAllowed: Boolean?,
|
||||
val cardTou: CardTou,
|
||||
val fiatCurrencyName: FiatCurrencyName,
|
||||
val fiatCurrencies: List<FiatCurrencyName>? = null,
|
||||
) : DetailsAction()
|
||||
|
||||
object ShowDisclaimer : DetailsAction()
|
||||
|
||||
data class ReCreateTwinsWallet(val number: TwinCardNumber) : DetailsAction()
|
||||
|
||||
sealed class EraseWallet : DetailsAction() {
|
||||
object Check : EraseWallet()
|
||||
|
|
@ -48,44 +42,6 @@ sealed class DetailsAction : Action {
|
|||
object Success : EraseWallet()
|
||||
}
|
||||
|
||||
sealed class CreateTwinWalletAction : DetailsAction() {
|
||||
data class ShowWarning(
|
||||
val twinCardNumber: TwinCardNumber?,
|
||||
val createTwinWallet: CreateTwinWallet = CreateTwinWallet.RecreateWallet
|
||||
) : CreateTwinWalletAction()
|
||||
object NotEmpty : CreateTwinWalletAction(), NotificationAction {
|
||||
override val messageResource = R.string.details_notification_erase_wallet_not_possible
|
||||
}
|
||||
object ShowAlert : CreateTwinWalletAction()
|
||||
object HideAlert : CreateTwinWalletAction()
|
||||
object Proceed: CreateTwinWalletAction()
|
||||
|
||||
object Cancel : CreateTwinWalletAction() {
|
||||
object Confirm : CreateTwinWalletAction()
|
||||
}
|
||||
|
||||
data class LaunchFirstStep(
|
||||
val message: Message, val context: Context
|
||||
) : CreateTwinWalletAction() {
|
||||
object Success : CreateTwinWalletAction()
|
||||
object Failure : CreateTwinWalletAction()
|
||||
}
|
||||
|
||||
data class LaunchSecondStep(
|
||||
val initialMessage: Message,
|
||||
val preparingMessage: Message,
|
||||
val creatingWalletMessage: Message,
|
||||
) : CreateTwinWalletAction() {
|
||||
object Success : CreateTwinWalletAction()
|
||||
object Failure : CreateTwinWalletAction()
|
||||
}
|
||||
|
||||
data class LaunchThirdStep(val message: Message) : CreateTwinWalletAction() {
|
||||
data class Success(val scanResponse: ScanResponse) : CreateTwinWalletAction()
|
||||
object Failure : CreateTwinWalletAction()
|
||||
}
|
||||
}
|
||||
|
||||
sealed class AppCurrencyAction : DetailsAction() {
|
||||
data class SetCurrencies(val currencies: List<FiatCurrency>) : AppCurrencyAction()
|
||||
object ChooseAppCurrency : AppCurrencyAction()
|
||||
|
|
|
|||
|
|
@ -4,13 +4,16 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.dispatchNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.twins.CreateTwinWalletMiddleware
|
||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.network.coinmarketcap.CoinMarketCapService
|
||||
import com.tangem.tap.preferencesStorage
|
||||
|
|
@ -26,7 +29,6 @@ class DetailsMiddleware {
|
|||
private val eraseWalletMiddleware = EraseWalletMiddleware()
|
||||
private val appCurrencyMiddleware = AppCurrencyMiddleware()
|
||||
private val manageSecurityMiddleware = ManageSecurityMiddleware()
|
||||
private val twinWalletMiddleware = CreateTwinWalletMiddleware()
|
||||
val detailsMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
|
|
@ -35,11 +37,25 @@ class DetailsMiddleware {
|
|||
is DetailsAction.EraseWallet -> eraseWalletMiddleware.handle(action)
|
||||
is DetailsAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action)
|
||||
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
|
||||
is DetailsAction.CreateTwinWalletAction -> twinWalletMiddleware.handle(action)
|
||||
is DetailsAction.ShowDisclaimer -> {
|
||||
store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
|
||||
}
|
||||
is DetailsAction.ReCreateTwinsWallet -> {
|
||||
val wallet = store.state.walletState.walletManagers.map { it.wallet }.firstOrNull()
|
||||
if (wallet == null) {
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))
|
||||
} else {
|
||||
if (wallet.hasSendableAmountsOrPendingTransactions()) {
|
||||
val walletIsNotEmpty = store.state.globalState.resources.strings.walletIsNotEmpty
|
||||
store.dispatchNotification(walletIsNotEmpty)
|
||||
} else {
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
|
|
@ -84,7 +100,7 @@ class DetailsMiddleware {
|
|||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
is DetailsAction.EraseWallet.Confirm -> {
|
||||
val card = store.state.detailsState.card ?: return
|
||||
val card = store.state.detailsState.card ?: return
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.eraseWallet(card)
|
||||
withContext(Dispatchers.Main) {
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ import com.tangem.tap.domain.TapWorkarounds.isStart2Coin
|
|||
import com.tangem.tap.domain.extensions.isWalletDataSupported
|
||||
import com.tangem.tap.domain.extensions.signedHashesCount
|
||||
import com.tangem.tap.domain.extensions.toSendableAmounts
|
||||
import com.tangem.tap.domain.twins.getTwinCardNumber
|
||||
import com.tangem.tap.domain.twins.isTangemTwin
|
||||
import com.tangem.tap.features.details.redux.twins.CreateTwinWalletReducer
|
||||
import com.tangem.tap.features.details.redux.twins.CreateTwinWalletState
|
||||
import com.tangem.tap.features.wallet.models.hasPendingTransactions
|
||||
import org.rekotlin.Action
|
||||
import java.util.*
|
||||
|
|
@ -38,34 +35,17 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
|
|||
is DetailsAction.ManageSecurity -> {
|
||||
handleSecurityAction(action, detailsState)
|
||||
}
|
||||
is DetailsAction.CreateTwinWalletAction -> {
|
||||
CreateTwinWalletReducer.handle(action, detailsState)
|
||||
}
|
||||
else -> detailsState
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState {
|
||||
val twinsState = if (action.card.isTangemTwin()) {
|
||||
CreateTwinWalletState(
|
||||
scanResponse = action.scanResponse,
|
||||
twinCardNumber = action.card.getTwinCardNumber(),
|
||||
createTwinWallet = null,
|
||||
showAlert = false,
|
||||
allowRecreatingWallet = action.isCreatingTwinWalletAllowed
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return DetailsState(
|
||||
card = action.card, wallets = action.wallets,
|
||||
cardInfo = action.card.toCardInfo(),
|
||||
appCurrencyState = AppCurrencyState(
|
||||
action.fiatCurrencyName
|
||||
),
|
||||
createTwinWalletState = twinsState,
|
||||
cardTermsOfUseUrl = action.cardTou.getUrl(action.card)
|
||||
card = action.scanResponse.card,
|
||||
wallets = action.wallets,
|
||||
cardInfo = action.scanResponse.card.toCardInfo(),
|
||||
appCurrencyState = AppCurrencyState(action.fiatCurrencyName),
|
||||
cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +79,7 @@ private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsS
|
|||
}
|
||||
|
||||
private fun handleAppCurrencyAction(
|
||||
action: DetailsAction.AppCurrencyAction, state: DetailsState
|
||||
action: DetailsAction.AppCurrencyAction, state: DetailsState
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.AppCurrencyAction.SetCurrencies -> {
|
||||
|
|
@ -113,9 +93,9 @@ private fun handleAppCurrencyAction(
|
|||
}
|
||||
is DetailsAction.AppCurrencyAction.SelectAppCurrency -> {
|
||||
state.copy(
|
||||
appCurrencyState = state.appCurrencyState.copy(
|
||||
fiatCurrencyName = action.fiatCurrencyName, showAppCurrencyDialog = false
|
||||
)
|
||||
appCurrencyState = state.appCurrencyState.copy(
|
||||
fiatCurrencyName = action.fiatCurrencyName, showAppCurrencyDialog = false
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> state
|
||||
|
|
@ -123,7 +103,7 @@ private fun handleAppCurrencyAction(
|
|||
}
|
||||
|
||||
private fun handleSecurityAction(
|
||||
action: DetailsAction.ManageSecurity, state: DetailsState
|
||||
action: DetailsAction.ManageSecurity, state: DetailsState
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.ManageSecurity.SetCurrentOption -> {
|
||||
|
|
@ -143,8 +123,8 @@ private fun handleSecurityAction(
|
|||
is DetailsAction.ManageSecurity.OpenSecurity -> {
|
||||
if (state.card?.isStart2Coin == true) {
|
||||
return state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
allowedOptions = EnumSet.of(SecurityOption.LongTap),
|
||||
selectedOption = state.securityScreenState.currentOption
|
||||
allowedOptions = EnumSet.of(SecurityOption.LongTap),
|
||||
selectedOption = state.securityScreenState.currentOption
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -152,13 +132,13 @@ private fun handleSecurityAction(
|
|||
state.card, state.securityScreenState?.currentOption
|
||||
)
|
||||
state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
allowedOptions = allowedSecurityOptions,
|
||||
selectedOption = state.securityScreenState.currentOption
|
||||
allowedOptions = allowedSecurityOptions,
|
||||
selectedOption = state.securityScreenState.currentOption
|
||||
))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.SelectOption -> {
|
||||
state.copy(securityScreenState = state.securityScreenState?.copy(
|
||||
selectedOption = action.option
|
||||
selectedOption = action.option
|
||||
))
|
||||
}
|
||||
is DetailsAction.ManageSecurity.ConfirmSelection -> {
|
||||
|
|
@ -213,4 +193,5 @@ private fun Card.toCardInfo(): CardInfo? {
|
|||
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
|
||||
val issuer = this.issuer.name
|
||||
val signedHashes = this.signedHashesCount()
|
||||
return CardInfo(cardId, issuer, signedHashes)}
|
||||
return CardInfo(cardId, issuer, signedHashes)
|
||||
}
|
||||
|
|
@ -6,41 +6,52 @@ import com.tangem.common.card.Card
|
|||
import com.tangem.tap.common.entities.Button
|
||||
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.features.details.redux.twins.CreateTwinWalletState
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.network.coinmarketcap.FiatCurrency
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StateType
|
||||
import java.util.*
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
data class DetailsState(
|
||||
val card: Card? = null,
|
||||
val wallets: List<Wallet> = emptyList(),
|
||||
val cardInfo: CardInfo? = null,
|
||||
val appCurrencyState: AppCurrencyState = AppCurrencyState(),
|
||||
val eraseWalletState: EraseWalletState? = null,
|
||||
val confirmScreenState: ConfirmScreenState? = null,
|
||||
val securityScreenState: SecurityScreenState? = null,
|
||||
val createTwinWalletState: CreateTwinWalletState? = null,
|
||||
val cardTermsOfUseUrl: Uri? = null,
|
||||
) : StateType
|
||||
val card: Card? = null,
|
||||
val wallets: List<Wallet> = emptyList(),
|
||||
val cardInfo: CardInfo? = null,
|
||||
val appCurrencyState: AppCurrencyState = AppCurrencyState(),
|
||||
val eraseWalletState: EraseWalletState? = null,
|
||||
val confirmScreenState: ConfirmScreenState? = null,
|
||||
val securityScreenState: SecurityScreenState? = null,
|
||||
val cardTermsOfUseUrl: Uri? = null,
|
||||
) : StateType {
|
||||
|
||||
// if you do not delegate - the application crashes on startup,
|
||||
// because twinCardsState has not been created yet
|
||||
val twinCardsState: TwinCardsState by ReadOnlyProperty<Any, TwinCardsState> { thisRef, property ->
|
||||
store.state.twinCardsState
|
||||
}
|
||||
|
||||
val isTangemTwins: Boolean
|
||||
get() = store.state.globalState.scanResponse?.isTangemTwins() == true
|
||||
}
|
||||
|
||||
data class CardInfo(
|
||||
val cardId: String,
|
||||
val issuer: String,
|
||||
val signedHashes: Int,
|
||||
val cardId: String,
|
||||
val issuer: String,
|
||||
val signedHashes: Int,
|
||||
)
|
||||
|
||||
enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty }
|
||||
enum class ConfirmScreenState { EraseWallet, LongTap, AccessCode, PassCode }
|
||||
data class SecurityScreenState(
|
||||
val currentOption: SecurityOption = SecurityOption.LongTap,
|
||||
val selectedOption: SecurityOption = currentOption,
|
||||
val allowedOptions: EnumSet<SecurityOption> = EnumSet.allOf(SecurityOption::class.java),
|
||||
val buttonProceed: Button = Button(true),
|
||||
val currentOption: SecurityOption = SecurityOption.LongTap,
|
||||
val selectedOption: SecurityOption = currentOption,
|
||||
val allowedOptions: EnumSet<SecurityOption> = EnumSet.allOf(SecurityOption::class.java),
|
||||
val buttonProceed: Button = Button(true),
|
||||
)
|
||||
|
||||
enum class SecurityOption { LongTap, PassCode, AccessCode }
|
||||
data class AppCurrencyState(
|
||||
val fiatCurrencyName: FiatCurrencyName = DEFAULT_FIAT_CURRENCY,
|
||||
val showAppCurrencyDialog: Boolean = false,
|
||||
val fiatCurrencies: List<FiatCurrency>? = null,
|
||||
val fiatCurrencyName: FiatCurrencyName = DEFAULT_FIAT_CURRENCY,
|
||||
val showAppCurrencyDialog: Boolean = false,
|
||||
val fiatCurrencies: List<FiatCurrency>? = null,
|
||||
)
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
package com.tangem.tap.features.details.redux.twins
|
||||
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.extensions.toSendableAmounts
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class CreateTwinWalletMiddleware {
|
||||
var twinsManager: TwinCardsManager? = null
|
||||
fun handle(action: DetailsAction.CreateTwinWalletAction) {
|
||||
when (action) {
|
||||
is DetailsAction.CreateTwinWalletAction.ShowWarning -> {
|
||||
val wallet = store.state.detailsState.wallets.firstOrNull()
|
||||
if (wallet == null) {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning))
|
||||
return
|
||||
}
|
||||
val notEmpty = wallet.recentTransactions.isNotEmpty() || wallet.amounts.toSendableAmounts().isNotEmpty()
|
||||
if (notEmpty) {
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.NotEmpty)
|
||||
} else {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning))
|
||||
}
|
||||
}
|
||||
is DetailsAction.CreateTwinWalletAction.Proceed ->
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet))
|
||||
is DetailsAction.CreateTwinWalletAction.Cancel -> {
|
||||
|
||||
val step = store.state.detailsState.createTwinWalletState?.step
|
||||
if (step != null && step != CreateTwinWalletStep.FirstStep
|
||||
) {
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.ShowAlert)
|
||||
} else {
|
||||
twinsManager = null
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
}
|
||||
is DetailsAction.CreateTwinWalletAction.Cancel.Confirm -> {
|
||||
twinsManager = null
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
is DetailsAction.CreateTwinWalletAction.LaunchFirstStep -> {
|
||||
store.state.globalState.scanResponse?.let {
|
||||
twinsManager = TwinCardsManager(it, action.context)
|
||||
}
|
||||
scope.launch {
|
||||
val result = twinsManager?.createFirstWallet(action.message)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
SimpleResult.Success ->
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Success)
|
||||
is SimpleResult.Failure ->
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Success -> {
|
||||
|
||||
}
|
||||
DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Failure -> {
|
||||
|
||||
}
|
||||
is DetailsAction.CreateTwinWalletAction.LaunchSecondStep ->
|
||||
scope.launch {
|
||||
val result = twinsManager?.createSecondWallet(action.initialMessage,
|
||||
action.preparingMessage, action.creatingWalletMessage)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
SimpleResult.Success ->
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Success)
|
||||
is SimpleResult.Failure ->
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Success -> {
|
||||
|
||||
}
|
||||
DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Failure -> {
|
||||
|
||||
}
|
||||
is DetailsAction.CreateTwinWalletAction.LaunchThirdStep -> {
|
||||
scope.launch {
|
||||
val result = twinsManager?.complete(action.message)
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is Result.Success ->
|
||||
store.dispatch(
|
||||
DetailsAction.CreateTwinWalletAction
|
||||
.LaunchThirdStep.Success(result.data)
|
||||
)
|
||||
is Result.Failure ->
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Success -> {
|
||||
scope.launch {
|
||||
store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse)
|
||||
}
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Failure -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package com.tangem.tap.features.details.redux.twins
|
||||
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
|
||||
class CreateTwinWalletReducer {
|
||||
companion object {
|
||||
fun handle(
|
||||
action: DetailsAction.CreateTwinWalletAction, state: DetailsState
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.CreateTwinWalletAction.ShowWarning -> {
|
||||
state.copy(createTwinWalletState = CreateTwinWalletState(
|
||||
scanResponse = null,
|
||||
twinCardNumber = action.twinCardNumber
|
||||
?: state.createTwinWalletState?.twinCardNumber,
|
||||
createTwinWallet = action.createTwinWallet,
|
||||
showAlert = false,
|
||||
allowRecreatingWallet = state.createTwinWalletState?.allowRecreatingWallet
|
||||
))
|
||||
}
|
||||
DetailsAction.CreateTwinWalletAction.NotEmpty -> state
|
||||
DetailsAction.CreateTwinWalletAction.ShowAlert -> {
|
||||
state.copy(createTwinWalletState = state.createTwinWalletState?.copy(
|
||||
showAlert = true
|
||||
))
|
||||
}
|
||||
DetailsAction.CreateTwinWalletAction.HideAlert -> {
|
||||
state.copy(createTwinWalletState = state.createTwinWalletState?.copy(
|
||||
showAlert = false
|
||||
))
|
||||
}
|
||||
is DetailsAction.CreateTwinWalletAction.Proceed -> {
|
||||
state
|
||||
}
|
||||
DetailsAction.CreateTwinWalletAction.Cancel -> state
|
||||
DetailsAction.CreateTwinWalletAction.Cancel.Confirm -> state
|
||||
is DetailsAction.CreateTwinWalletAction.LaunchFirstStep -> state
|
||||
|
||||
DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Success -> {
|
||||
state.copy(createTwinWalletState = state.createTwinWalletState?.copy(
|
||||
step = CreateTwinWalletStep.SecondStep
|
||||
))
|
||||
}
|
||||
DetailsAction.CreateTwinWalletAction.LaunchFirstStep.Failure -> state
|
||||
|
||||
is DetailsAction.CreateTwinWalletAction.LaunchSecondStep -> state
|
||||
DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Success ->
|
||||
state.copy(createTwinWalletState = state.createTwinWalletState?.copy(
|
||||
step = CreateTwinWalletStep.ThirdStep
|
||||
))
|
||||
DetailsAction.CreateTwinWalletAction.LaunchSecondStep.Failure -> {
|
||||
state
|
||||
}
|
||||
is DetailsAction.CreateTwinWalletAction.LaunchThirdStep -> {
|
||||
state
|
||||
}
|
||||
is DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Success -> {
|
||||
state
|
||||
}
|
||||
DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Failure -> {
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package com.tangem.tap.features.details.redux.twins
|
||||
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.twins.TwinCardNumber
|
||||
|
||||
data class CreateTwinWalletState(
|
||||
val scanResponse: ScanResponse?,
|
||||
val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep,
|
||||
val twinCardNumber: TwinCardNumber?,
|
||||
val createTwinWallet: CreateTwinWallet?,
|
||||
val showAlert: Boolean,
|
||||
val allowRecreatingWallet: Boolean? = null
|
||||
)
|
||||
|
||||
enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep }
|
||||
|
||||
enum class CreateTwinWallet { CreateWallet, RecreateWallet }
|
||||
|
|
@ -6,17 +6,14 @@ import android.view.View
|
|||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.twins.getTwinCardIdForUser
|
||||
import com.tangem.tap.domain.twins.isTangemTwin
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import com.tangem.tap.features.details.redux.twins.CreateTwinWallet
|
||||
import com.tangem.tap.features.feedback.FeedbackEmail
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -68,8 +65,8 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
|
||||
|
||||
if (state.cardInfo != null) {
|
||||
val cardId = if (state.card?.isTangemTwin() == true) {
|
||||
state.card.getTwinCardIdForUser()
|
||||
val cardId = if (state.isTangemTwins) {
|
||||
state.card?.getTwinCardIdForUser()
|
||||
} else {
|
||||
state.cardInfo.cardId
|
||||
}
|
||||
|
|
@ -78,8 +75,8 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
tv_signed_hashes.text = state.cardInfo.signedHashes.toString()
|
||||
}
|
||||
|
||||
tv_signed_hashes.show(state.card?.isTangemTwin() != true)
|
||||
tv_signed_hashes_title.show(state.card?.isTangemTwin() != true)
|
||||
tv_signed_hashes.show(!state.isTangemTwins)
|
||||
tv_signed_hashes_title.show(!state.isTangemTwins)
|
||||
|
||||
tv_disclaimer.setOnClickListener { store.dispatch(DetailsAction.ShowDisclaimer) }
|
||||
|
||||
|
|
@ -90,17 +87,11 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
startActivity(intent)
|
||||
}
|
||||
|
||||
if (state.createTwinWalletState != null) {
|
||||
if (state.createTwinWalletState.allowRecreatingWallet == true) {
|
||||
tv_erase_wallet.show()
|
||||
tv_erase_wallet.text = getText(R.string.details_row_title_twins_recreate)
|
||||
tv_erase_wallet.setOnClickListener {
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.ShowWarning(
|
||||
null, CreateTwinWallet.RecreateWallet
|
||||
))
|
||||
}
|
||||
} else {
|
||||
tv_erase_wallet.hide()
|
||||
if (state.isTangemTwins) {
|
||||
tv_erase_wallet.show()
|
||||
tv_erase_wallet.text = getText(R.string.details_row_title_twins_recreate)
|
||||
tv_erase_wallet.setOnClickListener {
|
||||
store.dispatch(DetailsAction.ReCreateTwinsWallet(state.twinCardsState.cardNumber!!))
|
||||
}
|
||||
} else {
|
||||
tv_erase_wallet.show()
|
||||
|
|
@ -140,9 +131,9 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
if (state.appCurrencyState.showAppCurrencyDialog &&
|
||||
!state.appCurrencyState.fiatCurrencies.isNullOrEmpty()) {
|
||||
currencySelectionDialog.show(
|
||||
state.appCurrencyState.fiatCurrencies,
|
||||
state.appCurrencyState.fiatCurrencyName,
|
||||
requireContext()
|
||||
state.appCurrencyState.fiatCurrencies,
|
||||
state.appCurrencyState.fiatCurrencyName,
|
||||
requireContext()
|
||||
)
|
||||
} else {
|
||||
currencySelectionDialog.clear()
|
||||
|
|
|
|||
|
|
@ -1,174 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui.twins
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.core.content.ContextCompat.getColor
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.Message
|
||||
import com.tangem.tap.domain.twins.TwinCardNumber
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.features.details.redux.twins.CreateTwinWallet
|
||||
import com.tangem.tap.features.details.redux.twins.CreateTwinWalletStep
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fragment_details_twin_cards.*
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
||||
class CreateTwinWalletFragment : Fragment(R.layout.fragment_details_twin_cards),
|
||||
StoreSubscriber<DetailsState> {
|
||||
|
||||
private var dialog: Dialog? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.Cancel)
|
||||
}
|
||||
})
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.detailsState == newState.detailsState
|
||||
}.select { it.detailsState }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.Cancel)
|
||||
}
|
||||
|
||||
Picasso.get()
|
||||
.load(Artwork.TWIN_CARD_1)
|
||||
.placeholder(R.drawable.card_placeholder)
|
||||
?.error(R.drawable.card_placeholder)
|
||||
?.into(iv_card_1)
|
||||
|
||||
Picasso.get()
|
||||
.load(Artwork.TWIN_CARD_2)
|
||||
.placeholder(R.drawable.card_placeholder)
|
||||
?.error(R.drawable.card_placeholder)
|
||||
?.into(iv_card_2)
|
||||
}
|
||||
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null) return
|
||||
|
||||
toolbar.title = when (state.createTwinWalletState?.createTwinWallet) {
|
||||
CreateTwinWallet.CreateWallet -> getText(R.string.wallet_button_create_wallet)
|
||||
CreateTwinWallet.RecreateWallet, null -> getText(R.string.details_twins_recreate_toolbar)
|
||||
|
||||
}
|
||||
|
||||
val selectedColor = getColor(requireContext(), R.color.colorSecondary)
|
||||
val defaultColor = getColor(requireContext(), R.color.blue_pale)
|
||||
|
||||
val twinCardNumber = state.createTwinWalletState?.twinCardNumber ?: TwinCardNumber.First
|
||||
|
||||
val cardNumber = when (state.createTwinWalletState?.step) {
|
||||
CreateTwinWalletStep.FirstStep -> {
|
||||
val twinCardNumberString = twinCardNumber.number.toString()
|
||||
tv_step_number.text =
|
||||
getString(R.string.details_twins_recreate_step_format, "1")
|
||||
v_step_1.setBackgroundColor(selectedColor)
|
||||
v_step_2.setBackgroundColor(defaultColor)
|
||||
v_step_3.setBackgroundColor(defaultColor)
|
||||
btn_tap.setOnClickListener {
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchFirstStep(
|
||||
Message(getString(
|
||||
R.string.details_twins_recreate_title_format,
|
||||
twinCardNumberString
|
||||
)),
|
||||
requireContext()
|
||||
))
|
||||
}
|
||||
btn_tap.text = getString(R.string.details_twins_recreate_button_format,
|
||||
twinCardNumber.number.toString())
|
||||
twinCardNumberString
|
||||
}
|
||||
CreateTwinWalletStep.SecondStep -> {
|
||||
val twinCardNumberString = twinCardNumber.pairNumber().number.toString()
|
||||
|
||||
tv_step_number.text =
|
||||
getString(R.string.details_twins_recreate_step_format, "2")
|
||||
v_step_1.setBackgroundColor(selectedColor)
|
||||
v_step_2.setBackgroundColor(selectedColor)
|
||||
v_step_3.setBackgroundColor(defaultColor)
|
||||
btn_tap.setOnClickListener {
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchSecondStep(
|
||||
Message(getString(R.string.details_twins_recreate_title_format, twinCardNumberString)),
|
||||
Message(getString(R.string.details_twins_recreate_title_preparing)),
|
||||
Message(getString(R.string.details_twins_recreate_title_creating_wallet)),
|
||||
))
|
||||
}
|
||||
btn_tap.text = getString(R.string.details_twins_recreate_button_format, "2")
|
||||
twinCardNumberString
|
||||
}
|
||||
CreateTwinWalletStep.ThirdStep -> {
|
||||
val twinCardNumberString = twinCardNumber.number.toString()
|
||||
|
||||
tv_step_number.text =
|
||||
getString(R.string.details_twins_recreate_step_format, "3")
|
||||
v_step_1.setBackgroundColor(selectedColor)
|
||||
v_step_2.setBackgroundColor(selectedColor)
|
||||
v_step_3.setBackgroundColor(selectedColor)
|
||||
btn_tap.setOnClickListener {
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.LaunchThirdStep(
|
||||
Message(getString(
|
||||
R.string.details_twins_recreate_title_format, twinCardNumberString)
|
||||
)
|
||||
))
|
||||
}
|
||||
btn_tap.text = getString(R.string.details_twins_recreate_button_format, twinCardNumberString)
|
||||
twinCardNumberString
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
btn_tap.text = getString(R.string.details_twins_recreate_button_format, cardNumber)
|
||||
tv_twin_title.text = getString(R.string.details_twins_recreate_title_format, cardNumber)
|
||||
|
||||
if (state.createTwinWalletState?.showAlert == true) {
|
||||
if (dialog == null) {
|
||||
dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setMessage(R.string.details_twins_recreate_alert)
|
||||
.setPositiveButton(R.string.common_ok) { _, _ ->
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.Cancel.Confirm)
|
||||
}
|
||||
.setNegativeButton(R.string.common_cancel) { _, _ ->
|
||||
dialog?.cancel()
|
||||
}
|
||||
.setOnCancelListener {
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.HideAlert)
|
||||
}
|
||||
.create()
|
||||
dialog?.show()
|
||||
}
|
||||
} else {
|
||||
dialog?.cancel()
|
||||
dialog = null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui.twins
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.twins.CreateTwinWallet
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fragment_details_twin_cards_warning.*
|
||||
import kotlinx.android.synthetic.main.layout_twin_cards_orange.*
|
||||
|
||||
class TwinWalletWarningFragment : Fragment(R.layout.fragment_details_twin_cards_warning) {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
})
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val createTwinWallet = store.state.detailsState.createTwinWalletState?.createTwinWallet
|
||||
if (createTwinWallet == CreateTwinWallet.CreateWallet) {
|
||||
tv_twin_cards_description.text = getText(R.string.details_twins_recreate_subtitle)
|
||||
} else if (createTwinWallet == CreateTwinWallet.RecreateWallet) {
|
||||
tv_twin_cards_description.text = getText(R.string.details_twins_recreate_warning)
|
||||
}
|
||||
|
||||
btn_cancel.setOnClickListener { store.dispatch(NavigationAction.PopBackTo()) }
|
||||
btn_start.setOnClickListener {
|
||||
store.dispatch(
|
||||
DetailsAction.CreateTwinWalletAction.Proceed)
|
||||
}
|
||||
Picasso.get()
|
||||
.load(Artwork.TWIN_CARD_1)
|
||||
.placeholder(R.drawable.card_placeholder)
|
||||
?.error(R.drawable.card_placeholder)
|
||||
?.into(iv_twin_card_1)
|
||||
|
||||
Picasso.get()
|
||||
.load(Artwork.TWIN_CARD_2)
|
||||
.placeholder(R.drawable.card_placeholder)
|
||||
?.error(R.drawable.card_placeholder)
|
||||
?.into(iv_twin_card_2)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -16,15 +16,14 @@ import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
|
|||
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_bottom_action_views.*
|
||||
import kotlinx.android.synthetic.main.layout_onboarding_home.*
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlinx.android.synthetic.main.layout_onboarding_container_bottom.*
|
||||
import kotlinx.android.synthetic.main.layout_onboarding_home_top.*
|
||||
|
||||
class HomeFragment : BaseOnboardingFragment<HomeState>() {
|
||||
|
||||
private lateinit var btnScanCard: ViewStateWidget
|
||||
|
||||
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_home
|
||||
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_home_top
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -38,7 +37,10 @@ class HomeFragment : BaseOnboardingFragment<HomeState>() {
|
|||
|
||||
toolbar.hide()
|
||||
val shareTransition = FragmentShareTransition(
|
||||
listOf(ShareElement(WeakReference(imv_front_card), imv_front_card.transitionName)),
|
||||
listOf(
|
||||
ShareElement(imv_front_card, ShareElement.imvFrontCard),
|
||||
ShareElement(imv_back_card, ShareElement.imvBackCard)
|
||||
),
|
||||
FrontCardEnterTransition(),
|
||||
FrontCardExitTransition()
|
||||
)
|
||||
|
|
@ -62,6 +64,7 @@ class HomeFragment : BaseOnboardingFragment<HomeState>() {
|
|||
oldState.homeState == newState.homeState
|
||||
}.select { it.homeState }
|
||||
}
|
||||
storeSubscribersList.add(this)
|
||||
}
|
||||
|
||||
override fun newState(state: HomeState) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.tangem.tap.features.home.redux
|
|||
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.post
|
||||
import com.tangem.tap.common.postUi
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -11,6 +13,8 @@ import com.tangem.tap.common.redux.navigation.FragmentShareTransition
|
|||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
|
||||
import com.tangem.tap.features.send.redux.states.ButtonState
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
|
|
@ -56,13 +60,24 @@ private fun handleReadCard() {
|
|||
} else {
|
||||
changeButtonState(ButtonState.PROGRESS)
|
||||
store.dispatch(GlobalAction.ScanCard({ scanResponse ->
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
|
||||
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
|
||||
val navigateTo = OnboardingHelper.whereToNavigate(scanResponse)
|
||||
store.dispatch(GlobalAction.Onboarding.Start(scanResponse))
|
||||
navigateTo(navigateTo, store.state.homeState.shareTransition)
|
||||
} else {
|
||||
scope.launch { store.state.globalState.tapWalletManager.onCardScanned(scanResponse) }
|
||||
navigateTo(AppScreen.Wallet, null)
|
||||
scope.launch {
|
||||
store.onCardScanned(scanResponse)
|
||||
withMainContext {
|
||||
if (scanResponse.twinsIsTwinned() && !preferencesStorage.wasTwinsOnboardingShown()) {
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly))
|
||||
navigateTo(AppScreen.OnboardingTwins, store.state.homeState.shareTransition)
|
||||
} else {
|
||||
navigateTo(AppScreen.Wallet, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
changeButtonState(ButtonState.ENABLED)
|
||||
|
|
|
|||
|
|
@ -12,14 +12,28 @@ import com.tangem.tap.preferencesStorage
|
|||
class OnboardingHelper {
|
||||
companion object {
|
||||
|
||||
fun isOnboardingCase(response: ScanResponse): Boolean = if (response.card.hasWallets())
|
||||
preferencesStorage.usedCardsPrefStorage.activationIsStarted(response.card.cardId) else true
|
||||
fun isOnboardingCase(response: ScanResponse): Boolean {
|
||||
val cardInfoStorage = preferencesStorage.usedCardsPrefStorage
|
||||
return when {
|
||||
response.productType == ProductType.Twins -> {
|
||||
if (!response.twinsIsTwinned()) {
|
||||
true
|
||||
} else {
|
||||
cardInfoStorage.activationIsStarted(response.card.cardId)
|
||||
}
|
||||
}
|
||||
response.card.hasWallets() -> {
|
||||
cardInfoStorage.activationIsStarted(response.card.cardId)
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
fun whereToNavigate(scanResponse: ScanResponse): AppScreen {
|
||||
return when (scanResponse.productType) {
|
||||
ProductType.Note -> AppScreen.OnboardingNote
|
||||
ProductType.Wallet -> AppScreen.OnboardingWallet
|
||||
ProductType.Twin -> AppScreen.OnboardingTwins
|
||||
ProductType.Twins -> AppScreen.OnboardingTwins
|
||||
ProductType.Other -> AppScreen.OnboardingOther
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class OnboardingManager(
|
|||
}
|
||||
|
||||
suspend fun updateBalance(walletManager: WalletManager): OnboardingWalletBalance {
|
||||
return when (val result = walletManager.safeUpdate()) {
|
||||
val balance = when (val result = walletManager.safeUpdate()) {
|
||||
is Result.Success -> {
|
||||
val wallet = walletManager.wallet
|
||||
val valueOfAmount = wallet.amounts[AmountType.Coin]?.value
|
||||
|
|
@ -63,11 +63,10 @@ class OnboardingManager(
|
|||
val customError = TapError.CustomError("Amount is NULL")
|
||||
OnboardingWalletBalance.criticalError(customError)
|
||||
} else {
|
||||
val currency = Currency.Blockchain(walletManager.wallet.blockchain)
|
||||
val balance = if (valueOfAmount.isZero()) {
|
||||
OnboardingWalletBalance.done(valueOfAmount, wallet.hasPendingTransactions(), currency)
|
||||
OnboardingWalletBalance.done(valueOfAmount, wallet.hasPendingTransactions())
|
||||
} else {
|
||||
OnboardingWalletBalance.done(valueOfAmount, false, currency)
|
||||
OnboardingWalletBalance.done(valueOfAmount, false)
|
||||
}
|
||||
balance
|
||||
}
|
||||
|
|
@ -81,6 +80,8 @@ class OnboardingManager(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return balance.copy(currency = Currency.Blockchain(walletManager.wallet.blockchain))
|
||||
}
|
||||
|
||||
fun activationStarted(cardId: String) {
|
||||
|
|
@ -122,7 +123,10 @@ data class OnboardingWalletBalance(
|
|||
state = ProgressState.Loading
|
||||
)
|
||||
|
||||
fun done(value: BigDecimal, hasTransactions: Boolean, currency: Currency.Blockchain): OnboardingWalletBalance =
|
||||
OnboardingWalletBalance(value, currency, hasTransactions, ProgressState.Done)
|
||||
fun done(value: BigDecimal, hasTransactions: Boolean): OnboardingWalletBalance = OnboardingWalletBalance(
|
||||
value,
|
||||
hasIncomingTransaction = hasTransactions,
|
||||
state = ProgressState.Done
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import android.view.View
|
|||
import com.tangem.tap.common.extensions.inflate
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.transitions.HomeToOnboardingTransition
|
||||
import com.tangem.tap.features.send.BaseStoreFragment
|
||||
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.*
|
||||
|
|
@ -16,11 +16,6 @@ import org.rekotlin.StoreSubscriber
|
|||
*/
|
||||
abstract class BaseOnboardingFragment<T> : BaseStoreFragment(R.layout.fragment_onboarding_main), StoreSubscriber<T> {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
configureTransitions()
|
||||
}
|
||||
|
||||
override fun configureTransitions() {
|
||||
enterTransition = HomeToOnboardingTransition()
|
||||
exitTransition = HomeToOnboardingTransition()
|
||||
|
|
|
|||
|
|
@ -15,16 +15,19 @@ import com.tangem.tangem_sdk_new.extensions.fadeIn
|
|||
import com.tangem.tangem_sdk_new.extensions.fadeOut
|
||||
import com.tangem.tap.common.extensions.getDrawableCompat
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.postUi
|
||||
import com.tangem.tap.common.redux.navigation.ShareElement
|
||||
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
|
||||
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
|
||||
import com.tangem.tap.features.addBackPressHandler
|
||||
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.*
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState
|
||||
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_bottom_action_views.*
|
||||
import kotlinx.android.synthetic.main.layout_onboarding_note.*
|
||||
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.*
|
||||
|
||||
|
|
@ -33,24 +36,28 @@ import kotlinx.android.synthetic.main.view_onboarding_tv_balance.*
|
|||
*/
|
||||
class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
||||
|
||||
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
|
||||
private var currentStep: OnboardingNoteStep = OnboardingNoteStep.None
|
||||
private var previousStep: OnboardingNoteStep = OnboardingNoteStep.None
|
||||
|
||||
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note
|
||||
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
|
||||
|
||||
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
postponeEnterTransition()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
addBackPressHandler(this)
|
||||
|
||||
imv_front_card.transitionName = ShareElement.imvFrontCard
|
||||
startPostponedEnterTransition()
|
||||
|
||||
toolbar.setTitle(R.string.onboarding_title)
|
||||
btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container)
|
||||
val resources = AndroidResources(
|
||||
OnboardingStringResources(
|
||||
R.string.copy_toast_msg
|
||||
)
|
||||
)
|
||||
|
||||
store.dispatch(OnboardingNoteAction.LoadCardArtwork)
|
||||
store.dispatch(OnboardingNoteAction.SetResources(resources))
|
||||
store.dispatch(OnboardingNoteAction.DetermineStepOfScreen)
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +85,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
}
|
||||
setBalance(state)
|
||||
showConfetti(state.showConfetti)
|
||||
currentStep = state.currentStep
|
||||
previousStep = state.currentStep
|
||||
}
|
||||
|
||||
private fun setBalance(state: OnboardingNoteState) {
|
||||
|
|
@ -129,14 +136,22 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
}
|
||||
|
||||
tv_header.setText(R.string.onboarding_top_up_header)
|
||||
tv_body.setText(R.string.onboarding_top_up_body)
|
||||
if (state.balanceNonCriticalError == null) {
|
||||
tv_body.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
|
||||
}
|
||||
}
|
||||
|
||||
btnRefreshBalanceWidget.changeState(state.walletBalance.state)
|
||||
if (btnRefreshBalanceWidget.isShowing != true) {
|
||||
postUi(300) {
|
||||
btnRefreshBalanceWidget.mainView.setOnClickListener {
|
||||
store.dispatch(OnboardingNoteAction.Balance.Update)
|
||||
}
|
||||
btnRefreshBalanceWidget.mainView.setOnClickListener {
|
||||
store.dispatch(OnboardingNoteAction.Balance.Update)
|
||||
}
|
||||
}
|
||||
imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8))
|
||||
|
|
@ -163,7 +178,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
|||
}
|
||||
|
||||
private fun updateConstraints(currentStep: OnboardingNoteStep, @LayoutRes layoutId: Int) {
|
||||
if (this.currentStep == currentStep) return
|
||||
if (this.previousStep == currentStep) return
|
||||
|
||||
val constraintSet = ConstraintSet()
|
||||
constraintSet.clone(requireContext(), layoutId)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.onboarding.products.note.redux
|
||||
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import org.rekotlin.Action
|
||||
|
|
@ -10,14 +11,13 @@ import org.rekotlin.Action
|
|||
*/
|
||||
sealed class OnboardingNoteAction : Action {
|
||||
// from user, ui
|
||||
data class SetResources(val resources: AndroidResources) : OnboardingNoteAction()
|
||||
object LoadCardArtwork : OnboardingNoteAction()
|
||||
object CreateWallet : OnboardingNoteAction()
|
||||
object TopUp : OnboardingNoteAction()
|
||||
object ShowAddressInfoDialog : OnboardingNoteAction()
|
||||
|
||||
// from redux
|
||||
class SetArtworkUrl(val artwork: Artwork) : OnboardingNoteAction()
|
||||
class SetArtwork(val artwork: Artwork) : OnboardingNoteAction()
|
||||
data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction()
|
||||
object DetermineStepOfScreen : OnboardingNoteAction()
|
||||
object Done : OnboardingNoteAction()
|
||||
|
|
@ -27,6 +27,7 @@ sealed class OnboardingNoteAction : Action {
|
|||
sealed class Balance {
|
||||
object Update : OnboardingNoteAction()
|
||||
data class Set(val balance: OnboardingWalletBalance) : OnboardingNoteAction()
|
||||
data class SetNonCriticalError(val error: TapError?) : OnboardingNoteAction()
|
||||
}
|
||||
|
||||
sealed class Confetti {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.hasWallets
|
||||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -50,7 +51,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
|
|||
is OnboardingNoteAction.LoadCardArtwork -> {
|
||||
scope.launch {
|
||||
val artwork = onboardingManager.loadArtwork()
|
||||
withMainContext { store.dispatch(OnboardingNoteAction.SetArtworkUrl(artwork)) }
|
||||
withMainContext { store.dispatch(OnboardingNoteAction.SetArtwork(artwork)) }
|
||||
}
|
||||
}
|
||||
is OnboardingNoteAction.DetermineStepOfScreen -> {
|
||||
|
|
@ -107,6 +108,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
|
|||
|
||||
val isLoadedBefore = noteState.walletBalance.state != ProgressState.Loading
|
||||
val balanceIsLoading = noteState.walletBalance.copy(
|
||||
currency = Currency.Blockchain(walletManager.wallet.blockchain),
|
||||
state = ProgressState.Loading,
|
||||
error = null,
|
||||
criticalError = null
|
||||
|
|
@ -117,9 +119,11 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
|
|||
val loadedBalance = onboardingManager.updateBalance(walletManager)
|
||||
loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) }
|
||||
delay(if (isLoadedBefore) 0 else 300)
|
||||
withMainContext { store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) }
|
||||
withMainContext {
|
||||
store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance))
|
||||
store.dispatch(OnboardingNoteAction.Balance.SetNonCriticalError(loadedBalance.error))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
is OnboardingNoteAction.Balance.Set -> {
|
||||
if (action.balance.balanceIsToppedUp()) {
|
||||
|
|
@ -128,7 +132,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
|
|||
}
|
||||
is OnboardingNoteAction.ShowAddressInfoDialog -> {
|
||||
val addressData = noteState.walletManager?.getAddressData() ?: return
|
||||
val addressWasCopied = noteState.resources.strings.addressWasCopied
|
||||
val addressWasCopied = globalState.resources.strings.addressWasCopied
|
||||
val appDialog = AppDialog.AddressInfoDialog(
|
||||
addressData,
|
||||
onCopyAddress = { store.dispatchToastNotification(addressWasCopied) },
|
||||
|
|
@ -143,7 +147,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
|
|||
OnboardingNoteAction.Done -> {
|
||||
store.dispatch(GlobalAction.Onboarding.Stop)
|
||||
scope.launch {
|
||||
globalState.tapWalletManager.onCardScanned(scanResponse)
|
||||
store.onCardScanned(scanResponse)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,18 +17,18 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt
|
|||
is GlobalAction.Onboarding.Start -> {
|
||||
state = OnboardingNoteState()
|
||||
}
|
||||
is OnboardingNoteAction.SetArtworkUrl -> {
|
||||
is OnboardingNoteAction.SetArtwork -> {
|
||||
state = state.copy(cardArtwork = action.artwork)
|
||||
}
|
||||
is OnboardingNoteAction.SetWalletManager -> {
|
||||
state = state.copy(walletManager = action.walletManager)
|
||||
}
|
||||
is OnboardingNoteAction.SetResources -> {
|
||||
state = state.copy(resources = action.resources)
|
||||
}
|
||||
is OnboardingNoteAction.Balance.Set -> {
|
||||
state = state.copy(walletBalance = action.balance)
|
||||
}
|
||||
is OnboardingNoteAction.Balance.SetNonCriticalError -> {
|
||||
state = state.copy(balanceNonCriticalError = action.error)
|
||||
}
|
||||
is OnboardingNoteAction.SetStepOfScreen -> {
|
||||
if (action.step != state.currentStep && state.steps.contains(action.step)) {
|
||||
state = state.copy(currentStep = action.step)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.onboarding.products.note.redux
|
||||
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import org.rekotlin.StateType
|
||||
|
|
@ -10,10 +11,10 @@ import org.rekotlin.StateType
|
|||
*/
|
||||
data class OnboardingNoteState(
|
||||
val walletManager: WalletManager? = null,
|
||||
val resources: AndroidResources = AndroidResources(),
|
||||
// UI
|
||||
val cardArtwork: Artwork? = null,
|
||||
val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(),
|
||||
val balanceNonCriticalError: TapError? = null,
|
||||
val currentStep: OnboardingNoteStep = OnboardingNoteStep.None,
|
||||
val steps: List<OnboardingNoteStep> = OnboardingNoteStep.values().toList(),
|
||||
val showConfetti: Boolean = false,
|
||||
|
|
@ -25,12 +26,4 @@ data class OnboardingNoteState(
|
|||
|
||||
enum class OnboardingNoteStep {
|
||||
None, CreateWallet, TopUpWallet, Done
|
||||
}
|
||||
|
||||
data class AndroidResources(
|
||||
val strings: OnboardingStringResources = OnboardingStringResources()
|
||||
)
|
||||
|
||||
data class OnboardingStringResources(
|
||||
val addressWasCopied: Int = -1
|
||||
)
|
||||
}
|
||||
|
|
@ -8,7 +8,9 @@ import androidx.constraintlayout.widget.ConstraintSet
|
|||
import androidx.core.view.isVisible
|
||||
import androidx.transition.TransitionManager
|
||||
import com.tangem.tap.common.extensions.getDrawableCompat
|
||||
import com.tangem.tap.common.redux.navigation.ShareElement
|
||||
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
|
||||
import com.tangem.tap.features.addBackPressHandler
|
||||
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
|
||||
import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction
|
||||
|
|
@ -17,8 +19,8 @@ import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOt
|
|||
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_bottom_action_views.*
|
||||
import kotlinx.android.synthetic.main.layout_onboarding_note.*
|
||||
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.*
|
||||
|
||||
/**
|
||||
|
|
@ -26,10 +28,19 @@ import kotlinx.android.synthetic.main.view_onboarding_progress.*
|
|||
*/
|
||||
class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCardsState>() {
|
||||
|
||||
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note
|
||||
override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_container_top
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
postponeEnterTransition()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
addBackPressHandler(this)
|
||||
|
||||
imv_front_card.transitionName = ShareElement.imvFrontCard
|
||||
startPostponedEnterTransition()
|
||||
|
||||
toolbar.setTitle(R.string.onboarding_title)
|
||||
store.dispatch(OnboardingOtherCardsAction.LoadCardArtwork)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ sealed class OnboardingOtherCardsAction : Action {
|
|||
object CreateWallet : OnboardingOtherCardsAction()
|
||||
|
||||
// from redux
|
||||
class SetArtworkUrl(val artwork: Artwork) : OnboardingOtherCardsAction()
|
||||
class SetArtwork(val artwork: Artwork) : OnboardingOtherCardsAction()
|
||||
object DetermineStepOfScreen : OnboardingOtherCardsAction()
|
||||
object Done : OnboardingOtherCardsAction()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.onboarding.products.otherCards.redux
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.extensions.withMainContext
|
||||
import com.tangem.tap.common.postUi
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -44,7 +45,7 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) {
|
|||
is OnboardingOtherCardsAction.LoadCardArtwork -> {
|
||||
scope.launch {
|
||||
val artwork = onboardingManager.loadArtwork()
|
||||
withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtworkUrl(artwork)) }
|
||||
withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtwork(artwork)) }
|
||||
}
|
||||
}
|
||||
is OnboardingOtherCardsAction.DetermineStepOfScreen -> {
|
||||
|
|
@ -83,7 +84,7 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) {
|
|||
OnboardingOtherCardsAction.Done -> {
|
||||
store.dispatch(GlobalAction.Onboarding.Stop)
|
||||
scope.launch {
|
||||
globalState.tapWalletManager.onCardScanned(onboardingManager.scanResponse)
|
||||
store.onCardScanned(onboardingManager.scanResponse)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingOtherC
|
|||
is GlobalAction.Onboarding.Start -> {
|
||||
state = OnboardingOtherCardsState()
|
||||
}
|
||||
is OnboardingOtherCardsAction.SetArtworkUrl -> {
|
||||
is OnboardingOtherCardsAction.SetArtwork -> {
|
||||
state = state.copy(cardArtwork = action.artwork)
|
||||
}
|
||||
is OnboardingOtherCardsAction.SetStepOfScreen -> {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
package com.tangem.tap.features.onboarding.products.twins.redux
|
||||
|
||||
import com.tangem.Message
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.twins.AssetReader
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
sealed class TwinCardsAction : Action {
|
||||
data class IfTwinsPrepareState(val scanResponse: ScanResponse) : TwinCardsAction()
|
||||
|
||||
object Init : TwinCardsAction()
|
||||
data class SetMode(val mode: CreateTwinWalletMode) : TwinCardsAction()
|
||||
data class SetStepOfScreen(val step: TwinCardsStep) : TwinCardsAction()
|
||||
data class SetUserUnderstand(val isUnderstand: Boolean) : TwinCardsAction()
|
||||
|
||||
sealed class CardsManager {
|
||||
data class Set(val manager: TwinCardsManager) : TwinCardsAction()
|
||||
object Release : TwinCardsAction()
|
||||
}
|
||||
|
||||
sealed class Wallet : TwinCardsAction() {
|
||||
data class HandleOnBackPressed(
|
||||
// this is necessary for the correct animation of the return of cards to the Home screen
|
||||
val shouldResetTwinCardsWidget: (should: Boolean, popAction: VoidCallback) -> Unit
|
||||
) : TwinCardsAction()
|
||||
|
||||
data class ShowInterruptDialog(val onOk: VoidCallback) : TwinCardsAction(), StateDialog
|
||||
|
||||
data class LaunchFirstStep(val initialMessage: Message, val reader: AssetReader) : TwinCardsAction()
|
||||
data class LaunchSecondStep(
|
||||
val initialMessage: Message,
|
||||
val preparingMessage: Message,
|
||||
val creatingWalletMessage: Message,
|
||||
) : TwinCardsAction()
|
||||
|
||||
data class LaunchThirdStep(val message: Message) : TwinCardsAction()
|
||||
}
|
||||
|
||||
|
||||
// for the onboarding
|
||||
data class SetPairCardId(val cardId: String) : TwinCardsAction()
|
||||
object TopUp : TwinCardsAction()
|
||||
object ShowAddressInfoDialog : TwinCardsAction()
|
||||
data class SetWalletManager(val walletManager: WalletManager) : TwinCardsAction()
|
||||
object Done : TwinCardsAction()
|
||||
|
||||
sealed class Balance {
|
||||
object Update : TwinCardsAction()
|
||||
data class Set(val balance: OnboardingWalletBalance) : TwinCardsAction()
|
||||
}
|
||||
|
||||
sealed class Confetti {
|
||||
object Show : TwinCardsAction()
|
||||
object Hide : TwinCardsAction()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
package com.tangem.tap.features.onboarding.products.twins.redux
|
||||
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.postUi
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.features.wallet.redux.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.DispatchFunction
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
class TwinCardsMiddleware {
|
||||
companion object {
|
||||
val handler = twinsWalletMiddleware
|
||||
}
|
||||
}
|
||||
|
||||
private val twinsWalletMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
handle(action, dispatch)
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handle(action: Action, dispatch: DispatchFunction) {
|
||||
val action = action as? TwinCardsAction ?: return
|
||||
|
||||
val globalState = store.state.globalState
|
||||
val onboardingManager = globalState.onboardingManager
|
||||
val twinCardsState = store.state.twinCardsState
|
||||
|
||||
fun getScanResponse(): ScanResponse {
|
||||
return when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse
|
||||
CreateTwinWalletMode.RecreateWallet -> globalState.scanResponse
|
||||
} ?: throw NullPointerException("ScanResponse can't be NULL")
|
||||
}
|
||||
|
||||
fun updateScanResponse(response: ScanResponse) {
|
||||
when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> onboardingManager?.scanResponse = response
|
||||
CreateTwinWalletMode.RecreateWallet -> store.dispatch(GlobalAction.SaveScanNoteResponse(response))
|
||||
}
|
||||
}
|
||||
|
||||
fun startCardActivation(cardId: String) {
|
||||
if (twinCardsState.mode == CreateTwinWalletMode.CreateWallet) {
|
||||
onboardingManager?.activationStarted(cardId)
|
||||
}
|
||||
}
|
||||
|
||||
fun finishCardActivation() {
|
||||
if (twinCardsState.mode == CreateTwinWalletMode.CreateWallet) {
|
||||
onboardingManager?.activationFinished(getScanResponse().card.cardId)
|
||||
twinCardsState.pairCardId?.let { onboardingManager?.activationFinished(it) }
|
||||
}
|
||||
}
|
||||
|
||||
when (action) {
|
||||
is TwinCardsAction.Init -> {
|
||||
if (twinCardsState.currentStep == TwinCardsStep.WelcomeOnly) return
|
||||
|
||||
when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> {
|
||||
if (preferencesStorage.wasTwinsOnboardingShown()) {
|
||||
val step = when {
|
||||
!getScanResponse().twinsIsTwinned() -> TwinCardsStep.CreateFirstWallet
|
||||
twinCardsState.walletBalance.balanceIsToppedUp() -> TwinCardsStep.Done
|
||||
else -> TwinCardsStep.TopUpWallet
|
||||
}
|
||||
store.dispatch((TwinCardsAction.SetStepOfScreen(step)))
|
||||
} else {
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Welcome))
|
||||
}
|
||||
}
|
||||
CreateTwinWalletMode.RecreateWallet -> {
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Warning))
|
||||
}
|
||||
}
|
||||
}
|
||||
is TwinCardsAction.Wallet.HandleOnBackPressed -> {
|
||||
val shouldReturnCardBack = twinCardsState.mode == CreateTwinWalletMode.CreateWallet
|
||||
&& twinCardsState.currentStep != TwinCardsStep.TopUpWallet
|
||||
&& twinCardsState.currentStep != TwinCardsStep.Done
|
||||
|
||||
if (twinCardsState.showAlert) {
|
||||
val onInterruptPrompt = {
|
||||
store.dispatch(TwinCardsAction.CardsManager.Release)
|
||||
action.shouldResetTwinCardsWidget(shouldReturnCardBack) {
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
}
|
||||
val stateDialog = TwinCardsAction.Wallet.ShowInterruptDialog(onInterruptPrompt)
|
||||
store.dispatchDialogShow(stateDialog)
|
||||
} else {
|
||||
store.dispatch(TwinCardsAction.CardsManager.Release)
|
||||
action.shouldResetTwinCardsWidget(shouldReturnCardBack) {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
}
|
||||
}
|
||||
is TwinCardsAction.SetStepOfScreen -> {
|
||||
when (action.step) {
|
||||
TwinCardsStep.WelcomeOnly -> preferencesStorage.saveTwinsOnboardingShown()
|
||||
TwinCardsStep.Welcome -> preferencesStorage.saveTwinsOnboardingShown()
|
||||
TwinCardsStep.TopUpWallet -> store.dispatch(TwinCardsAction.Balance.Update)
|
||||
TwinCardsStep.Done -> {
|
||||
finishCardActivation()
|
||||
postUi(500) { store.dispatch(TwinCardsAction.Confetti.Show) }
|
||||
}
|
||||
}
|
||||
}
|
||||
is TwinCardsAction.Wallet.LaunchFirstStep -> {
|
||||
val manager = TwinCardsManager(getScanResponse().card, action.reader)
|
||||
store.dispatch(TwinCardsAction.CardsManager.Set(manager))
|
||||
|
||||
scope.launch {
|
||||
when (val result = manager.createFirstWallet(action.initialMessage)) {
|
||||
is CompletionResult.Success -> {
|
||||
startCardActivation(result.data.cardId)
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
withMainContext {
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateSecondWallet))
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is TwinCardsAction.Wallet.LaunchSecondStep -> {
|
||||
val manager = twinCardsState.twinCardsManager ?: return
|
||||
|
||||
scope.launch {
|
||||
val result = manager.createSecondWallet(
|
||||
action.initialMessage,
|
||||
action.preparingMessage,
|
||||
action.creatingWalletMessage
|
||||
)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
startCardActivation(result.data.cardId)
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
withMainContext {
|
||||
store.dispatch(TwinCardsAction.SetPairCardId(result.data.cardId))
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateThirdWallet))
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is TwinCardsAction.Wallet.LaunchThirdStep -> {
|
||||
val manager = twinCardsState.twinCardsManager ?: return
|
||||
|
||||
scope.launch {
|
||||
when (val result = manager.complete(action.message)) {
|
||||
is Result.Success -> {
|
||||
updateScanResponse(result.data)
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
withMainContext { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.TopUpWallet)) }
|
||||
}
|
||||
is Result.Failure -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is TwinCardsAction.Balance.Update -> {
|
||||
val walletManager = if (twinCardsState.walletManager != null) {
|
||||
twinCardsState.walletManager
|
||||
} else {
|
||||
val wmFactory = globalState.tapWalletManager.walletManagerFactory
|
||||
val walletManager = wmFactory.makePrimaryWalletManager(getScanResponse()).guard {
|
||||
val message = "Loading cancelled. Cause: wallet manager didn't created"
|
||||
val customError = TapError.CustomError(message)
|
||||
store.dispatchErrorNotification(customError)
|
||||
return
|
||||
}
|
||||
dispatch(TwinCardsAction.SetWalletManager(walletManager))
|
||||
walletManager
|
||||
}
|
||||
val isLoadedBefore = twinCardsState.walletBalance.state != ProgressState.Loading
|
||||
val balanceIsLoading = twinCardsState.walletBalance.copy(
|
||||
currency = Currency.Blockchain(walletManager.wallet.blockchain),
|
||||
state = ProgressState.Loading,
|
||||
error = null,
|
||||
criticalError = null
|
||||
)
|
||||
store.dispatch(TwinCardsAction.Balance.Set(balanceIsLoading))
|
||||
|
||||
scope.launch {
|
||||
val loadedBalance = onboardingManager?.updateBalance(walletManager) ?: return@launch
|
||||
loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) }
|
||||
delay(if (isLoadedBefore) 0 else 300)
|
||||
withMainContext { store.dispatch(TwinCardsAction.Balance.Set(loadedBalance)) }
|
||||
}
|
||||
|
||||
}
|
||||
is TwinCardsAction.Balance.Set -> {
|
||||
if (action.balance.balanceIsToppedUp()) {
|
||||
scope.launch {
|
||||
withMainContext { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Done)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
is TwinCardsAction.ShowAddressInfoDialog -> {
|
||||
val addressData = twinCardsState.walletManager?.getAddressData() ?: return
|
||||
|
||||
val addressWasCopied = globalState.resources.strings.addressWasCopied
|
||||
val appDialog = AppDialog.AddressInfoDialog(
|
||||
addressData,
|
||||
onCopyAddress = { store.dispatchToastNotification(addressWasCopied) },
|
||||
onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) }
|
||||
)
|
||||
store.dispatchDialogShow(appDialog)
|
||||
}
|
||||
is TwinCardsAction.TopUp -> {
|
||||
val topUpUrl = twinCardsState.walletManager?.getToUpUrl() ?: return
|
||||
store.dispatchOpenUrl(topUpUrl)
|
||||
}
|
||||
TwinCardsAction.Done -> {
|
||||
scope.launch {
|
||||
val scanResponse = getScanResponse()
|
||||
store.onCardScanned(scanResponse)
|
||||
withMainContext {
|
||||
when (twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> {
|
||||
store.dispatch(GlobalAction.Onboarding.Stop)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
CreateTwinWalletMode.RecreateWallet -> {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.tangem.tap.features.onboarding.products.twins.redux
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.twins.getTwinCardNumber
|
||||
import org.rekotlin.Action
|
||||
|
||||
class TwinCardsReducer {
|
||||
companion object {
|
||||
fun reduce(action: Action, state: AppState): TwinCardsState = internalReduce(action, state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun internalReduce(action: Action, state: AppState): TwinCardsState {
|
||||
if (action !is TwinCardsAction) return state.twinCardsState
|
||||
|
||||
var state = state.twinCardsState
|
||||
|
||||
when (action) {
|
||||
is TwinCardsAction.IfTwinsPrepareState -> {
|
||||
state = if (action.scanResponse.isTangemTwins()) {
|
||||
val cardNumber = action.scanResponse.card.getTwinCardNumber() ?: return state
|
||||
|
||||
TwinCardsState(
|
||||
cardNumber = cardNumber,
|
||||
mode = CreateTwinWalletMode.CreateWallet,
|
||||
)
|
||||
} else {
|
||||
TwinCardsState()
|
||||
}
|
||||
}
|
||||
is TwinCardsAction.SetMode -> {
|
||||
state = state.copy(mode = action.mode)
|
||||
}
|
||||
is TwinCardsAction.SetStepOfScreen -> {
|
||||
state = state.copy(currentStep = action.step)
|
||||
}
|
||||
is TwinCardsAction.SetUserUnderstand -> {
|
||||
state = state.copy(userWasUnderstandIfWalletRecreate = action.isUnderstand)
|
||||
}
|
||||
is TwinCardsAction.CardsManager.Set -> {
|
||||
state = state.copy(twinCardsManager = action.manager)
|
||||
}
|
||||
TwinCardsAction.CardsManager.Release -> {
|
||||
state = state.copy(twinCardsManager = null)
|
||||
}
|
||||
is TwinCardsAction.SetPairCardId -> {
|
||||
state = state.copy(pairCardId = action.cardId)
|
||||
}
|
||||
is TwinCardsAction.SetWalletManager -> {
|
||||
state = state.copy(walletManager = action.walletManager)
|
||||
}
|
||||
is TwinCardsAction.Balance.Set -> {
|
||||
state = state.copy(walletBalance = action.balance)
|
||||
}
|
||||
is TwinCardsAction.Confetti.Show -> {
|
||||
state = state.copy(showConfetti = true)
|
||||
}
|
||||
is TwinCardsAction.Confetti.Hide -> {
|
||||
state = state.copy(showConfetti = false)
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package com.tangem.tap.features.onboarding.products.twins.redux
|
||||
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.tap.domain.twins.TwinCardNumber
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
|
||||
import org.rekotlin.StateType
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class TwinCardsState(
|
||||
// if cardNumber = null -> the scanned card is not Twin
|
||||
val cardNumber: TwinCardNumber? = null,
|
||||
val mode: CreateTwinWalletMode = CreateTwinWalletMode.CreateWallet,
|
||||
val currentStep: TwinCardsStep = TwinCardsStep.Welcome,
|
||||
val userWasUnderstandIfWalletRecreate: Boolean = false,
|
||||
// available only when creating or recreating wallets
|
||||
val twinCardsManager: TwinCardsManager? = null,
|
||||
|
||||
// for the onboarding
|
||||
val pairCardId: String? = null, // available after create\recreate wallets
|
||||
val walletManager: WalletManager? = null,
|
||||
val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(),
|
||||
val showConfetti: Boolean = false,
|
||||
) : StateType {
|
||||
|
||||
val steps: List<TwinCardsStep>
|
||||
get() = when (mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> listOf(
|
||||
TwinCardsStep.None,
|
||||
TwinCardsStep.CreateFirstWallet,
|
||||
TwinCardsStep.CreateSecondWallet,
|
||||
TwinCardsStep.CreateThirdWallet,
|
||||
TwinCardsStep.TopUpWallet,
|
||||
TwinCardsStep.Done
|
||||
)
|
||||
CreateTwinWalletMode.RecreateWallet -> listOf(
|
||||
TwinCardsStep.None,
|
||||
TwinCardsStep.CreateFirstWallet,
|
||||
TwinCardsStep.CreateSecondWallet,
|
||||
TwinCardsStep.CreateThirdWallet,
|
||||
TwinCardsStep.Done
|
||||
)
|
||||
}
|
||||
|
||||
val progress: Int
|
||||
get() = steps.indexOf(currentStep)
|
||||
|
||||
val showAlert: Boolean
|
||||
get() = currentStep == TwinCardsStep.CreateSecondWallet || currentStep == TwinCardsStep.CreateThirdWallet
|
||||
}
|
||||
|
||||
enum class CreateTwinWalletMode { CreateWallet, RecreateWallet }
|
||||
|
||||
enum class TwinCardsStep {
|
||||
None, WelcomeOnly, Welcome, Warning, CreateFirstWallet, CreateSecondWallet, CreateThirdWallet,
|
||||
|
||||
// for the onboarding
|
||||
TopUpWallet, Done
|
||||
}
|
||||
|
|
@ -0,0 +1,368 @@
|
|||
package com.tangem.tap.features.onboarding.products.twins.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.transition.TransitionInflater
|
||||
import androidx.transition.TransitionManager
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.Message
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget
|
||||
import com.tangem.tap.common.postUi
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.common.redux.navigation.ShareElement
|
||||
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
|
||||
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
|
||||
import com.tangem.tap.domain.twins.AssetReader
|
||||
import com.tangem.tap.domain.twins.TwinCardNumber
|
||||
import com.tangem.tap.domain.twins.TwinsCardWidget
|
||||
import com.tangem.tap.features.addBackPressHandler
|
||||
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
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 org.rekotlin.Action
|
||||
|
||||
class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
|
||||
|
||||
private var previousStep = TwinCardsStep.None
|
||||
|
||||
private lateinit var twinsWidget: TwinsCardWidget
|
||||
private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget
|
||||
|
||||
private val assetReader: AssetReader by lazy {
|
||||
object : AssetReader {
|
||||
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()
|
||||
store.dispatch(TwinCardsAction.Init)
|
||||
}
|
||||
|
||||
override fun configureTransitions() {
|
||||
when (store.state.twinCardsState.mode) {
|
||||
CreateTwinWalletMode.CreateWallet -> super.configureTransitions()
|
||||
CreateTwinWalletMode.RecreateWallet -> {
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
addBackPressHandler(this)
|
||||
|
||||
reconfigureLayoutForTwins()
|
||||
twinsWidget = TwinsCardWidget(LeapfrogWidget(cards_container)) { 250f }
|
||||
btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container)
|
||||
|
||||
toolbar.title = getText(R.string.twins_recreate_toolbar)
|
||||
|
||||
Picasso.get()
|
||||
.load(Artwork.TWIN_CARD_1)
|
||||
.placeholder(R.drawable.card_placeholder_black)
|
||||
?.into(imv_twin_front_card)
|
||||
|
||||
Picasso.get()
|
||||
.load(Artwork.TWIN_CARD_2)
|
||||
.placeholder(R.drawable.card_placeholder_white)
|
||||
?.into(imv_twin_back_card)
|
||||
}
|
||||
|
||||
private fun reconfigureLayoutForTwins() {
|
||||
imv_front_card.hide()
|
||||
imv_card_background.hide()
|
||||
cards_container.show()
|
||||
|
||||
imv_twin_front_card.transitionName = ShareElement.imvFrontCard
|
||||
imv_twin_back_card.transitionName = ShareElement.imvBackCard
|
||||
}
|
||||
|
||||
override fun subscribeToStore() {
|
||||
store.subscribe(this) { state ->
|
||||
state.skipRepeats { oldState, newState ->
|
||||
oldState.twinCardsState == newState.twinCardsState
|
||||
}.select { it.twinCardsState }
|
||||
}
|
||||
storeSubscribersList.add(this)
|
||||
}
|
||||
|
||||
override fun newState(state: TwinCardsState) {
|
||||
if (activity == null) return
|
||||
|
||||
// toolbar.title = when (state.mode) {
|
||||
// CreateTwinWalletMode.CreateWallet -> getText(R.string.wallet_button_create_wallet)
|
||||
// CreateTwinWalletMode.RecreateWallet -> getText(R.string.twins_recreate_toolbar)
|
||||
// }
|
||||
|
||||
pb_state.max = state.steps.size - 1
|
||||
pb_state.progress = state.progress
|
||||
|
||||
when (state.currentStep) {
|
||||
TwinCardsStep.WelcomeOnly -> setupWelcomeOnlyState(state)
|
||||
TwinCardsStep.Welcome -> setupWelcomeState(state)
|
||||
TwinCardsStep.Warning -> setupWarningState(state)
|
||||
TwinCardsStep.CreateFirstWallet -> setupCreateFirstWalletState(state)
|
||||
TwinCardsStep.CreateSecondWallet -> setupCreateSecondWalletState(state)
|
||||
TwinCardsStep.CreateThirdWallet -> setupCreateThirdWalletState(state)
|
||||
TwinCardsStep.TopUpWallet -> setupTopUpWalletState(state)
|
||||
TwinCardsStep.Done -> setupDoneState(state)
|
||||
}
|
||||
|
||||
setBalance(state)
|
||||
showConfetti(state.showConfetti)
|
||||
previousStep = state.currentStep
|
||||
}
|
||||
|
||||
private fun setBalance(state: TwinCardsState) {
|
||||
if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return
|
||||
|
||||
val balanceValue = state.walletBalance.value.stripZeroPlainString()
|
||||
val currency = state.walletBalance.currency.currencySymbol
|
||||
tv_balance_value.text = "$balanceValue $currency"
|
||||
}
|
||||
|
||||
private fun setupWelcomeOnlyState(state: TwinCardsState) {
|
||||
setupWelcomeState(state, NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
|
||||
private fun setupWelcomeState(state: TwinCardsState) {
|
||||
setupWelcomeState(state, TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet))
|
||||
}
|
||||
|
||||
private fun setupWelcomeState(state: TwinCardsState, mainAction: Action) {
|
||||
twinsWidget.toWelcome(false) { startPostponedEnterTransition() }
|
||||
|
||||
pb_state.hide()
|
||||
|
||||
tv_header.setText(R.string.twins_onboarding_subtitle)
|
||||
tv_body.text = getString(R.string.twins_onboarding_description_format, state.cardNumber?.pairIndexNumber())
|
||||
|
||||
btn_main_action.setText(R.string.common_continue)
|
||||
btn_main_action.setOnClickListener { store.dispatch(mainAction) }
|
||||
}
|
||||
|
||||
private fun setupWarningState(state: TwinCardsState) {
|
||||
twinsWidget.toWelcome(false) { startPostponedEnterTransition() }
|
||||
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 ->
|
||||
store.dispatch(TwinCardsAction.SetUserUnderstand(isChecked))
|
||||
}
|
||||
btn_main_action.isEnabled = state.userWasUnderstandIfWalletRecreate
|
||||
btn_main_action.setText(R.string.common_continue)
|
||||
btn_main_action.setOnClickListener {
|
||||
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateFirstWallet))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupCreateFirstWalletState(state: TwinCardsState) {
|
||||
bg_circle_large.hide()
|
||||
bg_circle_medium.hide()
|
||||
bg_circle_min.hide()
|
||||
chb_understand.hide()
|
||||
pb_state.show()
|
||||
|
||||
onboarding_main_container.beginDelayedTransition()
|
||||
|
||||
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) })
|
||||
}
|
||||
}
|
||||
|
||||
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.LaunchFirstStep(
|
||||
Message(getString(R.string.twins_recreate_title_format, twinIndexNumber)),
|
||||
assetReader
|
||||
))
|
||||
}
|
||||
|
||||
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) {
|
||||
startPostponedEnterTransition()
|
||||
}
|
||||
}
|
||||
}
|
||||
TwinCardNumber.Second -> {
|
||||
switchToCard(state.cardNumber, false) {
|
||||
twinsWidget.toActivate(false)
|
||||
startPostponedEnterTransition()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TwinCardsStep.CreateThirdWallet -> {
|
||||
twinsWidget.toActivate()
|
||||
}
|
||||
}
|
||||
|
||||
btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto)
|
||||
btn_main_action.setOnClickListener {
|
||||
store.dispatch(TwinCardsAction.TopUp)
|
||||
}
|
||||
|
||||
btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address)
|
||||
btn_alternative_action.setOnClickListener {
|
||||
store.dispatch(TwinCardsAction.ShowAddressInfoDialog)
|
||||
}
|
||||
|
||||
tv_header.setText(R.string.onboarding_top_up_header)
|
||||
tv_body.setText(R.string.onboarding_top_up_body)
|
||||
|
||||
btnRefreshBalanceWidget.changeState(state.walletBalance.state)
|
||||
if (btnRefreshBalanceWidget.isShowing != true) {
|
||||
postUi(300) {
|
||||
btnRefreshBalanceWidget.mainView.setOnClickListener {
|
||||
store.dispatch(TwinCardsAction.Balance.Update)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
private fun switchToCard(cardNumber: TwinCardNumber?, animate: Boolean = true, onEnd: VoidCallback = {}) {
|
||||
val position = cardNumber?.number ?: return
|
||||
|
||||
if (twinsWidget.leapfrogWidget.getViewPositionByIndex(1) != position - 1) {
|
||||
twinsWidget.leapfrogWidget.leap(animate, onEnd)
|
||||
} else {
|
||||
onEnd()
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed { should, popAction ->
|
||||
store.dispatch(TwinCardsAction.Confetti.Hide)
|
||||
showConfetti(false)
|
||||
if (should) switchToCard(TwinCardNumber.First, true, popAction)
|
||||
else popAction()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun TwinCardNumber?.indexNumber(): String {
|
||||
return this?.number?.toString() ?: ""
|
||||
}
|
||||
|
||||
private fun TwinCardNumber?.pairIndexNumber(): String {
|
||||
return this?.pairNumber()?.number?.toString() ?: ""
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.tap.features.onboarding.products.twins.ui.dialog
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class CreateWalletInterruptDialog {
|
||||
companion object {
|
||||
fun create(state: TwinCardsAction.Wallet.ShowInterruptDialog, context: Context): AlertDialog {
|
||||
return MaterialAlertDialogBuilder(context)
|
||||
.setMessage(R.string.twins_recreate_alert)
|
||||
.setPositiveButton(R.string.common_ok) { _, _ -> state.onOk() }
|
||||
.setNegativeButton(R.string.common_cancel) { _, _ -> }
|
||||
.setOnDismissListener { store.dispatchDialogHide() }
|
||||
.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@ package com.tangem.tap.features.onboarding.products.wallet
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.tap.features.BaseStoreFragment
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
|
||||
import com.tangem.tap.features.send.BaseStoreFragment
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import android.text.method.DigitsKeyListener
|
|||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.EditText
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.core.view.postDelayed
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
|
@ -25,7 +24,8 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.common.snackBar.MaxAmountSnackbar
|
||||
import com.tangem.tap.common.text.truncateMiddleWith
|
||||
import com.tangem.tap.common.toggleWidget.*
|
||||
import com.tangem.tap.features.send.BaseStoreFragment
|
||||
import com.tangem.tap.features.BaseStoreFragment
|
||||
import com.tangem.tap.features.addBackPressHandler
|
||||
import com.tangem.tap.features.send.redux.*
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi.*
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi.*
|
||||
|
|
@ -63,25 +63,9 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
private val sendSubscriber = SendStateSubscriber(this)
|
||||
private lateinit var keyboardObserver: KeyboardObserver
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
val externalTransactionData = store.state.sendState.externalTransactionData
|
||||
if (externalTransactionData == null) {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
} else {
|
||||
store.dispatch(
|
||||
WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
addBackPressHandler(this)
|
||||
|
||||
etAmountToSend = view.findViewById(R.id.etAmountToSend)
|
||||
|
||||
|
|
@ -97,7 +81,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
private fun initSendButtonStates() {
|
||||
btnSend.setOnClickListener {
|
||||
store.dispatch(SendActionUi.SendAmountToRecipient(
|
||||
Message(getString(R.string.initial_message_sign_header))
|
||||
Message(getString(R.string.initial_message_sign_header))
|
||||
))
|
||||
}
|
||||
sendBtn = IndeterminateProgressButtonWidget(btnSend, progress)
|
||||
|
|
@ -124,8 +108,8 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
}
|
||||
imvQrCode.setOnClickListener {
|
||||
startActivityForResult(
|
||||
Intent(requireContext(), ScanQrCodeActivity::class.java),
|
||||
ScanQrCodeActivity.SCAN_QR_REQUEST_CODE
|
||||
Intent(requireContext(), ScanQrCodeActivity::class.java),
|
||||
ScanQrCodeActivity.SCAN_QR_REQUEST_CODE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -281,6 +265,17 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
sp.edit().putString("mainCurrency", type.name).apply()
|
||||
}
|
||||
|
||||
override fun handleOnBackPressed() {
|
||||
val externalTransactionData = store.state.sendState.externalTransactionData
|
||||
if (externalTransactionData == null) {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
} else {
|
||||
store.dispatch(
|
||||
WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
keyboardObserver.unregisterListener()
|
||||
store.dispatch(ReleaseSendState)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.tap.features.send.ui.stateSubscribers
|
||||
|
||||
import com.tangem.tap.features.send.BaseStoreFragment
|
||||
import com.tangem.tap.features.BaseStoreFragment
|
||||
import org.rekotlin.StateType
|
||||
import org.rekotlin.StoreSubscriber
|
||||
import java.lang.ref.WeakReference
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import com.tangem.tap.common.redux.getMessageString
|
|||
import com.tangem.tap.common.text.DecimalDigitsInputFilter
|
||||
import com.tangem.tap.domain.MultiMessageError
|
||||
import com.tangem.tap.domain.assembleErrors
|
||||
import com.tangem.tap.features.send.BaseStoreFragment
|
||||
import com.tangem.tap.features.BaseStoreFragment
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error
|
||||
import com.tangem.tap.features.send.redux.FeeAction
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
|
|
@ -188,15 +188,15 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
|
|||
(fg as? SendFragment)?.saveMainCurrency(state.mainCurrency.type)
|
||||
|
||||
val balanceText = fg.getString(R.string.send_balance_subtitle_format,
|
||||
state.mainCurrency.currencySymbol,
|
||||
state.viewBalanceValue)
|
||||
state.mainCurrency.currencySymbol,
|
||||
state.viewBalanceValue)
|
||||
fg.tvBalance.update(balanceText)
|
||||
|
||||
fg.tilAmountToSend.isEnabled = state.inputIsEnabled
|
||||
|
||||
val imageRes = if (state.inputIsEnabled) R.drawable.ic_arrows_up_down else 0
|
||||
fg.tvAmountCurrency.setCompoundDrawablesWithIntrinsicBounds(0, 0, imageRes, 0)
|
||||
val textColor = if (state.inputIsEnabled) R.color.blue else R.color.textGray
|
||||
val textColor = if (state.inputIsEnabled) R.color.blue else R.color.textGray
|
||||
fg.tvAmountCurrency.setTextColor(fg.getColor(textColor))
|
||||
|
||||
}
|
||||
|
|
@ -245,7 +245,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
|
|||
totalLayout.tvTotalValue.update("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}")
|
||||
|
||||
val willSent = getString(R.string.send_total_subtitle_format,
|
||||
receipt.willSentCrypto, receipt.symbols.crypto)
|
||||
receipt.willSentCrypto, receipt.symbols.crypto)
|
||||
totalLayout.tvWillBeSentValue.update(willSent)
|
||||
|
||||
}
|
||||
|
|
@ -277,9 +277,9 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
|
|||
totalLayout.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
|
||||
R.string.send_total_subtitle_asset_format,
|
||||
receipt.symbols.token ?: "", receipt.willSentToken,
|
||||
receipt.symbols.crypto, receipt.willSentFeeCoin
|
||||
)
|
||||
totalLayout.tvWillBeSentValue.update(willSent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import com.tangem.tap.mainScope
|
|||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fragment_add_tokens.*
|
||||
import kotlinx.android.synthetic.main.fragment_details_twin_cards.toolbar
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ import com.tangem.blockchain.common.TransactionData
|
|||
import com.tangem.blockchain.common.TransactionStatus
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
import com.tangem.tap.domain.extensions.toSendableAmounts
|
||||
|
||||
data class PendingTransaction(
|
||||
val address: String?,
|
||||
val amount: String?,
|
||||
val currency: String,
|
||||
val type: PendingTransactionType
|
||||
val address: String?,
|
||||
val amount: String?,
|
||||
val currency: String,
|
||||
val type: PendingTransactionType
|
||||
)
|
||||
|
||||
enum class PendingTransactionType { Incoming, Outgoing, Unknown }
|
||||
|
|
@ -36,10 +37,10 @@ fun TransactionData.toPendingTransaction(walletAddress: String): PendingTransact
|
|||
}
|
||||
|
||||
return PendingTransaction(
|
||||
if (address == "unknown") null else address,
|
||||
this.amount.value?.toFormattedString(amount.decimals),
|
||||
this.amount.currencySymbol,
|
||||
type
|
||||
if (address == "unknown") null else address,
|
||||
this.amount.value?.toFormattedString(amount.decimals),
|
||||
this.amount.currencySymbol,
|
||||
type
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -65,5 +66,9 @@ fun Wallet.getPendingTransactions(): List<PendingTransaction> {
|
|||
}
|
||||
|
||||
fun Wallet.hasPendingTransactions(): Boolean {
|
||||
return recentTransactions.toPendingTransactions(address).isNotEmpty()
|
||||
return getPendingTransactions().isNotEmpty()
|
||||
}
|
||||
|
||||
fun Wallet.hasSendableAmountsOrPendingTransactions(): Boolean {
|
||||
return hasPendingTransactions() || amounts.toSendableAmounts().isNotEmpty()
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ import com.tangem.tap.common.redux.NotificationAction
|
|||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.tokens.CardCurrencies
|
||||
import com.tangem.tap.domain.twins.TwinCardNumber
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -147,12 +146,4 @@ sealed class WalletAction : Action {
|
|||
|
||||
data class ChangeSelectedAddress(val type: AddressType) : WalletAction()
|
||||
|
||||
sealed class TwinsAction : WalletAction() {
|
||||
object ShowOnboarding : TwinsAction()
|
||||
object SetOnboardingShown : TwinsAction()
|
||||
data class SetTwinCard(
|
||||
val number: TwinCardNumber,
|
||||
val isCreatingTwinCardsAllowed: Boolean,
|
||||
) : TwinsAction()
|
||||
}
|
||||
}
|
||||
|
|
@ -12,39 +12,49 @@ import com.tangem.tap.common.toggleWidget.WidgetState
|
|||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.extensions.toSendableAmounts
|
||||
import com.tangem.tap.domain.topup.TradeCryptoHelper
|
||||
import com.tangem.tap.domain.twins.TwinCardNumber
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.models.toPendingTransactions
|
||||
import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StateType
|
||||
import java.math.BigDecimal
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
data class WalletState(
|
||||
val state: ProgressState = ProgressState.Done,
|
||||
val error: ErrorType? = null,
|
||||
val cardImage: Artwork? = null,
|
||||
val hashesCountVerified: Boolean? = null,
|
||||
val walletDialog: StateDialog? = null,
|
||||
val twinCardsState: TwinCardsState? = null,
|
||||
val mainWarningsList: List<WarningMessage> = mutableListOf(),
|
||||
val wallets: List<WalletData> = emptyList(),
|
||||
val walletManagers: List<WalletManager> = emptyList(),
|
||||
val isMultiwalletAllowed: Boolean = false,
|
||||
val cardCurrency: CryptoCurrencyName? = null,
|
||||
val selectedWallet: Currency? = null,
|
||||
val primaryBlockchain: Blockchain? = null,
|
||||
val primaryToken: Token? = null,
|
||||
val isTestnet: Boolean = false,
|
||||
val tradeCryptoAllowed: TradeCryptoAvailability = TradeCryptoAvailability()
|
||||
val state: ProgressState = ProgressState.Done,
|
||||
val error: ErrorType? = null,
|
||||
val cardImage: Artwork? = null,
|
||||
val hashesCountVerified: Boolean? = null,
|
||||
val walletDialog: StateDialog? = null,
|
||||
val mainWarningsList: List<WarningMessage> = mutableListOf(),
|
||||
val wallets: List<WalletData> = emptyList(),
|
||||
val walletManagers: List<WalletManager> = emptyList(),
|
||||
val isMultiwalletAllowed: Boolean = false,
|
||||
val cardCurrency: CryptoCurrencyName? = null,
|
||||
val selectedWallet: Currency? = null,
|
||||
val primaryBlockchain: Blockchain? = null,
|
||||
val primaryToken: Token? = null,
|
||||
val isTestnet: Boolean = false,
|
||||
val tradeCryptoAllowed: TradeCryptoAvailability = TradeCryptoAvailability()
|
||||
) : StateType {
|
||||
|
||||
// if you do not delegate - the application crashes on startup,
|
||||
// because twinCardsState has not been created yet
|
||||
val twinCardsState: TwinCardsState by ReadOnlyProperty<Any, TwinCardsState> { thisRef, property ->
|
||||
store.state.twinCardsState
|
||||
}
|
||||
|
||||
val isTangemTwins: Boolean
|
||||
get() = store.state.globalState.scanResponse?.isTangemTwins() == true
|
||||
|
||||
val primaryWallet = if (wallets.isNotEmpty()) wallets[0] else null
|
||||
|
||||
val shouldShowDetails: Boolean =
|
||||
primaryWallet?.currencyData?.status != com.tangem.tap.features.wallet.ui.BalanceStatus.EmptyCard &&
|
||||
primaryWallet?.currencyData?.status != com.tangem.tap.features.wallet.ui.BalanceStatus.UnknownBlockchain
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain
|
||||
|
||||
val blockchains: List<Blockchain>
|
||||
get() = walletManagers.map { it.wallet.blockchain }
|
||||
|
|
@ -57,16 +67,16 @@ data class WalletState(
|
|||
return walletManagers.find { it.wallet.blockchain == token.blockchain }
|
||||
}
|
||||
|
||||
fun getWalletManager(currency: Currency?) : WalletManager? {
|
||||
fun getWalletManager(currency: Currency?): WalletManager? {
|
||||
if (currency?.blockchain == null) return null
|
||||
return walletManagers.find { it.wallet.blockchain == currency.blockchain }
|
||||
}
|
||||
|
||||
fun getWalletManager(blockchain: Blockchain) : WalletManager? {
|
||||
fun getWalletManager(blockchain: Blockchain): WalletManager? {
|
||||
return walletManagers.find { it.wallet.blockchain == blockchain }
|
||||
}
|
||||
|
||||
fun getWalletData(currency: Currency?) : WalletData? {
|
||||
fun getWalletData(currency: Currency?): WalletData? {
|
||||
if (currency == null) return null
|
||||
return wallets.find { it.currency == currency }
|
||||
}
|
||||
|
|
@ -93,7 +103,7 @@ data class WalletState(
|
|||
?: return true
|
||||
|
||||
if (walletData.currency is Currency.Blockchain &&
|
||||
walletManager.cardTokens.isNotEmpty()
|
||||
walletManager.cardTokens.isNotEmpty()
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
|
@ -105,9 +115,9 @@ data class WalletState(
|
|||
wallet.amounts.toSendableAmounts().isEmpty()
|
||||
} else if (walletData.currency is Currency.Token) (
|
||||
return wallet.recentTransactions.toPendingTransactionsForToken(
|
||||
walletData.currency.token, wallet.address).isEmpty()
|
||||
walletData.currency.token, wallet.address).isEmpty()
|
||||
&& wallet.amounts[AmountType.Token(token = walletData.currency.token)]
|
||||
?.isAboveZero() != true
|
||||
?.isAboveZero() != true
|
||||
)
|
||||
}
|
||||
return false
|
||||
|
|
@ -137,9 +147,9 @@ data class WalletState(
|
|||
}
|
||||
}
|
||||
|
||||
sealed class WalletDialog: StateDialog {
|
||||
sealed class WalletDialog : StateDialog {
|
||||
data class QrDialog(
|
||||
val qrCode: Bitmap?, val shareUrl: String?, val currencyName: CryptoCurrencyName?
|
||||
val qrCode: Bitmap?, val shareUrl: String?, val currencyName: CryptoCurrencyName?
|
||||
) : WalletDialog()
|
||||
|
||||
data class SelectAmountToSendDialog(val amounts: List<Amount>?) : WalletDialog()
|
||||
|
|
@ -147,7 +157,7 @@ sealed class WalletDialog: StateDialog {
|
|||
object ChooseTradeActionDialog : WalletDialog()
|
||||
}
|
||||
|
||||
enum class ProgressState: WidgetState { Loading, Done, Error }
|
||||
enum class ProgressState : WidgetState { Loading, Done, Error }
|
||||
|
||||
enum class ErrorType { NoInternetConnection }
|
||||
|
||||
|
|
@ -157,22 +167,22 @@ sealed class WalletMainButton(enabled: Boolean) : Button(enabled) {
|
|||
}
|
||||
|
||||
data class WalletAddresses(
|
||||
val selectedAddress: AddressData,
|
||||
val list: List<AddressData>
|
||||
val selectedAddress: AddressData,
|
||||
val list: List<AddressData>
|
||||
)
|
||||
|
||||
data class AddressData(
|
||||
val address: String,
|
||||
val type: AddressType,
|
||||
val shareUrl: String,
|
||||
val exploreUrl: String,
|
||||
val address: String,
|
||||
val type: AddressType,
|
||||
val shareUrl: String,
|
||||
val exploreUrl: String,
|
||||
) {
|
||||
val qrCode: Bitmap by lazy { shareUrl.toQrCode() }
|
||||
}
|
||||
|
||||
data class Artwork(
|
||||
val artworkId: String,
|
||||
val artwork: Bitmap? = null
|
||||
val artworkId: String,
|
||||
val artwork: Bitmap? = null
|
||||
) {
|
||||
companion object {
|
||||
const val DEFAULT_IMG_URL = "https://app.tangem.com/cards/card_default.png"
|
||||
|
|
@ -198,12 +208,6 @@ data class TradeCryptoAvailability(
|
|||
val availableToSell: Set<String> = TradeCryptoHelper.AVAILABLE_TO_SELL,
|
||||
)
|
||||
|
||||
data class TwinCardsState(
|
||||
val cardNumber: TwinCardNumber?,
|
||||
val showTwinOnboarding: Boolean,
|
||||
val isCreatingTwinCardsAllowed: Boolean
|
||||
)
|
||||
|
||||
data class WalletData(
|
||||
val pendingTransactions: List<PendingTransaction> = emptyList(),
|
||||
val hashesCountVerified: Boolean? = null,
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
package com.tangem.tap.features.wallet.redux.middlewares
|
||||
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.store
|
||||
|
||||
class TwinsMiddleware {
|
||||
fun handle(action: WalletAction.TwinsAction) {
|
||||
when (action) {
|
||||
is WalletAction.TwinsAction.SetTwinCard -> {
|
||||
val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown()
|
||||
if (showOnboarding) store.dispatch(WalletAction.TwinsAction.ShowOnboarding)
|
||||
}
|
||||
WalletAction.TwinsAction.SetOnboardingShown -> {
|
||||
preferencesStorage.saveTwinsOnboardingShown()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,209 +11,185 @@ import com.tangem.common.services.Result
|
|||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.copyToClipboard
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.shareText
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.extensions.toSendableAmounts
|
||||
import com.tangem.tap.domain.twins.TwinsHelper
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.twins.CreateTwinWallet
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.network.NetworkStateChanged
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.DispatchFunction
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
class WalletMiddleware {
|
||||
private val tradeCryptoMiddleware = TradeCryptoMiddleware()
|
||||
private val twinsMiddleware = TwinsMiddleware()
|
||||
private val warningsMiddleware = WarningsMiddleware()
|
||||
private val multiWalletMiddleware = MultiWalletMiddleware()
|
||||
|
||||
val walletMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
val globalState = state()?.globalState
|
||||
val walletState = state()?.walletState
|
||||
when (action) {
|
||||
is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(action)
|
||||
is WalletAction.TwinsAction -> twinsMiddleware.handle(action)
|
||||
is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState)
|
||||
is WalletAction.MultiWallet ->
|
||||
multiWalletMiddleware.handle(action, walletState, globalState)
|
||||
is WalletAction.LoadWallet -> {
|
||||
scope.launch {
|
||||
if (action.blockchain == null) {
|
||||
walletState?.walletManagers?.map { walletManager ->
|
||||
globalState?.tapWalletManager?.loadWalletData(walletManager)
|
||||
}
|
||||
} else {
|
||||
val walletManager = walletState?.getWalletManager(action.blockchain)
|
||||
walletManager?.let { globalState?.tapWalletManager?.loadWalletData(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadWallet.Success -> {
|
||||
val coinAmount = action.wallet.amounts[AmountType.Coin]?.value
|
||||
if (coinAmount != null && !coinAmount.isZero()) {
|
||||
if (walletState?.getWalletData(action.wallet.blockchain) == null) {
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(action.wallet.blockchain))
|
||||
store.dispatch(WalletAction.LoadWallet.Success(action.wallet))
|
||||
}
|
||||
}
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
|
||||
warningsMiddleware.tryToShowAppRatingWarning(action.wallet)
|
||||
}
|
||||
is WalletAction.LoadFiatRate -> {
|
||||
scope.launch {
|
||||
when {
|
||||
action.wallet != null -> {
|
||||
globalState?.tapWalletManager?.loadFiatRate(
|
||||
globalState.appCurrency, action.wallet
|
||||
)
|
||||
}
|
||||
action.currency != null -> {
|
||||
globalState?.tapWalletManager?.loadFiatRate(
|
||||
globalState.appCurrency, action.currency
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
globalState?.tapWalletManager?.loadFiatRate(
|
||||
fiatCurrency = globalState.appCurrency,
|
||||
currencies = walletState?.wallets?.mapNotNull { it.currency }
|
||||
?: emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.CreateWallet -> {
|
||||
if (walletState?.twinCardsState != null) {
|
||||
store.dispatch(DetailsAction.CreateTwinWalletAction.ShowWarning(
|
||||
globalState?.scanResponse?.card?.cardId?.let {
|
||||
TwinsHelper.getTwinCardNumber(it)
|
||||
},
|
||||
CreateTwinWallet.CreateWallet
|
||||
))
|
||||
} else {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.createWallet(
|
||||
globalState?.scanResponse?.card?.cardId
|
||||
)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val scanNoteResponse =
|
||||
globalState?.scanResponse?.copy(card = result.data)
|
||||
scanNoteResponse?.let {
|
||||
globalState.tapWalletManager.onCardScanned(scanNoteResponse)
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
|
||||
card = store.state.detailsState.card
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.UpdateWallet -> {
|
||||
if (action.blockchain != null) {
|
||||
scope.launch {
|
||||
val walletManager = walletState?.getWalletManager(action.blockchain)
|
||||
walletManager?.let { globalState?.tapWalletManager?.updateWallet(it) }
|
||||
}
|
||||
} else {
|
||||
scope.launch {
|
||||
if (walletState?.state == ProgressState.Done) {
|
||||
walletState.walletManagers.map { walletManager ->
|
||||
globalState?.tapWalletManager?.updateWallet(walletManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handleAction(action, dispatch)
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAction(action: Action, dispatch: DispatchFunction) {
|
||||
val globalState = store.state.globalState
|
||||
val walletState = store.state.walletState
|
||||
|
||||
when (action) {
|
||||
is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(action)
|
||||
is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState)
|
||||
is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState, globalState)
|
||||
is WalletAction.LoadWallet -> {
|
||||
scope.launch {
|
||||
if (action.blockchain == null) {
|
||||
walletState.walletManagers.map { walletManager ->
|
||||
globalState.tapWalletManager.loadWalletData(walletManager)
|
||||
}
|
||||
} else {
|
||||
val walletManager = walletState.getWalletManager(action.blockchain)
|
||||
walletManager?.let { globalState.tapWalletManager.loadWalletData(it) }
|
||||
}
|
||||
is WalletAction.Scan -> {
|
||||
store.dispatch(HomeAction.ShouldScanCardOnResume(true))
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadWallet.Success -> {
|
||||
val coinAmount = action.wallet.amounts[AmountType.Coin]?.value
|
||||
if (coinAmount != null && !coinAmount.isZero()) {
|
||||
if (walletState.getWalletData(action.wallet.blockchain) == null) {
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(action.wallet.blockchain))
|
||||
store.dispatch(WalletAction.LoadWallet.Success(action.wallet))
|
||||
}
|
||||
is WalletAction.LoadCardInfo -> {
|
||||
scope.launch {
|
||||
val response = OnlineCardVerifier().getCardInfo(
|
||||
action.card.cardId, action.card.cardPublicKey
|
||||
}
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
|
||||
warningsMiddleware.tryToShowAppRatingWarning(action.wallet)
|
||||
}
|
||||
is WalletAction.LoadFiatRate -> {
|
||||
scope.launch {
|
||||
when {
|
||||
action.wallet != null -> {
|
||||
globalState.tapWalletManager.loadFiatRate(
|
||||
globalState.appCurrency, action.wallet
|
||||
)
|
||||
when (response) {
|
||||
is Result.Success -> {
|
||||
store.dispatchOnMain(
|
||||
WalletAction.SetArtworkId(response.data.artwork?.id)
|
||||
)
|
||||
store.dispatchOnMain(
|
||||
WalletAction.LoadArtwork(action.card, response.data.artwork?.id)
|
||||
)
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.SetIfCardVerifiedOnline(response.data.passed)
|
||||
)
|
||||
}
|
||||
is Result.Failure -> {
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.SetIfCardVerifiedOnline(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
store.dispatchOnMain(WalletAction.Warnings.CheckIfNeeded)
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadData -> {
|
||||
scope.launch {
|
||||
val scanNoteResponse = globalState?.scanResponse
|
||||
?: return@launch
|
||||
if (!walletState?.wallets.isNullOrEmpty()) {
|
||||
globalState.tapWalletManager.reloadData(scanNoteResponse)
|
||||
} else {
|
||||
globalState.tapWalletManager.loadData(scanNoteResponse)
|
||||
}
|
||||
action.currency != null -> {
|
||||
globalState.tapWalletManager.loadFiatRate(
|
||||
globalState.appCurrency, action.currency
|
||||
)
|
||||
}
|
||||
}
|
||||
is NetworkStateChanged -> {
|
||||
globalState?.scanResponse?.let { scanNoteResponse ->
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
|
||||
scope.launch {
|
||||
globalState.tapWalletManager.loadData(scanNoteResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.CopyAddress -> {
|
||||
action.context.copyToClipboard(action.address)
|
||||
store.dispatch(WalletAction.CopyAddress.Success)
|
||||
}
|
||||
is WalletAction.ShareAddress -> {
|
||||
action.context.shareText(action.address)
|
||||
}
|
||||
is WalletAction.ExploreAddress -> {
|
||||
val uri = Uri.parse(action.exploreUrl)
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
ContextCompat.startActivity(action.context, intent, null)
|
||||
}
|
||||
is WalletAction.Send -> {
|
||||
val newAction = prepareSendAction(action.amount, state()?.walletState)
|
||||
store.dispatch(newAction)
|
||||
if (newAction is PrepareSendScreen) {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Send))
|
||||
else -> {
|
||||
globalState.tapWalletManager.loadFiatRate(
|
||||
fiatCurrency = globalState.appCurrency,
|
||||
currencies = walletState.wallets.mapNotNull { it.currency }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
is WalletAction.CreateWallet -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.createWallet(
|
||||
globalState.scanResponse?.card?.cardId
|
||||
)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val scanNoteResponse = globalState.scanResponse?.copy(card = result.data)
|
||||
scanNoteResponse?.let { store.onCardScanned(scanNoteResponse) }
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
FirebaseAnalyticsHandler.logCardSdkError(
|
||||
error,
|
||||
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
|
||||
card = store.state.detailsState.card
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.UpdateWallet -> {
|
||||
if (action.blockchain != null) {
|
||||
scope.launch {
|
||||
val walletManager = walletState.getWalletManager(action.blockchain)
|
||||
walletManager?.let { globalState.tapWalletManager.updateWallet(it) }
|
||||
}
|
||||
} else {
|
||||
scope.launch {
|
||||
if (walletState.state == ProgressState.Done) {
|
||||
walletState.walletManagers.map { walletManager ->
|
||||
globalState.tapWalletManager.updateWallet(walletManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is WalletAction.Scan -> {
|
||||
store.dispatch(HomeAction.ShouldScanCardOnResume(true))
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
is WalletAction.LoadCardInfo -> {
|
||||
scope.launch {
|
||||
val response = OnlineCardVerifier().getCardInfo(
|
||||
action.card.cardId, action.card.cardPublicKey
|
||||
)
|
||||
when (response) {
|
||||
is Result.Success -> {
|
||||
val actionList = listOf(
|
||||
WalletAction.SetArtworkId(response.data.artwork?.id),
|
||||
WalletAction.LoadArtwork(action.card, response.data.artwork?.id),
|
||||
GlobalAction.SetIfCardVerifiedOnline(response.data.passed)
|
||||
)
|
||||
actionList.forEach { store.dispatch(it) }
|
||||
}
|
||||
is Result.Failure -> {
|
||||
store.dispatchOnMain(GlobalAction.SetIfCardVerifiedOnline(false))
|
||||
}
|
||||
}
|
||||
store.dispatchOnMain(WalletAction.Warnings.CheckIfNeeded)
|
||||
}
|
||||
}
|
||||
is WalletAction.LoadData -> {
|
||||
scope.launch {
|
||||
val scanNoteResponse = globalState.scanResponse ?: return@launch
|
||||
if (!walletState.wallets.isEmpty()) {
|
||||
globalState.tapWalletManager.reloadData(scanNoteResponse)
|
||||
} else {
|
||||
globalState.tapWalletManager.loadData(scanNoteResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
is NetworkStateChanged -> {
|
||||
globalState.scanResponse?.let { scanNoteResponse ->
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
|
||||
scope.launch { globalState.tapWalletManager.loadData(scanNoteResponse) }
|
||||
}
|
||||
}
|
||||
is WalletAction.CopyAddress -> {
|
||||
action.context.copyToClipboard(action.address)
|
||||
store.dispatch(WalletAction.CopyAddress.Success)
|
||||
}
|
||||
is WalletAction.ShareAddress -> {
|
||||
action.context.shareText(action.address)
|
||||
}
|
||||
is WalletAction.ExploreAddress -> {
|
||||
val uri = Uri.parse(action.exploreUrl)
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
ContextCompat.startActivity(action.context, intent, null)
|
||||
}
|
||||
is WalletAction.Send -> {
|
||||
val newAction = prepareSendAction(action.amount, store.state.walletState)
|
||||
store.dispatch(newAction)
|
||||
if (newAction is PrepareSendScreen) {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Send))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -236,12 +212,12 @@ class WalletMiddleware {
|
|||
when (currency) {
|
||||
is Currency.Blockchain -> {
|
||||
val amountToSend = amounts?.find { it.currencySymbol == currency.blockchain.currency }
|
||||
?: return WalletAction.Send.ChooseCurrency(amounts)
|
||||
?: return WalletAction.Send.ChooseCurrency(amounts)
|
||||
PrepareSendScreen(amountToSend, selectedWalletData.fiatRate, walletManager)
|
||||
}
|
||||
is Currency.Token -> {
|
||||
val amountToSend = amounts?.find { it.currencySymbol == currency.token.symbol }
|
||||
?: return WalletAction.Send.ChooseCurrency(amounts)
|
||||
?: return WalletAction.Send.ChooseCurrency(amounts)
|
||||
prepareSendActionForToken(amountToSend, state, selectedWalletData, wallet, walletManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -257,8 +233,8 @@ class WalletMiddleware {
|
|||
}
|
||||
|
||||
private fun prepareSendActionForToken(
|
||||
amount: Amount, state: WalletState?, selectedWalletData: WalletData?, wallet: Wallet?,
|
||||
walletManager: WalletManager?
|
||||
amount: Amount, state: WalletState?, selectedWalletData: WalletData?, wallet: Wallet?,
|
||||
walletManager: WalletManager?
|
||||
): PrepareSendScreen {
|
||||
val coinRate = state?.getWalletData(wallet?.blockchain)?.fiatRate
|
||||
val tokenRate = if (state?.isMultiwalletAllowed == true) {
|
||||
|
|
@ -267,7 +243,7 @@ class WalletMiddleware {
|
|||
selectedWalletData?.currencyData?.token?.fiatRate
|
||||
}
|
||||
return PrepareSendScreen(
|
||||
wallet?.amounts?.get(AmountType.Coin), coinRate, walletManager,
|
||||
amount, tokenRate)
|
||||
wallet?.amounts?.get(AmountType.Coin), coinRate, walletManager,
|
||||
amount, tokenRate)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import com.tangem.tap.domain.extensions.getSingleWallet
|
|||
import com.tangem.tap.domain.extensions.hasSignedHashes
|
||||
import com.tangem.tap.domain.extensions.remainingSignatures
|
||||
import com.tangem.tap.domain.isMultiwalletAllowed
|
||||
import com.tangem.tap.domain.twins.isTangemTwin
|
||||
import com.tangem.tap.domain.tasks.product.ScanResponse
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.network.NetworkConnectivity
|
||||
import com.tangem.tap.preferencesStorage
|
||||
|
|
@ -51,12 +51,12 @@ class WarningsMiddleware {
|
|||
}
|
||||
is WalletAction.Warnings.CheckRemainingSignatures -> {
|
||||
if (action.remainingSignatures != null &&
|
||||
action.remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING
|
||||
action.remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING
|
||||
) {
|
||||
store.state.globalState.warningManager
|
||||
?.removeWarnings(
|
||||
messageRes = R.string.warning_low_signatures_format
|
||||
)
|
||||
?.removeWarnings(
|
||||
messageRes = R.string.warning_low_signatures_format
|
||||
)
|
||||
addWarningMessage(
|
||||
warning =
|
||||
WarningMessagesManager.remainingSignaturesNotEnough(action.remainingSignatures),
|
||||
|
|
@ -81,7 +81,8 @@ class WarningsMiddleware {
|
|||
}
|
||||
|
||||
private fun showCardWarningsIfNeeded(globalState: GlobalState?) {
|
||||
globalState?.scanResponse?.card?.let { card ->
|
||||
globalState?.scanResponse?.let { scanResponse ->
|
||||
val card = scanResponse.card
|
||||
globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local)
|
||||
if (card.isTestCard) {
|
||||
addWarningMessage(WarningMessagesManager.testCardWarning(), autoUpdate = true)
|
||||
|
|
@ -92,7 +93,7 @@ class WarningsMiddleware {
|
|||
if (card.firmwareVersion.type != FirmwareVersion.FirmwareType.Release) {
|
||||
addWarningMessage(WarningMessagesManager.devCardWarning())
|
||||
} else if (!preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) {
|
||||
checkIfWarningNeeded(card)?.let { warning -> addWarningMessage(warning) }
|
||||
checkIfWarningNeeded(scanResponse)?.let { warning -> addWarningMessage(warning) }
|
||||
}
|
||||
if (card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release) {
|
||||
if (!globalState.cardVerifiedOnline) {
|
||||
|
|
@ -106,7 +107,7 @@ class WarningsMiddleware {
|
|||
private fun showWarningLowRemainingSignaturesIfNeeded(card: Card) {
|
||||
val remainingSignatures = card.remainingSignatures
|
||||
if (remainingSignatures != null &&
|
||||
remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING
|
||||
remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING
|
||||
) {
|
||||
addWarningMessage(
|
||||
WarningMessagesManager.remainingSignaturesNotEnough(
|
||||
|
|
@ -117,12 +118,12 @@ class WarningsMiddleware {
|
|||
}
|
||||
|
||||
private fun checkIfWarningNeeded(
|
||||
card: Card,
|
||||
scanResponse: ScanResponse,
|
||||
): WarningMessage? {
|
||||
if (card.isTangemTwin()) return null
|
||||
if (scanResponse.isTangemTwins()) return null
|
||||
|
||||
if (card.isMultiwalletAllowed) {
|
||||
return if (card.hasSignedHashes()) {
|
||||
if (scanResponse.card.isMultiwalletAllowed) {
|
||||
return if (scanResponse.card.hasSignedHashes()) {
|
||||
WarningMessagesManager.signedHashesMultiWalletWarning()
|
||||
} else {
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId)
|
||||
|
|
@ -133,7 +134,7 @@ class WarningsMiddleware {
|
|||
val validator = store.state.walletState.walletManagers.firstOrNull()
|
||||
as? SignatureCountValidator
|
||||
return if (validator == null) {
|
||||
if (card.hasSignedHashes()) {
|
||||
if (scanResponse.card.hasSignedHashes()) {
|
||||
WarningMessagesManager.alreadySignedHashesWarning()
|
||||
} else {
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId)
|
||||
|
|
@ -149,10 +150,11 @@ class WarningsMiddleware {
|
|||
if (store.state.walletState.hashesCountVerified != false) return
|
||||
if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) return
|
||||
|
||||
val card = store.state.globalState.scanResponse?.card
|
||||
val scanResponse = store.state.globalState.scanResponse
|
||||
val card = scanResponse?.card
|
||||
if (card == null || preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) return
|
||||
|
||||
if (card.isTangemTwin() || card.isMultiwalletAllowed) return
|
||||
if (scanResponse.isTangemTwins() || card.isMultiwalletAllowed) return
|
||||
|
||||
val validator = store.state.walletState.walletManagers.firstOrNull()
|
||||
as? SignatureCountValidator
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
package com.tangem.tap.features.wallet.redux.reducers
|
||||
|
||||
import com.tangem.tap.features.wallet.redux.TwinCardsState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
|
||||
class TwinsReducer {
|
||||
fun reduce(action: WalletAction.TwinsAction, state: WalletState): WalletState {
|
||||
return when (action) {
|
||||
is WalletAction.TwinsAction.SetTwinCard -> {
|
||||
state.copy(
|
||||
twinCardsState = TwinCardsState(
|
||||
cardNumber = action.number,
|
||||
showTwinOnboarding = state.twinCardsState?.showTwinOnboarding
|
||||
?: false,
|
||||
isCreatingTwinCardsAllowed = action.isCreatingTwinCardsAllowed
|
||||
)
|
||||
)
|
||||
}
|
||||
is WalletAction.TwinsAction.ShowOnboarding -> {
|
||||
state.copy(
|
||||
twinCardsState = state.twinCardsState?.copy(showTwinOnboarding = true)
|
||||
?: TwinCardsState(cardNumber = null,
|
||||
showTwinOnboarding = true,
|
||||
isCreatingTwinCardsAllowed = false)
|
||||
)
|
||||
}
|
||||
is WalletAction.TwinsAction.SetOnboardingShown -> {
|
||||
state.copy(
|
||||
twinCardsState = state.twinCardsState?.copy(showTwinOnboarding = false)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,6 @@ class WalletReducer {
|
|||
private fun internalReduce(action: Action, state: AppState): WalletState {
|
||||
|
||||
val multiWalletReducer = MultiWalletReducer()
|
||||
val twinsReducer = TwinsReducer()
|
||||
val onWalletLoadedReducer = OnWalletLoadedReducer()
|
||||
|
||||
if (action !is WalletAction) return state.walletState
|
||||
|
|
@ -34,21 +33,17 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
|
||||
when (action) {
|
||||
is WalletAction.Warnings -> newState = handleCheckSignedHashesActions(action, newState)
|
||||
is WalletAction.TwinsAction -> newState = twinsReducer.reduce(action, newState)
|
||||
is WalletAction.MultiWallet -> newState = multiWalletReducer.reduce(action, newState)
|
||||
|
||||
is WalletAction.ResetState -> newState = WalletState()
|
||||
is WalletAction.SetIfTestnetCard -> newState = newState.copy(isTestnet = action.isTestnet)
|
||||
is WalletAction.EmptyWallet -> {
|
||||
val creatingWalletAllowed = !(newState.twinCardsState != null &&
|
||||
newState.twinCardsState?.isCreatingTwinCardsAllowed != true)
|
||||
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Done,
|
||||
wallets = listOf(
|
||||
WalletData(
|
||||
currencyData = BalanceWidgetData(BalanceStatus.EmptyCard),
|
||||
mainButton = WalletMainButton.CreateWalletButton(creatingWalletAllowed),
|
||||
mainButton = WalletMainButton.CreateWalletButton(true),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
@ -215,7 +210,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
newState = setNewFiatRate(action.fiatRate, state.globalState.appCurrency, newState)
|
||||
is WalletAction.LoadArtwork -> {
|
||||
val artworkUrl = action.card.getArtworkUrl(action.artworkId)
|
||||
?: when (newState.twinCardsState?.cardNumber) {
|
||||
?: when (state.twinCardsState.cardNumber) {
|
||||
TwinCardNumber.First -> Artwork.TWIN_CARD_1
|
||||
TwinCardNumber.Second -> Artwork.TWIN_CARD_2
|
||||
else -> Artwork.DEFAULT_IMG_URL
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
package com.tangem.tap.features.wallet.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.fragment_twin_cards.*
|
||||
import kotlinx.android.synthetic.main.layout_twin_cards.*
|
||||
|
||||
class OnboardingTwinsFragment : Fragment(R.layout.fragment_twin_cards) {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
})
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
enterTransition = inflater.inflateTransition(R.transition.slide_right)
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
store.dispatch(WalletAction.TwinsAction.SetOnboardingShown)
|
||||
val secondTwinNumber =
|
||||
store.state.walletState.twinCardsState?.cardNumber?.pairNumber()?.number ?: ""
|
||||
val text = getString(R.string.twins_onboarding_description_format, secondTwinNumber)
|
||||
tv_twin_cards_description_1.text = text
|
||||
|
||||
setOnClickListeners()
|
||||
|
||||
Picasso.get()
|
||||
.load(Artwork.TWIN_CARD_1)
|
||||
.placeholder(R.drawable.card_placeholder)
|
||||
?.error(R.drawable.card_placeholder)
|
||||
?.into(iv_twin_card_1)
|
||||
|
||||
Picasso.get()
|
||||
.load(Artwork.TWIN_CARD_2)
|
||||
.placeholder(R.drawable.card_placeholder)
|
||||
?.error(R.drawable.card_placeholder)
|
||||
?.into(iv_twin_card_2)
|
||||
}
|
||||
|
||||
private fun setOnClickListeners() {
|
||||
btn_continue.setOnClickListener {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -20,9 +20,7 @@ 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_details_twin_cards.*
|
||||
import kotlinx.android.synthetic.main.fragment_wallet_details.*
|
||||
import kotlinx.android.synthetic.main.fragment_wallet_details.toolbar
|
||||
import kotlinx.android.synthetic.main.item_currency_wallet.view.*
|
||||
import kotlinx.android.synthetic.main.item_popular_token.view.*
|
||||
import kotlinx.android.synthetic.main.layout_balance_error.*
|
||||
|
|
@ -98,12 +96,12 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
|
|||
|
||||
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()
|
||||
if (currency is Currency.Token) {
|
||||
tv_currency_subtitle.text = currency.blockchain.tokenDisplayName()
|
||||
tv_currency_subtitle.show()
|
||||
} else {
|
||||
tv_currency_subtitle.hide()
|
||||
}
|
||||
tv_currency_subtitle.hide()
|
||||
}
|
||||
|
||||
|
||||
showPendingTransactionsIfPresent(selectedWallet.pendingTransactions)
|
||||
|
|
@ -129,9 +127,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
|
|||
srl_wallet_details.setOnRefreshListener {
|
||||
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
|
||||
store.dispatch(WalletAction.LoadWallet(
|
||||
allowToBuy = selectedWallet.tradeCryptoState.buyingAllowed,
|
||||
allowToSell = selectedWallet.tradeCryptoState.sellingAllowed,
|
||||
blockchain = selectedWallet.currency?.blockchain
|
||||
allowToBuy = selectedWallet.tradeCryptoState.buyingAllowed,
|
||||
allowToSell = selectedWallet.tradeCryptoState.sellingAllowed,
|
||||
blockchain = selectedWallet.currency?.blockchain
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -180,8 +178,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
|
|||
tv_address.text = state.walletAddresses.selectedAddress.address
|
||||
tv_explore?.setOnClickListener {
|
||||
store.dispatch(WalletAction.ExploreAddress(
|
||||
state.walletAddresses.selectedAddress.exploreUrl,
|
||||
requireContext()))
|
||||
state.walletAddresses.selectedAddress.exploreUrl,
|
||||
requireContext()))
|
||||
}
|
||||
iv_qr_code.setImageBitmap(state.walletAddresses.selectedAddress.shareUrl.toQrCode())
|
||||
}
|
||||
|
|
@ -192,8 +190,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
|
|||
if (state.error == ErrorType.NoInternetConnection) {
|
||||
srl_wallet_details?.isRefreshing = false
|
||||
(activity as? SnackbarHandler)?.showSnackbar(
|
||||
text = R.string.wallet_notification_no_internet,
|
||||
buttonTitle = R.string.common_retry
|
||||
text = R.string.wallet_notification_no_internet,
|
||||
buttonTitle = R.string.common_retry
|
||||
) { store.dispatch(WalletAction.LoadData) }
|
||||
}
|
||||
} else {
|
||||
|
|
@ -249,8 +247,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
|
|||
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
|
||||
R.string.wallet_error_no_account_subtitle_format,
|
||||
data.amountToCreateAccount, data.currencySymbol
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,8 +138,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
if (state.error == ErrorType.NoInternetConnection) {
|
||||
srl_wallet_details?.isRefreshing = false
|
||||
(activity as? MainActivity)?.showSnackbar(
|
||||
text = R.string.wallet_notification_no_internet,
|
||||
buttonTitle = R.string.common_retry
|
||||
text = R.string.wallet_notification_no_internet,
|
||||
buttonTitle = R.string.common_retry
|
||||
) { store.dispatch(WalletAction.LoadData) }
|
||||
}
|
||||
} else {
|
||||
|
|
@ -150,8 +150,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
private fun setupCardImage(cardImage: Artwork?) {
|
||||
Picasso.get()
|
||||
.load(cardImage?.artworkId)
|
||||
.placeholder(R.drawable.card_placeholder)
|
||||
?.error(R.drawable.card_placeholder)
|
||||
.placeholder(R.drawable.card_placeholder_black)
|
||||
?.error(R.drawable.card_placeholder_black)
|
||||
?.into(iv_card)
|
||||
}
|
||||
|
||||
|
|
@ -161,11 +161,10 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
store.dispatch(GlobalAction.UpdateFeedbackInfo(store.state.walletState.walletManagers))
|
||||
store.state.globalState.scanResponse?.let { scanNoteResponse ->
|
||||
store.dispatch(DetailsAction.PrepareScreen(
|
||||
scanNoteResponse.card, scanNoteResponse,
|
||||
store.state.walletState.walletManagers.map { it.wallet },
|
||||
store.state.globalState.configManager?.config?.isCreatingTwinCardsAllowed,
|
||||
CardTou(),
|
||||
store.state.globalState.appCurrency
|
||||
scanNoteResponse,
|
||||
store.state.walletState.walletManagers.map { it.wallet },
|
||||
CardTou(),
|
||||
store.state.globalState.appCurrency
|
||||
))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Details))
|
||||
true
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ import android.widget.Button
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.twins.TwinCardNumber
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidget
|
||||
|
|
@ -71,7 +70,7 @@ class SingleWalletView : WalletView {
|
|||
state.primaryWallet ?: return
|
||||
|
||||
setupTwinCards(state.twinCardsState, fragment)
|
||||
setupButtons(state.primaryWallet, state.twinCardsState != null, fragment)
|
||||
setupButtons(state.primaryWallet, state.isTangemTwins, fragment)
|
||||
setupAddressCard(state.primaryWallet, fragment)
|
||||
showPendingTransactionsIfPresent(state.primaryWallet.pendingTransactions)
|
||||
setupBalance(state, state.primaryWallet)
|
||||
|
|
@ -87,12 +86,12 @@ class SingleWalletView : WalletView {
|
|||
private fun setupBalance(state: WalletState, primaryWallet: WalletData) {
|
||||
fragment?.apply {
|
||||
this.l_balance.show()
|
||||
BalanceWidget(this, primaryWallet.currencyData, state.twinCardsState != null).setup()
|
||||
BalanceWidget(this, primaryWallet.currencyData, state.isTangemTwins).setup()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTwinCards(
|
||||
twinCardsState: TwinCardsState?, fragment: WalletFragment
|
||||
twinCardsState: TwinCardsState?, fragment: WalletFragment
|
||||
) = with(fragment) {
|
||||
twinCardsState?.cardNumber?.let { cardNumber ->
|
||||
tv_twin_card_number.show()
|
||||
|
|
@ -106,11 +105,6 @@ class SingleWalletView : WalletView {
|
|||
if (twinCardsState?.cardNumber == null) {
|
||||
tv_twin_card_number.hide()
|
||||
}
|
||||
if (twinCardsState?.showTwinOnboarding == true) {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun setupButtons(
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
|
||||
<corners android:radius="12dp" />
|
||||
<size
|
||||
android:width="328dp"
|
||||
android:height="174dp" />
|
||||
<gradient
|
||||
android:endColor="#FFFFFF"
|
||||
android:startColor="#FFFFFF" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:drawable="@drawable/ic_tangem_pale"
|
||||
android:gravity="center">
|
||||
<size
|
||||
android:width="85dp"
|
||||
android:height="23dp" />
|
||||
</item>
|
||||
</layer-list>
|
||||
126
app/src/main/res/drawable/card_placeholder_black.xml
Normal file
126
app/src/main/res/drawable/card_placeholder_black.xml
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="249dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="249"
|
||||
android:viewportHeight="150">
|
||||
<path
|
||||
android:fillAlpha="0.03"
|
||||
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z" />
|
||||
<path
|
||||
android:fillAlpha="0.5"
|
||||
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:centerX="7.404067"
|
||||
android:centerY="8.65384"
|
||||
android:gradientRadius="267.36932"
|
||||
android:type="radial">
|
||||
<item
|
||||
android:color="#FFFFFFFF"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#19FFFFFF"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#1B1D1C"
|
||||
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M10,0.5L238,0.5A9.499,9.501 134.998,0 1,247.501 10L247.517,140A9.499,9.501 134.998,0 1,238.019 149.5L10.019,149.5A9.499,9.501 134.998,0 1,0.517 140L0.501,10A9.499,9.501 134.998,0 1,10 0.5z"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:strokeColor">
|
||||
<gradient
|
||||
android:endX="246.96196"
|
||||
android:endY="144.774"
|
||||
android:startX="2.844399"
|
||||
android:startY="3.38345"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FF1A1A1A"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FF565656"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M28.359,121.942L26.389,120.734L26.472,123H25.41L25.498,120.734L23.505,121.95L23.002,121.003L25.044,119.992L23.009,118.974L23.511,118.058L25.497,119.25L25.415,117H26.468L26.389,119.258L28.365,118.058L28.884,118.974L26.826,120L28.885,121.011L28.359,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M38.855,121.942L36.885,120.734L36.967,123H35.906L35.994,120.734L34.001,121.95L33.498,121.003L35.54,119.992L33.505,118.974L34.007,118.058L35.993,119.25L35.911,117H36.964L36.884,119.258L38.861,118.058L39.38,118.974L37.322,120L39.381,121.011L38.855,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M49.351,121.942L47.381,120.734L47.463,123H46.402L46.49,120.734L44.497,121.95L43.994,121.003L46.035,119.992L44.001,118.974L44.503,118.058L46.489,119.25L46.407,117H47.46L47.38,119.258L49.357,118.058L49.876,118.974L47.818,120L49.877,121.011L49.351,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M59.847,121.942L57.877,120.734L57.959,123H56.897L56.985,120.734L54.993,121.95L54.49,121.003L56.531,119.992L54.497,118.974L54.999,118.058L56.985,119.25L56.902,117H57.956L57.876,119.258L59.853,118.058L60.372,118.974L58.314,120L60.373,121.011L59.847,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M76.235,121.942L74.266,120.734L74.348,123H73.286L73.374,120.734L71.381,121.95L70.879,121.003L72.92,119.992L70.885,118.974L71.387,118.058L73.373,119.25L73.291,117H74.345L74.265,119.258L76.242,118.058L76.761,118.974L74.703,120L76.762,121.011L76.235,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M86.731,121.942L84.761,120.734L84.844,123H83.782L83.87,120.734L81.877,121.95L81.374,121.003L83.416,119.992L81.381,118.974L81.883,118.058L83.869,119.25L83.787,117H84.841L84.761,119.258L86.737,118.058L87.257,118.974L85.199,120L87.258,121.011L86.731,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M97.227,121.942L95.257,120.734L95.34,123H94.278L94.366,120.734L92.373,121.95L91.87,121.003L93.912,119.992L91.877,118.974L92.379,118.058L94.365,119.25L94.283,117H95.337L95.257,119.258L97.233,118.058L97.752,118.974L95.695,120L97.753,121.011L97.227,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M107.723,121.942L105.753,120.734L105.835,123H104.774L104.862,120.734L102.869,121.95L102.366,121.003L104.408,119.992L102.373,118.974L102.875,118.058L104.861,119.25L104.779,117H105.832L105.752,119.258L107.729,118.058L108.248,118.974L106.19,120L108.249,121.011L107.723,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M124.112,121.942L122.142,120.734L122.224,123H121.163L121.251,120.734L119.258,121.95L118.755,121.003L120.796,119.992L118.762,118.974L119.264,118.058L121.25,119.25L121.168,117H122.221L122.141,119.258L124.118,118.058L124.637,118.974L122.579,120L124.638,121.011L124.112,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M134.608,121.942L132.638,120.734L132.72,123H131.659L131.746,120.734L129.754,121.95L129.251,121.003L131.292,119.992L129.258,118.974L129.76,118.058L131.746,119.25L131.664,117H132.717L132.637,119.258L134.614,118.058L135.133,118.974L133.075,120L135.134,121.011L134.608,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M145.104,121.942L143.134,120.734L143.216,123H142.154L142.242,120.734L140.249,121.95L139.747,121.003L141.788,119.992L139.754,118.974L140.256,118.058L142.242,119.25L142.159,117H143.213L143.133,119.258L145.11,118.058L145.629,118.974L143.571,120L145.63,121.011L145.104,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M155.599,121.942L153.63,120.734L153.712,123H152.65L152.738,120.734L150.745,121.95L150.242,121.003L152.284,119.992L150.249,118.974L150.751,118.058L152.737,119.25L152.655,117H153.709L153.629,119.258L155.606,118.058L156.125,118.974L154.067,120L156.126,121.011L155.599,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M171.988,121.942L170.018,120.734L170.101,123H169.039L169.127,120.734L167.134,121.95L166.631,121.003L168.673,119.992L166.638,118.974L167.14,118.058L169.126,119.25L169.044,117H170.098L170.018,119.258L171.994,118.058L172.513,118.974L170.456,120L172.515,121.011L171.988,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M182.484,121.942L180.514,120.734L180.597,123H179.535L179.623,120.734L177.63,121.95L177.127,121.003L179.169,119.992L177.134,118.974L177.636,118.058L179.622,119.25L179.54,117H180.593L180.514,119.258L182.49,118.058L183.009,118.974L180.952,120L183.01,121.011L182.484,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M192.98,121.942L191.01,120.734L191.092,123H190.031L190.119,120.734L188.126,121.95L187.623,121.003L189.665,119.992L187.63,118.974L188.132,118.058L190.118,119.25L190.036,117H191.089L191.009,119.258L192.986,118.058L193.505,118.974L191.447,120L193.506,121.011L192.98,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#A5A5A5"
|
||||
android:pathData="M203.476,121.942L201.506,120.734L201.588,123H200.527L200.615,120.734L198.622,121.95L198.119,121.003L200.16,119.992L198.126,118.974L198.628,118.058L200.614,119.25L200.532,117H201.585L201.505,119.258L203.482,118.058L204.001,118.974L201.943,120L204.002,121.011L203.476,121.942Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M26.483,30.4L26.483,36.64L29.382,36.64C29.494,36.639 29.602,36.596 29.681,36.522C29.761,36.447 29.806,36.346 29.807,36.241L29.807,30.4L26.483,30.4Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M20,36.241C20.001,36.346 20.046,36.447 20.126,36.522C20.205,36.596 20.313,36.639 20.425,36.64L23.324,36.64L23.324,30.4L20,30.4L20,36.241Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M29.807,27.2L29.807,24.409C29.807,24.301 29.763,24.197 29.685,24.12C29.606,24.043 29.5,24 29.389,24L20.418,24C20.307,24 20.201,24.043 20.123,24.12C20.044,24.197 20,24.301 20,24.409L20,27.2L29.807,27.2Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M45.32,28.163C44.719,27.921 44.068,27.812 43.416,27.846C42.555,27.844 41.696,27.934 40.856,28.113L40.856,29.532C41.526,29.455 42.199,29.412 42.874,29.4C43.236,29.385 43.598,29.418 43.949,29.5C44.062,29.532 44.164,29.59 44.247,29.669C44.33,29.748 44.392,29.846 44.425,29.953C44.531,30.334 44.576,30.728 44.558,31.122L44.558,31.539C43.764,31.498 43.131,31.476 42.698,31.476C42.236,31.454 41.775,31.539 41.356,31.725C41.025,31.903 40.779,32.195 40.666,32.54C40.509,33.044 40.438,33.569 40.456,34.095C40.398,34.771 40.564,35.448 40.932,36.029C41.127,36.243 41.372,36.409 41.648,36.513C41.924,36.618 42.222,36.659 42.517,36.632C42.864,36.632 43.945,36.605 44.421,36.007C44.499,35.912 44.566,35.808 44.62,35.699L44.673,36.496L46.424,36.496L46.424,31.222C46.45,30.539 46.371,29.857 46.191,29.196C46.127,28.977 46.017,28.772 45.867,28.594C45.717,28.417 45.531,28.27 45.32,28.163ZM44.54,33.256C44.589,33.736 44.503,34.219 44.292,34.656C44.189,34.783 44.048,34.876 43.887,34.924C43.706,34.972 43.519,34.995 43.331,34.992C43.037,35.028 42.74,34.953 42.503,34.783C42.343,34.523 42.277,34.221 42.312,33.922C42.302,33.681 42.327,33.44 42.388,33.206C42.407,33.139 42.44,33.076 42.486,33.021C42.532,32.966 42.59,32.921 42.655,32.889C42.834,32.821 43.028,32.791 43.221,32.803L44.54,32.803L44.54,33.256Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M60.474,28.645C60.438,28.584 60.398,28.526 60.354,28.471C59.777,27.736 58.401,27.846 58.401,27.846C57.946,27.832 57.499,27.964 57.126,28.222C56.75,28.573 56.507,29.042 56.438,29.55C56.168,31.328 56.168,33.136 56.438,34.915C56.504,35.417 56.74,35.882 57.108,36.233C57.509,36.52 58,36.659 58.493,36.623C58.493,36.623 58.521,36.623 58.539,36.623C58.539,36.623 59.782,36.72 60.386,36.104C60.398,36.607 60.364,37.11 60.285,37.606C60.264,37.738 60.212,37.864 60.131,37.971C60.051,38.078 59.945,38.164 59.823,38.222C59.439,38.351 59.033,38.406 58.627,38.382L56.72,38.35L56.72,39.788C57.071,39.853 57.426,39.9 57.782,39.93C58.221,39.972 58.636,39.99 59.034,39.99C59.773,40.044 60.512,39.884 61.162,39.53C61.39,39.374 61.583,39.174 61.732,38.943C61.88,38.71 61.98,38.451 62.026,38.18C62.174,37.297 62.237,36.402 62.215,35.507L62.215,27.979L60.516,27.979L60.474,28.645ZM60.262,34.221C60.233,34.452 60.118,34.664 59.938,34.813C59.729,34.939 59.486,34.999 59.241,34.984C58.968,35.006 58.694,34.95 58.452,34.823C58.263,34.636 58.153,34.384 58.147,34.12C58.042,32.876 58.042,31.625 58.147,30.381C58.153,30.125 58.259,29.882 58.442,29.701C58.69,29.579 58.966,29.526 59.241,29.55C59.502,29.529 59.761,29.599 59.975,29.747C60.166,29.947 60.28,30.207 60.299,30.482C60.369,31.072 60.396,31.666 60.382,32.26C60.405,32.916 60.364,33.573 60.262,34.221Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M69.304,28.124C68.702,27.787 68.01,27.636 67.319,27.691C66.601,27.642 65.884,27.802 65.259,28.152C64.804,28.473 64.497,28.959 64.408,29.502C64.259,30.388 64.196,31.286 64.219,32.184C64.195,33.083 64.261,33.983 64.417,34.87C64.465,35.139 64.568,35.395 64.72,35.623C64.872,35.851 65.07,36.046 65.301,36.197C65.961,36.531 66.701,36.681 67.441,36.631C67.913,36.63 68.384,36.603 68.852,36.552C69.241,36.517 69.626,36.453 70.005,36.359L70.005,34.916C69.139,35.004 68.429,35.05 67.874,35.05C67.464,35.074 67.053,35.025 66.66,34.907C66.54,34.857 66.433,34.778 66.351,34.677C66.269,34.577 66.214,34.458 66.19,34.331C66.106,33.822 66.071,33.307 66.087,32.792L70.193,32.792L70.193,32.165C70.215,31.241 70.167,30.317 70.047,29.4C69.98,28.896 69.713,28.438 69.304,28.124ZM68.41,31.428L66.087,31.428C66.086,30.95 66.119,30.473 66.186,30C66.195,29.887 66.228,29.777 66.283,29.678C66.338,29.579 66.414,29.492 66.505,29.424C66.988,29.233 67.527,29.233 68.01,29.424C68.1,29.494 68.173,29.583 68.227,29.682C68.28,29.782 68.312,29.892 68.321,30.004C68.394,30.475 68.424,30.952 68.41,31.428Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M81.818,29.12C81.739,28.745 81.527,28.406 81.218,28.161C80.849,27.928 80.409,27.817 79.966,27.844C79.966,27.844 79.924,27.844 79.895,27.844C79.895,27.844 78.467,27.767 77.877,28.464C77.813,28.542 77.755,28.625 77.705,28.713C77.61,28.478 77.445,28.275 77.229,28.129C76.858,27.924 76.431,27.826 76.001,27.848C76.001,27.848 74.573,27.767 73.983,28.468C73.917,28.545 73.86,28.629 73.812,28.717L73.764,27.984L72.027,27.984L72.027,36.48L73.931,36.48L73.931,32.051C73.921,31.52 73.942,30.988 73.993,30.459C74.005,30.198 74.107,29.949 74.283,29.749C74.493,29.584 74.764,29.506 75.035,29.531C75.276,29.513 75.518,29.557 75.735,29.658C75.815,29.731 75.879,29.819 75.923,29.916C75.967,30.013 75.991,30.118 75.992,30.224C76.042,30.747 76.063,31.272 76.054,31.798L76.054,36.48L77.958,36.48L77.958,32.051C77.958,31.314 77.958,30.785 78.015,30.459C78.028,30.197 78.133,29.946 78.315,29.749C78.53,29.586 78.802,29.508 79.076,29.531C79.319,29.513 79.562,29.557 79.781,29.658C79.86,29.732 79.923,29.82 79.966,29.917C80.01,30.014 80.033,30.118 80.033,30.224C80.086,30.747 80.106,31.272 80.095,31.798L80.095,36.48L81.999,36.48L81.999,30.884C82.009,30.291 81.949,29.699 81.818,29.12Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M53.781,28.171C53.383,27.928 52.913,27.814 52.44,27.845C52.44,27.845 52.402,27.845 52.382,27.845C52.382,27.845 50.936,27.773 50.343,28.465C50.301,28.514 50.262,28.565 50.227,28.619L50.184,27.981L48.424,27.981L48.424,36.48L50.353,36.48L50.353,32.049C50.342,31.521 50.363,30.992 50.415,30.465C50.429,30.203 50.538,29.952 50.724,29.755C50.957,29.584 51.25,29.503 51.543,29.529C51.8,29.511 52.057,29.555 52.291,29.655C52.378,29.728 52.448,29.816 52.497,29.915C52.546,30.014 52.572,30.121 52.575,30.23C52.63,30.75 52.653,31.273 52.643,31.796L52.643,36.48L54.571,36.48L54.571,30.963C54.587,30.356 54.533,29.75 54.412,29.153C54.333,28.765 54.109,28.417 53.781,28.171Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M38.128,25.44L36.175,25.44L36.175,27.877L34.96,27.877L34.96,29.434L36.175,29.434L36.175,36.48L38.128,36.48L38.128,29.434L39.614,29.434L39.614,27.877L38.128,27.877L38.128,25.44Z" />
|
||||
</vector>
|
||||
108
app/src/main/res/drawable/card_placeholder_white.xml
Normal file
108
app/src/main/res/drawable/card_placeholder_white.xml
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="214dp"
|
||||
android:height="130dp"
|
||||
android:viewportWidth="214"
|
||||
android:viewportHeight="130">
|
||||
<path
|
||||
android:fillColor="#E4E4E4"
|
||||
android:pathData="M1.043,8.273C1.019,4.256 4.265,1 8.294,1H205.706C209.735,1 213.019,4.256 213.043,8.273L213.709,121.725C213.733,125.742 210.487,128.998 206.459,128.998H9.046C5.018,128.998 1.733,125.742 1.709,121.725L1.043,8.273Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M1.043,8.273C1.019,4.256 4.265,1 8.294,1H205.706C209.735,1 213.019,4.256 213.043,8.273L213.709,121.725C213.733,125.742 210.487,128.998 206.459,128.998H9.046C5.018,128.998 1.733,125.742 1.709,121.725L1.043,8.273Z"
|
||||
android:strokeWidth="0.5">
|
||||
<aapt:attr name="android:strokeColor">
|
||||
<gradient
|
||||
android:endX="216.304"
|
||||
android:endY="122.149"
|
||||
android:startX="-1.98014"
|
||||
android:startY="-1.3567"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFDCDCDC"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FFDEDEDE"
|
||||
android:offset="0.9999" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M25.274,26.4L25.274,32.64L28.079,32.64C28.188,32.639 28.292,32.596 28.369,32.522C28.446,32.447 28.49,32.346 28.491,32.241L28.491,26.4L25.274,26.4Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M19,32.241C19.001,32.346 19.045,32.447 19.122,32.522C19.199,32.596 19.303,32.639 19.412,32.64L22.217,32.64L22.217,26.4L19,26.4L19,32.241Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M28.491,23.2L28.491,20.409C28.491,20.301 28.448,20.197 28.372,20.12C28.296,20.043 28.194,20 28.086,20L19.405,20C19.297,20 19.195,20.043 19.119,20.12C19.043,20.197 19,20.301 19,20.409L19,23.2L28.491,23.2Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M43.503,24.163C42.922,23.921 42.292,23.812 41.661,23.846C40.827,23.844 39.996,23.934 39.183,24.113L39.183,25.532C39.832,25.455 40.483,25.412 41.136,25.4C41.486,25.385 41.836,25.418 42.177,25.5C42.286,25.532 42.385,25.59 42.465,25.669C42.546,25.748 42.605,25.846 42.637,25.953C42.74,26.334 42.783,26.728 42.766,27.122L42.766,27.539C41.997,27.498 41.385,27.476 40.965,27.476C40.519,27.454 40.073,27.539 39.667,27.725C39.347,27.903 39.109,28.195 38.999,28.54C38.847,29.044 38.779,29.569 38.796,30.094C38.74,30.771 38.901,31.448 39.257,32.029C39.445,32.243 39.683,32.409 39.95,32.513C40.216,32.618 40.505,32.659 40.791,32.632C41.127,32.632 42.172,32.605 42.633,32.007C42.709,31.912 42.774,31.808 42.826,31.698L42.877,32.496L44.572,32.496L44.572,27.222C44.596,26.539 44.52,25.857 44.346,25.196C44.284,24.977 44.178,24.772 44.033,24.594C43.888,24.417 43.708,24.27 43.503,24.163ZM42.748,29.256C42.796,29.736 42.713,30.219 42.508,30.656C42.409,30.782 42.272,30.876 42.117,30.924C41.942,30.972 41.76,30.995 41.578,30.992C41.294,31.028 41.006,30.953 40.777,30.783C40.623,30.523 40.558,30.221 40.592,29.922C40.582,29.681 40.607,29.44 40.666,29.206C40.684,29.139 40.716,29.075 40.761,29.021C40.805,28.966 40.861,28.921 40.924,28.889C41.098,28.821 41.285,28.791 41.472,28.803L42.748,28.803L42.748,29.256Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M58.169,24.645C58.133,24.584 58.095,24.526 58.053,24.471C57.494,23.736 56.162,23.846 56.162,23.846C55.722,23.831 55.29,23.964 54.929,24.222C54.564,24.573 54.329,25.042 54.263,25.55C54.001,27.328 54.001,29.136 54.263,30.914C54.326,31.417 54.555,31.882 54.911,32.233C55.299,32.52 55.774,32.659 56.251,32.623C56.251,32.623 56.278,32.623 56.296,32.623C56.296,32.623 57.498,32.72 58.084,32.104C58.095,32.607 58.062,33.11 57.985,33.606C57.966,33.738 57.915,33.864 57.837,33.971C57.759,34.078 57.656,34.164 57.539,34.222C57.166,34.351 56.773,34.406 56.381,34.382L54.535,34.35L54.535,35.788C54.876,35.853 55.219,35.9 55.563,35.93C55.988,35.972 56.39,35.99 56.774,35.99C57.49,36.044 58.205,35.884 58.834,35.53C59.055,35.374 59.242,35.174 59.386,34.943C59.529,34.71 59.626,34.451 59.67,34.18C59.813,33.297 59.875,32.402 59.853,31.507L59.853,23.979L58.209,23.979L58.169,24.645ZM57.963,30.221C57.936,30.452 57.824,30.664 57.65,30.813C57.447,30.94 57.212,30.999 56.975,30.983C56.711,31.006 56.446,30.95 56.211,30.823C56.028,30.636 55.923,30.385 55.916,30.12C55.815,28.876 55.815,27.625 55.916,26.381C55.923,26.125 56.025,25.882 56.202,25.701C56.442,25.579 56.709,25.526 56.975,25.55C57.227,25.529 57.479,25.599 57.686,25.747C57.87,25.947 57.981,26.207 57.999,26.482C58.066,27.072 58.093,27.666 58.079,28.26C58.101,28.916 58.062,29.573 57.963,30.221Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M66.713,24.124C66.131,23.787 65.461,23.636 64.793,23.691C64.098,23.642 63.404,23.802 62.799,24.152C62.358,24.473 62.062,24.959 61.975,25.502C61.831,26.388 61.77,27.286 61.793,28.184C61.769,29.083 61.833,29.983 61.984,30.87C62.031,31.139 62.131,31.395 62.278,31.623C62.425,31.851 62.616,32.046 62.84,32.197C63.478,32.531 64.194,32.681 64.911,32.631C65.367,32.63 65.823,32.603 66.276,32.552C66.653,32.517 67.025,32.453 67.392,32.359L67.392,30.916C66.554,31.004 65.867,31.05 65.33,31.05C64.933,31.074 64.535,31.025 64.155,30.907C64.038,30.857 63.936,30.778 63.856,30.677C63.777,30.577 63.723,30.458 63.7,30.331C63.618,29.823 63.585,29.307 63.6,28.792L67.574,28.792L67.574,28.165C67.596,27.241 67.548,26.317 67.433,25.4C67.368,24.896 67.109,24.438 66.713,24.124ZM65.849,27.428L63.6,27.428C63.6,26.95 63.632,26.473 63.696,26C63.704,25.887 63.736,25.777 63.79,25.678C63.843,25.579 63.917,25.492 64.005,25.424C64.473,25.233 64.994,25.233 65.462,25.424C65.548,25.494 65.619,25.583 65.671,25.683C65.723,25.782 65.754,25.892 65.762,26.004C65.833,26.475 65.862,26.952 65.849,27.428Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M78.824,25.12C78.747,24.744 78.542,24.406 78.243,24.161C77.886,23.928 77.46,23.817 77.032,23.844C77.032,23.844 76.991,23.844 76.963,23.844C76.963,23.844 75.581,23.767 75.01,24.464C74.948,24.542 74.892,24.625 74.844,24.712C74.752,24.478 74.592,24.275 74.383,24.129C74.024,23.923 73.61,23.826 73.195,23.848C73.195,23.848 71.813,23.767 71.242,24.468C71.178,24.545 71.122,24.629 71.076,24.717L71.03,23.984L69.349,23.984L69.349,32.48L71.191,32.48L71.191,28.051C71.182,27.52 71.201,26.988 71.251,26.459C71.263,26.198 71.362,25.949 71.532,25.749C71.736,25.584 71.997,25.506 72.26,25.531C72.493,25.513 72.727,25.557 72.937,25.658C73.015,25.731 73.076,25.819 73.119,25.916C73.162,26.013 73.185,26.118 73.186,26.223C73.234,26.747 73.254,27.272 73.245,27.798L73.245,32.48L75.088,32.48L75.088,28.051C75.088,27.314 75.088,26.785 75.143,26.459C75.156,26.197 75.258,25.946 75.434,25.749C75.642,25.586 75.906,25.508 76.171,25.531C76.405,25.513 76.641,25.557 76.852,25.658C76.929,25.732 76.99,25.82 77.032,25.917C77.074,26.014 77.096,26.118 77.096,26.223C77.147,26.747 77.167,27.272 77.156,27.798L77.156,32.48L78.999,32.48L78.999,26.884C79.009,26.291 78.951,25.699 78.824,25.12Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M51.691,24.171C51.307,23.928 50.851,23.814 50.394,23.845C50.394,23.845 50.356,23.845 50.338,23.845C50.338,23.845 48.938,23.773 48.364,24.465C48.323,24.514 48.286,24.565 48.252,24.619L48.21,23.981L46.507,23.981L46.507,32.48L48.373,32.48L48.373,28.049C48.363,27.521 48.384,26.992 48.434,26.465C48.448,26.203 48.553,25.952 48.733,25.755C48.958,25.584 49.242,25.503 49.526,25.528C49.774,25.511 50.023,25.555 50.249,25.655C50.333,25.728 50.401,25.816 50.448,25.915C50.496,26.014 50.521,26.121 50.524,26.23C50.578,26.75 50.6,27.273 50.59,27.796L50.59,32.48L52.456,32.48L52.456,26.963C52.471,26.356 52.419,25.749 52.302,25.153C52.226,24.765 52.009,24.417 51.691,24.171Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M36.543,21.44L34.653,21.44L34.653,23.877L33.477,23.877L33.477,25.434L34.653,25.434L34.653,32.48L36.543,32.48L36.543,25.434L37.981,25.434L37.981,23.877L36.543,23.877L36.543,21.44Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M23.463,105.118L21.826,104.112L21.904,106H21.024L21.087,104.112L19.441,105.125L19.02,104.335L20.707,103.493L19.016,102.645L19.428,101.882L21.08,102.875L21.001,101H21.874L21.818,102.882L23.451,101.882L23.885,102.645L22.184,103.5L23.895,104.342L23.463,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M32.161,105.118L30.524,104.112L30.602,106H29.722L29.785,104.112L28.139,105.125L27.718,104.335L29.405,103.493L27.715,102.645L28.126,101.882L29.778,102.875L29.699,101H30.573L30.517,102.882L32.149,101.882L32.583,102.645L30.883,103.5L32.593,104.342L32.161,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M40.86,105.118L39.222,104.112L39.3,106H38.42L38.483,104.112L36.837,105.125L36.416,104.335L38.104,103.493L36.413,102.645L36.825,101.882L38.476,102.875L38.398,101H39.271L39.215,102.882L40.847,101.882L41.282,102.645L39.581,103.5L41.292,104.342L40.86,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M49.558,105.118L47.92,104.112L47.998,106H47.119L47.181,104.112L45.535,105.125L45.114,104.335L46.802,103.493L45.111,102.645L45.523,101.882L47.174,102.875L47.096,101H47.969L47.913,102.882L49.546,101.882L49.98,102.645L48.279,103.5L49.99,104.342L49.558,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M63.14,105.118L61.502,104.112L61.58,106H60.701L60.763,104.112L59.117,105.125L58.696,104.335L60.384,103.493L58.693,102.645L59.105,101.882L60.756,102.875L60.678,101H61.551L61.495,102.882L63.128,101.882L63.562,102.645L61.861,103.5L63.572,104.342L63.14,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M71.838,105.118L70.2,104.112L70.279,106H69.399L69.462,104.112L67.815,105.125L67.394,104.335L69.082,103.493L67.391,102.645L67.803,101.882L69.454,102.875L69.376,101H70.249L70.193,102.882L71.826,101.882L72.26,102.645L70.559,103.5L72.27,104.342L71.838,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M80.536,105.118L78.898,104.112L78.977,106H78.097L78.16,104.112L76.514,105.125L76.093,104.335L77.78,103.493L76.089,102.645L76.501,101.882L78.152,102.875L78.074,101H78.947L78.891,102.882L80.524,101.882L80.958,102.645L79.257,103.5L80.968,104.342L80.536,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M89.234,105.118L87.597,104.112L87.675,106H86.795L86.858,104.112L85.212,105.125L84.791,104.335L86.478,103.493L84.788,102.645L85.2,101.882L86.851,102.875L86.772,101H87.646L87.59,102.882L89.222,101.882L89.657,102.645L87.956,103.5L89.666,104.342L89.234,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M102.816,105.118L101.179,104.112L101.257,106H100.377L100.44,104.112L98.794,105.125L98.373,104.335L100.06,103.493L98.369,102.645L98.781,101.882L100.433,102.875L100.354,101H101.227L101.171,102.882L102.804,101.882L103.238,102.645L101.538,103.5L103.248,104.342L102.816,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M111.515,105.118L109.877,104.112L109.955,106H109.075L109.138,104.112L107.492,105.125L107.071,104.335L108.758,103.493L107.068,102.645L107.48,101.882L109.131,102.875L109.053,101H109.926L109.87,102.882L111.502,101.882L111.937,102.645L110.236,103.5L111.947,104.342L111.515,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M120.213,105.118L118.575,104.112L118.653,106H117.774L117.836,104.112L116.19,105.125L115.769,104.335L117.457,103.493L115.766,102.645L116.178,101.882L117.829,102.875L117.751,101H118.624L118.568,102.882L120.201,101.882L120.635,102.645L118.934,103.5L120.645,104.342L120.213,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M128.911,105.118L127.273,104.112L127.352,106H126.472L126.535,104.112L124.888,105.125L124.467,104.335L126.155,103.493L124.464,102.645L124.876,101.882L126.527,102.875L126.449,101H127.322L127.266,102.882L128.899,101.882L129.333,102.645L127.632,103.5L129.343,104.342L128.911,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M142.493,105.118L140.855,104.112L140.933,106H140.054L140.116,104.112L138.47,105.125L138.049,104.335L139.737,103.493L138.046,102.645L138.458,101.882L140.109,102.875L140.031,101H140.904L140.848,102.882L142.481,101.882L142.915,102.645L141.214,103.5L142.925,104.342L142.493,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M151.191,105.118L149.553,104.112L149.632,106H148.752L148.815,104.112L147.169,105.125L146.748,104.335L148.435,103.493L146.744,102.645L147.156,101.882L148.807,102.875L148.729,101H149.602L149.546,102.882L151.179,101.882L151.613,102.645L149.912,103.5L151.623,104.342L151.191,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M159.889,105.118L158.252,104.112L158.33,106H157.45L157.513,104.112L155.867,105.125L155.446,104.335L157.133,103.493L155.443,102.645L155.854,101.882L157.506,102.875L157.427,101H158.3L158.244,102.882L159.877,101.882L160.311,102.645L158.611,103.5L160.321,104.342L159.889,105.118Z" />
|
||||
<path
|
||||
android:fillColor="#707070"
|
||||
android:pathData="M168.588,105.118L166.95,104.112L167.028,106H166.148L166.211,104.112L164.565,105.125L164.144,104.335L165.831,103.493L164.141,102.645L164.553,101.882L166.204,102.875L166.126,101H166.999L166.943,102.882L168.575,101.882L169.01,102.645L167.309,103.5L169.02,104.342L168.588,105.118Z" />
|
||||
</vector>
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="214dp"
|
||||
android:height="130dp"
|
||||
android:viewportWidth="214"
|
||||
android:viewportHeight="130">
|
||||
<path
|
||||
android:pathData="M1.043,8.273C1.019,4.256 4.265,1 8.294,1H205.706C209.735,1 213.019,4.256 213.043,8.273L213.709,121.725C213.733,125.742 210.487,128.998 206.459,128.998H9.046C5.018,128.998 1.733,125.742 1.709,121.725L1.043,8.273Z"
|
||||
android:fillColor="#E4E4E4"/>
|
||||
<path
|
||||
android:pathData="M1.043,8.273C1.019,4.256 4.265,1 8.294,1H205.706C209.735,1 213.019,4.256 213.043,8.273L213.709,121.725C213.733,125.742 210.487,128.998 206.459,128.998H9.046C5.018,128.998 1.733,125.742 1.709,121.725L1.043,8.273Z"
|
||||
android:strokeWidth="0.5"
|
||||
android:fillColor="#00000000">
|
||||
<aapt:attr name="android:strokeColor">
|
||||
<gradient
|
||||
android:startY="-1.3567"
|
||||
android:startX="-1.98014"
|
||||
android:endY="122.149"
|
||||
android:endX="216.304"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFDCDCDC"/>
|
||||
<item android:offset="0.9999" android:color="#FFDEDEDE"/>
|
||||
<item android:offset="1" android:color="#00000000"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M25.274,26.4L25.274,32.64L28.079,32.64C28.188,32.639 28.292,32.596 28.369,32.522C28.446,32.447 28.49,32.346 28.491,32.241L28.491,26.4L25.274,26.4Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M19,32.241C19.001,32.346 19.045,32.447 19.122,32.522C19.199,32.596 19.303,32.639 19.412,32.64L22.217,32.64L22.217,26.4L19,26.4L19,32.241Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M28.491,23.2L28.491,20.409C28.491,20.301 28.448,20.197 28.372,20.12C28.296,20.043 28.194,20 28.086,20L19.405,20C19.297,20 19.195,20.043 19.119,20.12C19.043,20.197 19,20.301 19,20.409L19,23.2L28.491,23.2Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M43.503,24.163C42.922,23.921 42.292,23.812 41.661,23.846C40.827,23.844 39.996,23.934 39.183,24.113L39.183,25.532C39.832,25.455 40.483,25.412 41.136,25.4C41.486,25.385 41.836,25.418 42.177,25.5C42.286,25.532 42.385,25.59 42.465,25.669C42.546,25.748 42.605,25.846 42.637,25.953C42.74,26.334 42.783,26.728 42.766,27.122L42.766,27.539C41.997,27.498 41.385,27.476 40.965,27.476C40.519,27.454 40.073,27.539 39.667,27.725C39.347,27.903 39.109,28.195 38.999,28.54C38.847,29.044 38.779,29.569 38.796,30.094C38.74,30.771 38.901,31.448 39.257,32.029C39.445,32.243 39.683,32.409 39.95,32.513C40.216,32.618 40.505,32.659 40.791,32.632C41.127,32.632 42.172,32.605 42.633,32.007C42.709,31.912 42.774,31.808 42.826,31.698L42.877,32.496L44.572,32.496L44.572,27.222C44.596,26.539 44.52,25.857 44.346,25.196C44.284,24.977 44.178,24.772 44.033,24.594C43.888,24.417 43.708,24.27 43.503,24.163ZM42.748,29.256C42.796,29.736 42.713,30.219 42.508,30.656C42.409,30.782 42.272,30.876 42.117,30.924C41.942,30.972 41.76,30.995 41.578,30.992C41.294,31.028 41.006,30.953 40.777,30.783C40.623,30.523 40.558,30.221 40.592,29.922C40.582,29.681 40.607,29.44 40.666,29.206C40.684,29.139 40.716,29.075 40.761,29.021C40.805,28.966 40.861,28.921 40.924,28.889C41.098,28.821 41.285,28.791 41.472,28.803L42.748,28.803L42.748,29.256Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M58.169,24.645C58.133,24.584 58.095,24.526 58.053,24.471C57.494,23.736 56.162,23.846 56.162,23.846C55.722,23.831 55.29,23.964 54.929,24.222C54.564,24.573 54.329,25.042 54.263,25.55C54.001,27.328 54.001,29.136 54.263,30.914C54.326,31.417 54.555,31.882 54.911,32.233C55.299,32.52 55.774,32.659 56.251,32.623C56.251,32.623 56.278,32.623 56.296,32.623C56.296,32.623 57.498,32.72 58.084,32.104C58.095,32.607 58.062,33.11 57.985,33.606C57.966,33.738 57.915,33.864 57.837,33.971C57.759,34.078 57.656,34.164 57.539,34.222C57.166,34.351 56.773,34.406 56.381,34.382L54.535,34.35L54.535,35.788C54.876,35.853 55.219,35.9 55.563,35.93C55.988,35.972 56.39,35.99 56.774,35.99C57.49,36.044 58.205,35.884 58.834,35.53C59.055,35.374 59.242,35.174 59.386,34.943C59.529,34.71 59.626,34.451 59.67,34.18C59.813,33.297 59.875,32.402 59.853,31.507L59.853,23.979L58.209,23.979L58.169,24.645ZM57.963,30.221C57.936,30.452 57.824,30.664 57.65,30.813C57.447,30.94 57.212,30.999 56.975,30.983C56.711,31.006 56.446,30.95 56.211,30.823C56.028,30.636 55.923,30.385 55.916,30.12C55.815,28.876 55.815,27.625 55.916,26.381C55.923,26.125 56.025,25.882 56.202,25.701C56.442,25.579 56.709,25.526 56.975,25.55C57.227,25.529 57.479,25.599 57.686,25.747C57.87,25.947 57.981,26.207 57.999,26.482C58.066,27.072 58.093,27.666 58.079,28.26C58.101,28.916 58.062,29.573 57.963,30.221Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M66.713,24.124C66.131,23.787 65.461,23.636 64.793,23.691C64.098,23.642 63.404,23.802 62.799,24.152C62.358,24.473 62.062,24.959 61.975,25.502C61.831,26.388 61.77,27.286 61.793,28.184C61.769,29.083 61.833,29.983 61.984,30.87C62.031,31.139 62.131,31.395 62.278,31.623C62.425,31.851 62.616,32.046 62.84,32.197C63.478,32.531 64.194,32.681 64.911,32.631C65.367,32.63 65.823,32.603 66.276,32.552C66.653,32.517 67.025,32.453 67.392,32.359L67.392,30.916C66.554,31.004 65.867,31.05 65.33,31.05C64.933,31.074 64.535,31.025 64.155,30.907C64.038,30.857 63.936,30.778 63.856,30.677C63.777,30.577 63.723,30.458 63.7,30.331C63.618,29.823 63.585,29.307 63.6,28.792L67.574,28.792L67.574,28.165C67.596,27.241 67.548,26.317 67.433,25.4C67.368,24.896 67.109,24.438 66.713,24.124ZM65.849,27.428L63.6,27.428C63.6,26.95 63.632,26.473 63.696,26C63.704,25.887 63.736,25.777 63.79,25.678C63.843,25.579 63.917,25.492 64.005,25.424C64.473,25.233 64.994,25.233 65.462,25.424C65.548,25.494 65.619,25.583 65.671,25.683C65.723,25.782 65.754,25.892 65.762,26.004C65.833,26.475 65.862,26.952 65.849,27.428Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M78.824,25.12C78.747,24.744 78.542,24.406 78.243,24.161C77.886,23.928 77.46,23.817 77.032,23.844C77.032,23.844 76.991,23.844 76.963,23.844C76.963,23.844 75.581,23.767 75.01,24.464C74.948,24.542 74.892,24.625 74.844,24.712C74.752,24.478 74.592,24.275 74.383,24.129C74.024,23.923 73.61,23.826 73.195,23.848C73.195,23.848 71.813,23.767 71.242,24.468C71.178,24.545 71.122,24.629 71.076,24.717L71.03,23.984L69.349,23.984L69.349,32.48L71.191,32.48L71.191,28.051C71.182,27.52 71.201,26.988 71.251,26.459C71.263,26.198 71.362,25.949 71.532,25.749C71.736,25.584 71.997,25.506 72.26,25.531C72.493,25.513 72.727,25.557 72.937,25.658C73.015,25.731 73.076,25.819 73.119,25.916C73.162,26.013 73.185,26.118 73.186,26.223C73.234,26.747 73.254,27.272 73.245,27.798L73.245,32.48L75.088,32.48L75.088,28.051C75.088,27.314 75.088,26.785 75.143,26.459C75.156,26.197 75.258,25.946 75.434,25.749C75.642,25.586 75.906,25.508 76.171,25.531C76.405,25.513 76.641,25.557 76.852,25.658C76.929,25.732 76.99,25.82 77.032,25.917C77.074,26.014 77.096,26.118 77.096,26.223C77.147,26.747 77.167,27.272 77.156,27.798L77.156,32.48L78.999,32.48L78.999,26.884C79.009,26.291 78.951,25.699 78.824,25.12Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M51.691,24.171C51.307,23.928 50.851,23.814 50.394,23.845C50.394,23.845 50.356,23.845 50.338,23.845C50.338,23.845 48.938,23.773 48.364,24.465C48.323,24.514 48.286,24.565 48.252,24.619L48.21,23.981L46.507,23.981L46.507,32.48L48.373,32.48L48.373,28.049C48.363,27.521 48.384,26.992 48.434,26.465C48.448,26.203 48.553,25.952 48.733,25.755C48.958,25.584 49.242,25.503 49.526,25.528C49.774,25.511 50.023,25.555 50.249,25.655C50.333,25.728 50.401,25.816 50.448,25.915C50.496,26.014 50.521,26.121 50.524,26.23C50.578,26.75 50.6,27.273 50.59,27.796L50.59,32.48L52.456,32.48L52.456,26.963C52.471,26.356 52.419,25.749 52.302,25.153C52.226,24.765 52.009,24.417 51.691,24.171Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M36.543,21.44L34.653,21.44L34.653,23.877L33.477,23.877L33.477,25.434L34.653,25.434L34.653,32.48L36.543,32.48L36.543,25.434L37.981,25.434L37.981,23.877L36.543,23.877L36.543,21.44Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M23.463,105.118L21.826,104.112L21.904,106H21.024L21.087,104.112L19.441,105.125L19.02,104.335L20.707,103.493L19.016,102.645L19.428,101.882L21.08,102.875L21.001,101H21.874L21.818,102.882L23.451,101.882L23.885,102.645L22.184,103.5L23.895,104.342L23.463,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M32.161,105.118L30.524,104.112L30.602,106H29.722L29.785,104.112L28.139,105.125L27.718,104.335L29.405,103.493L27.715,102.645L28.126,101.882L29.778,102.875L29.699,101H30.573L30.517,102.882L32.149,101.882L32.583,102.645L30.883,103.5L32.593,104.342L32.161,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M40.86,105.118L39.222,104.112L39.3,106H38.42L38.483,104.112L36.837,105.125L36.416,104.335L38.104,103.493L36.413,102.645L36.825,101.882L38.476,102.875L38.398,101H39.271L39.215,102.882L40.847,101.882L41.282,102.645L39.581,103.5L41.292,104.342L40.86,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M49.558,105.118L47.92,104.112L47.998,106H47.119L47.181,104.112L45.535,105.125L45.114,104.335L46.802,103.493L45.111,102.645L45.523,101.882L47.174,102.875L47.096,101H47.969L47.913,102.882L49.546,101.882L49.98,102.645L48.279,103.5L49.99,104.342L49.558,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M63.14,105.118L61.502,104.112L61.58,106H60.701L60.763,104.112L59.117,105.125L58.696,104.335L60.384,103.493L58.693,102.645L59.105,101.882L60.756,102.875L60.678,101H61.551L61.495,102.882L63.128,101.882L63.562,102.645L61.861,103.5L63.572,104.342L63.14,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M71.838,105.118L70.2,104.112L70.279,106H69.399L69.462,104.112L67.815,105.125L67.394,104.335L69.082,103.493L67.391,102.645L67.803,101.882L69.454,102.875L69.376,101H70.249L70.193,102.882L71.826,101.882L72.26,102.645L70.559,103.5L72.27,104.342L71.838,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M80.536,105.118L78.898,104.112L78.977,106H78.097L78.16,104.112L76.514,105.125L76.093,104.335L77.78,103.493L76.089,102.645L76.501,101.882L78.152,102.875L78.074,101H78.947L78.891,102.882L80.524,101.882L80.958,102.645L79.257,103.5L80.968,104.342L80.536,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M89.234,105.118L87.597,104.112L87.675,106H86.795L86.858,104.112L85.212,105.125L84.791,104.335L86.478,103.493L84.788,102.645L85.2,101.882L86.851,102.875L86.772,101H87.646L87.59,102.882L89.222,101.882L89.657,102.645L87.956,103.5L89.666,104.342L89.234,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M102.816,105.118L101.179,104.112L101.257,106H100.377L100.44,104.112L98.794,105.125L98.373,104.335L100.06,103.493L98.369,102.645L98.781,101.882L100.433,102.875L100.354,101H101.227L101.171,102.882L102.804,101.882L103.238,102.645L101.538,103.5L103.248,104.342L102.816,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M111.515,105.118L109.877,104.112L109.955,106H109.075L109.138,104.112L107.492,105.125L107.071,104.335L108.758,103.493L107.068,102.645L107.48,101.882L109.131,102.875L109.053,101H109.926L109.87,102.882L111.502,101.882L111.937,102.645L110.236,103.5L111.947,104.342L111.515,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M120.213,105.118L118.575,104.112L118.653,106H117.774L117.836,104.112L116.19,105.125L115.769,104.335L117.457,103.493L115.766,102.645L116.178,101.882L117.829,102.875L117.751,101H118.624L118.568,102.882L120.201,101.882L120.635,102.645L118.934,103.5L120.645,104.342L120.213,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M128.911,105.118L127.273,104.112L127.352,106H126.472L126.535,104.112L124.888,105.125L124.467,104.335L126.155,103.493L124.464,102.645L124.876,101.882L126.527,102.875L126.449,101H127.322L127.266,102.882L128.899,101.882L129.333,102.645L127.632,103.5L129.343,104.342L128.911,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M142.493,105.118L140.855,104.112L140.933,106H140.054L140.116,104.112L138.47,105.125L138.049,104.335L139.737,103.493L138.046,102.645L138.458,101.882L140.109,102.875L140.031,101H140.904L140.848,102.882L142.481,101.882L142.915,102.645L141.214,103.5L142.925,104.342L142.493,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M151.191,105.118L149.553,104.112L149.632,106H148.752L148.815,104.112L147.169,105.125L146.748,104.335L148.435,103.493L146.744,102.645L147.156,101.882L148.807,102.875L148.729,101H149.602L149.546,102.882L151.179,101.882L151.613,102.645L149.912,103.5L151.623,104.342L151.191,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M159.889,105.118L158.252,104.112L158.33,106H157.45L157.513,104.112L155.867,105.125L155.446,104.335L157.133,103.493L155.443,102.645L155.854,101.882L157.506,102.875L157.427,101H158.3L158.244,102.882L159.877,101.882L160.311,102.645L158.611,103.5L160.321,104.342L159.889,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
<path
|
||||
android:pathData="M168.588,105.118L166.95,104.112L167.028,106H166.148L166.211,104.112L164.565,105.125L164.144,104.335L165.831,103.493L164.141,102.645L164.553,101.882L166.204,102.875L166.126,101H166.999L166.943,102.882L168.575,101.882L169.01,102.645L167.309,103.5L169.02,104.342L168.588,105.118Z"
|
||||
android:fillColor="#707070"/>
|
||||
</vector>
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="249dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="249"
|
||||
android:viewportHeight="150">
|
||||
<path
|
||||
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z"
|
||||
android:fillAlpha="0.03"/>
|
||||
<path
|
||||
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z"
|
||||
android:fillAlpha="0.5">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:gradientRadius="267.36932"
|
||||
android:centerX="7.404067"
|
||||
android:centerY="8.65384"
|
||||
android:type="radial">
|
||||
<item android:offset="0" android:color="#FFFFFFFF"/>
|
||||
<item android:offset="1" android:color="#19FFFFFF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M10,0L238,0A9.999,10.001 134.998,0 1,248.001 10L248.017,140A9.999,10.001 134.998,0 1,238.019 150L10.019,150A9.999,10.001 134.998,0 1,0.017 140L0.001,10A9.999,10.001 134.998,0 1,10 0z"
|
||||
android:fillColor="#1B1D1C"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M10,0.5L238,0.5A9.499,9.501 134.998,0 1,247.501 10L247.517,140A9.499,9.501 134.998,0 1,238.019 149.5L10.019,149.5A9.499,9.501 134.998,0 1,0.517 140L0.501,10A9.499,9.501 134.998,0 1,10 0.5z"
|
||||
android:fillColor="#00000000">
|
||||
<aapt:attr name="android:strokeColor">
|
||||
<gradient
|
||||
android:startY="3.38345"
|
||||
android:startX="2.844399"
|
||||
android:endY="144.774"
|
||||
android:endX="246.96196"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FF1A1A1A"/>
|
||||
<item android:offset="1" android:color="#FF565656"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M28.359,121.942L26.389,120.734L26.472,123H25.41L25.498,120.734L23.505,121.95L23.002,121.003L25.044,119.992L23.009,118.974L23.511,118.058L25.497,119.25L25.415,117H26.468L26.389,119.258L28.365,118.058L28.884,118.974L26.826,120L28.885,121.011L28.359,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M38.855,121.942L36.885,120.734L36.967,123H35.906L35.994,120.734L34.001,121.95L33.498,121.003L35.54,119.992L33.505,118.974L34.007,118.058L35.993,119.25L35.911,117H36.964L36.884,119.258L38.861,118.058L39.38,118.974L37.322,120L39.381,121.011L38.855,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M49.351,121.942L47.381,120.734L47.463,123H46.402L46.49,120.734L44.497,121.95L43.994,121.003L46.035,119.992L44.001,118.974L44.503,118.058L46.489,119.25L46.407,117H47.46L47.38,119.258L49.357,118.058L49.876,118.974L47.818,120L49.877,121.011L49.351,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M59.847,121.942L57.877,120.734L57.959,123H56.897L56.985,120.734L54.993,121.95L54.49,121.003L56.531,119.992L54.497,118.974L54.999,118.058L56.985,119.25L56.902,117H57.956L57.876,119.258L59.853,118.058L60.372,118.974L58.314,120L60.373,121.011L59.847,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M76.235,121.942L74.266,120.734L74.348,123H73.286L73.374,120.734L71.381,121.95L70.879,121.003L72.92,119.992L70.885,118.974L71.387,118.058L73.373,119.25L73.291,117H74.345L74.265,119.258L76.242,118.058L76.761,118.974L74.703,120L76.762,121.011L76.235,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M86.731,121.942L84.761,120.734L84.844,123H83.782L83.87,120.734L81.877,121.95L81.374,121.003L83.416,119.992L81.381,118.974L81.883,118.058L83.869,119.25L83.787,117H84.841L84.761,119.258L86.737,118.058L87.257,118.974L85.199,120L87.258,121.011L86.731,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M97.227,121.942L95.257,120.734L95.34,123H94.278L94.366,120.734L92.373,121.95L91.87,121.003L93.912,119.992L91.877,118.974L92.379,118.058L94.365,119.25L94.283,117H95.337L95.257,119.258L97.233,118.058L97.752,118.974L95.695,120L97.753,121.011L97.227,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M107.723,121.942L105.753,120.734L105.835,123H104.774L104.862,120.734L102.869,121.95L102.366,121.003L104.408,119.992L102.373,118.974L102.875,118.058L104.861,119.25L104.779,117H105.832L105.752,119.258L107.729,118.058L108.248,118.974L106.19,120L108.249,121.011L107.723,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M124.112,121.942L122.142,120.734L122.224,123H121.163L121.251,120.734L119.258,121.95L118.755,121.003L120.796,119.992L118.762,118.974L119.264,118.058L121.25,119.25L121.168,117H122.221L122.141,119.258L124.118,118.058L124.637,118.974L122.579,120L124.638,121.011L124.112,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M134.608,121.942L132.638,120.734L132.72,123H131.659L131.746,120.734L129.754,121.95L129.251,121.003L131.292,119.992L129.258,118.974L129.76,118.058L131.746,119.25L131.664,117H132.717L132.637,119.258L134.614,118.058L135.133,118.974L133.075,120L135.134,121.011L134.608,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M145.104,121.942L143.134,120.734L143.216,123H142.154L142.242,120.734L140.249,121.95L139.747,121.003L141.788,119.992L139.754,118.974L140.256,118.058L142.242,119.25L142.159,117H143.213L143.133,119.258L145.11,118.058L145.629,118.974L143.571,120L145.63,121.011L145.104,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M155.599,121.942L153.63,120.734L153.712,123H152.65L152.738,120.734L150.745,121.95L150.242,121.003L152.284,119.992L150.249,118.974L150.751,118.058L152.737,119.25L152.655,117H153.709L153.629,119.258L155.606,118.058L156.125,118.974L154.067,120L156.126,121.011L155.599,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M171.988,121.942L170.018,120.734L170.101,123H169.039L169.127,120.734L167.134,121.95L166.631,121.003L168.673,119.992L166.638,118.974L167.14,118.058L169.126,119.25L169.044,117H170.098L170.018,119.258L171.994,118.058L172.513,118.974L170.456,120L172.515,121.011L171.988,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M182.484,121.942L180.514,120.734L180.597,123H179.535L179.623,120.734L177.63,121.95L177.127,121.003L179.169,119.992L177.134,118.974L177.636,118.058L179.622,119.25L179.54,117H180.593L180.514,119.258L182.49,118.058L183.009,118.974L180.952,120L183.01,121.011L182.484,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M192.98,121.942L191.01,120.734L191.092,123H190.031L190.119,120.734L188.126,121.95L187.623,121.003L189.665,119.992L187.63,118.974L188.132,118.058L190.118,119.25L190.036,117H191.089L191.009,119.258L192.986,118.058L193.505,118.974L191.447,120L193.506,121.011L192.98,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M203.476,121.942L201.506,120.734L201.588,123H200.527L200.615,120.734L198.622,121.95L198.119,121.003L200.16,119.992L198.126,118.974L198.628,118.058L200.614,119.25L200.532,117H201.585L201.505,119.258L203.482,118.058L204.001,118.974L201.943,120L204.002,121.011L203.476,121.942Z"
|
||||
android:fillColor="#A5A5A5"/>
|
||||
<path
|
||||
android:pathData="M26.483,30.4L26.483,36.64L29.382,36.64C29.494,36.639 29.602,36.596 29.681,36.522C29.761,36.447 29.806,36.346 29.807,36.241L29.807,30.4L26.483,30.4Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M20,36.241C20.001,36.346 20.046,36.447 20.126,36.522C20.205,36.596 20.313,36.639 20.425,36.64L23.324,36.64L23.324,30.4L20,30.4L20,36.241Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M29.807,27.2L29.807,24.409C29.807,24.301 29.763,24.197 29.685,24.12C29.606,24.043 29.5,24 29.389,24L20.418,24C20.307,24 20.201,24.043 20.123,24.12C20.044,24.197 20,24.301 20,24.409L20,27.2L29.807,27.2Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M45.32,28.163C44.719,27.921 44.068,27.812 43.416,27.846C42.555,27.844 41.696,27.934 40.856,28.113L40.856,29.532C41.526,29.455 42.199,29.412 42.874,29.4C43.236,29.385 43.598,29.418 43.949,29.5C44.062,29.532 44.164,29.59 44.247,29.669C44.33,29.748 44.392,29.846 44.425,29.953C44.531,30.334 44.576,30.728 44.558,31.122L44.558,31.539C43.764,31.498 43.131,31.476 42.698,31.476C42.236,31.454 41.775,31.539 41.356,31.725C41.025,31.903 40.779,32.195 40.666,32.54C40.509,33.044 40.438,33.569 40.456,34.095C40.398,34.771 40.564,35.448 40.932,36.029C41.127,36.243 41.372,36.409 41.648,36.513C41.924,36.618 42.222,36.659 42.517,36.632C42.864,36.632 43.945,36.605 44.421,36.007C44.499,35.912 44.566,35.808 44.62,35.699L44.673,36.496L46.424,36.496L46.424,31.222C46.45,30.539 46.371,29.857 46.191,29.196C46.127,28.977 46.017,28.772 45.867,28.594C45.717,28.417 45.531,28.27 45.32,28.163ZM44.54,33.256C44.589,33.736 44.503,34.219 44.292,34.656C44.189,34.783 44.048,34.876 43.887,34.924C43.706,34.972 43.519,34.995 43.331,34.992C43.037,35.028 42.74,34.953 42.503,34.783C42.343,34.523 42.277,34.221 42.312,33.922C42.302,33.681 42.327,33.44 42.388,33.206C42.407,33.139 42.44,33.076 42.486,33.021C42.532,32.966 42.59,32.921 42.655,32.889C42.834,32.821 43.028,32.791 43.221,32.803L44.54,32.803L44.54,33.256Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M60.474,28.645C60.438,28.584 60.398,28.526 60.354,28.471C59.777,27.736 58.401,27.846 58.401,27.846C57.946,27.832 57.499,27.964 57.126,28.222C56.75,28.573 56.507,29.042 56.438,29.55C56.168,31.328 56.168,33.136 56.438,34.915C56.504,35.417 56.74,35.882 57.108,36.233C57.509,36.52 58,36.659 58.493,36.623C58.493,36.623 58.521,36.623 58.539,36.623C58.539,36.623 59.782,36.72 60.386,36.104C60.398,36.607 60.364,37.11 60.285,37.606C60.264,37.738 60.212,37.864 60.131,37.971C60.051,38.078 59.945,38.164 59.823,38.222C59.439,38.351 59.033,38.406 58.627,38.382L56.72,38.35L56.72,39.788C57.071,39.853 57.426,39.9 57.782,39.93C58.221,39.972 58.636,39.99 59.034,39.99C59.773,40.044 60.512,39.884 61.162,39.53C61.39,39.374 61.583,39.174 61.732,38.943C61.88,38.71 61.98,38.451 62.026,38.18C62.174,37.297 62.237,36.402 62.215,35.507L62.215,27.979L60.516,27.979L60.474,28.645ZM60.262,34.221C60.233,34.452 60.118,34.664 59.938,34.813C59.729,34.939 59.486,34.999 59.241,34.984C58.968,35.006 58.694,34.95 58.452,34.823C58.263,34.636 58.153,34.384 58.147,34.12C58.042,32.876 58.042,31.625 58.147,30.381C58.153,30.125 58.259,29.882 58.442,29.701C58.69,29.579 58.966,29.526 59.241,29.55C59.502,29.529 59.761,29.599 59.975,29.747C60.166,29.947 60.28,30.207 60.299,30.482C60.369,31.072 60.396,31.666 60.382,32.26C60.405,32.916 60.364,33.573 60.262,34.221Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M69.304,28.124C68.702,27.787 68.01,27.636 67.319,27.691C66.601,27.642 65.884,27.802 65.259,28.152C64.804,28.473 64.497,28.959 64.408,29.502C64.259,30.388 64.196,31.286 64.219,32.184C64.195,33.083 64.261,33.983 64.417,34.87C64.465,35.139 64.568,35.395 64.72,35.623C64.872,35.851 65.07,36.046 65.301,36.197C65.961,36.531 66.701,36.681 67.441,36.631C67.913,36.63 68.384,36.603 68.852,36.552C69.241,36.517 69.626,36.453 70.005,36.359L70.005,34.916C69.139,35.004 68.429,35.05 67.874,35.05C67.464,35.074 67.053,35.025 66.66,34.907C66.54,34.857 66.433,34.778 66.351,34.677C66.269,34.577 66.214,34.458 66.19,34.331C66.106,33.822 66.071,33.307 66.087,32.792L70.193,32.792L70.193,32.165C70.215,31.241 70.167,30.317 70.047,29.4C69.98,28.896 69.713,28.438 69.304,28.124ZM68.41,31.428L66.087,31.428C66.086,30.95 66.119,30.473 66.186,30C66.195,29.887 66.228,29.777 66.283,29.678C66.338,29.579 66.414,29.492 66.505,29.424C66.988,29.233 67.527,29.233 68.01,29.424C68.1,29.494 68.173,29.583 68.227,29.682C68.28,29.782 68.312,29.892 68.321,30.004C68.394,30.475 68.424,30.952 68.41,31.428Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M81.818,29.12C81.739,28.745 81.527,28.406 81.218,28.161C80.849,27.928 80.409,27.817 79.966,27.844C79.966,27.844 79.924,27.844 79.895,27.844C79.895,27.844 78.467,27.767 77.877,28.464C77.813,28.542 77.755,28.625 77.705,28.713C77.61,28.478 77.445,28.275 77.229,28.129C76.858,27.924 76.431,27.826 76.001,27.848C76.001,27.848 74.573,27.767 73.983,28.468C73.917,28.545 73.86,28.629 73.812,28.717L73.764,27.984L72.027,27.984L72.027,36.48L73.931,36.48L73.931,32.051C73.921,31.52 73.942,30.988 73.993,30.459C74.005,30.198 74.107,29.949 74.283,29.749C74.493,29.584 74.764,29.506 75.035,29.531C75.276,29.513 75.518,29.557 75.735,29.658C75.815,29.731 75.879,29.819 75.923,29.916C75.967,30.013 75.991,30.118 75.992,30.224C76.042,30.747 76.063,31.272 76.054,31.798L76.054,36.48L77.958,36.48L77.958,32.051C77.958,31.314 77.958,30.785 78.015,30.459C78.028,30.197 78.133,29.946 78.315,29.749C78.53,29.586 78.802,29.508 79.076,29.531C79.319,29.513 79.562,29.557 79.781,29.658C79.86,29.732 79.923,29.82 79.966,29.917C80.01,30.014 80.033,30.118 80.033,30.224C80.086,30.747 80.106,31.272 80.095,31.798L80.095,36.48L81.999,36.48L81.999,30.884C82.009,30.291 81.949,29.699 81.818,29.12Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M53.781,28.171C53.383,27.928 52.913,27.814 52.44,27.845C52.44,27.845 52.402,27.845 52.382,27.845C52.382,27.845 50.936,27.773 50.343,28.465C50.301,28.514 50.262,28.565 50.227,28.619L50.184,27.981L48.424,27.981L48.424,36.48L50.353,36.48L50.353,32.049C50.342,31.521 50.363,30.992 50.415,30.465C50.429,30.203 50.538,29.952 50.724,29.755C50.957,29.584 51.25,29.503 51.543,29.529C51.8,29.511 52.057,29.555 52.291,29.655C52.378,29.728 52.448,29.816 52.497,29.915C52.546,30.014 52.572,30.121 52.575,30.23C52.63,30.75 52.653,31.273 52.643,31.796L52.643,36.48L54.571,36.48L54.571,30.963C54.587,30.356 54.533,29.75 54.412,29.153C54.333,28.765 54.109,28.417 53.781,28.171Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M38.128,25.44L36.175,25.44L36.175,27.877L34.96,27.877L34.96,29.434L36.175,29.434L36.175,36.48L38.128,36.48L38.128,29.434L39.614,29.434L39.614,27.877L38.128,27.877L38.128,25.44Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
android:clipChildren="false">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/linearLayoutCompat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
|
|
@ -37,6 +38,7 @@
|
|||
android:layout_marginStart="64dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="64dp"
|
||||
android:minLines="3"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -45,6 +47,20 @@
|
|||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/chb_understand"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/alert_button_i_understand"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
app:buttonTint="@color/accent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayoutCompat"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fl_scan_card_container"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/coordinator_details_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/backgroundLightGray"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/app_bar"
|
||||
style="@style/Widget.MaterialComponents.Toolbar.Surface"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/backgroundLightGray"
|
||||
android:fitsSystemWindows="true"
|
||||
app:liftOnScroll="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:navigationIcon="@drawable/ic_baseline_arrow_back_24"
|
||||
app:title="Re-create wallet" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_details_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="33dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_step_number"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="start"
|
||||
android:text="@string/details_twins_recreate_step_format"
|
||||
android:textColor="@color/colorSecondary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="@+id/v_step_1"
|
||||
app:layout_constraintStart_toStartOf="@id/v_step_1"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/v_step_1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="3dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@color/colorSecondary"
|
||||
app:layout_constraintEnd_toStartOf="@+id/v_step_2"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_step_number" />
|
||||
|
||||
<View
|
||||
android:id="@+id/v_step_2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="3dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="@color/colorSecondary"
|
||||
app:layout_constraintEnd_toStartOf="@+id/v_step_3"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/v_step_1"
|
||||
app:layout_constraintTop_toTopOf="@id/v_step_1" />
|
||||
|
||||
<View
|
||||
android:id="@+id/v_step_3"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="3dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:background="@color/colorSecondary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/v_step_2"
|
||||
app:layout_constraintTop_toTopOf="@id/v_step_1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_twin_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/details_twins_recreate_title_format"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/v_step_1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_twin_cards_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/details_twins_recreate_subtitle"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_twin_title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_card_1"
|
||||
android:layout_width="130dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_marginStart="47dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/card_placeholder"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_twin_cards_description" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_card_2"
|
||||
android:layout_width="130dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:src="@drawable/card_placeholder"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_card_1" />
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintGuide_percent="0.37" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_tap"
|
||||
style="@style/TapBlackButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:icon="@drawable/ic_scan"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
tools:text="@string/details_twins_recreate_button_format" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/coordinator_details_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/backgroundLightGray"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_details_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="33dp">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/l_twin_cards"
|
||||
android:alpha="0.55"
|
||||
android:background="@color/warning"/>
|
||||
|
||||
<include
|
||||
android:id="@+id/l_twin_cards"
|
||||
layout="@layout/layout_twin_cards_orange"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="-40dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/tv_twin_title"
|
||||
app:layout_constraintVertical_bias="0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_twin_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:drawablePadding="4dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/common_warning"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:drawableStartCompat="@drawable/ic_warning"
|
||||
app:drawableTint="@color/darkGray6"
|
||||
app:layout_constraintBottom_toTopOf="@id/tv_twin_cards_description"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_twin_cards_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/details_twins_recreate_warning"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/btn_start"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_cancel"
|
||||
style="@style/TapBlackButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/common_back"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintGuide_percent="0.37" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_start"
|
||||
style="@style/TapButtonWithIcon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/common_start"
|
||||
app:icon="@drawable/ic_send"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
<include
|
||||
android:id="@+id/onboarding_action_container"
|
||||
layout="@layout/layout_onboarding_bottom_action_views"
|
||||
layout="@layout/layout_onboarding_container_bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/backgroundLightGray"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_details_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="33dp">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/l_twin_cards"
|
||||
android:alpha="0.22"
|
||||
android:background="@color/darkGray6"/>
|
||||
|
||||
<include
|
||||
android:id="@+id/l_twin_cards"
|
||||
layout="@layout/layout_twin_cards"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="-40dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:gravity="top"
|
||||
app:layout_constraintBottom_toTopOf="@id/tv_twin_title"
|
||||
app:layout_constraintVertical_bias="0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_twin_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24sp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/twins_onboarding_title"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/tv_twin_subtitle"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_twin_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/twins_onboarding_subtitle"
|
||||
android:textColor="@color/darkGray6"
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@id/tv_twin_cards_description_1"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_twin_cards_description_1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/twins_onboarding_description_format"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/btn_continue"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintGuide_percent="0.37" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_continue"
|
||||
style="@style/TapBlackButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginStart="7dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/common_continue"
|
||||
app:icon="@drawable/ic_send"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
android:elevation="3dp"
|
||||
android:maxHeight="215dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/card_placeholder"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@
|
|||
android:clipChildren="false">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/linearLayoutCompat"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
|
||||
|
|
@ -37,6 +38,7 @@
|
|||
android:layout_marginStart="64dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="64dp"
|
||||
android:minLines="3"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -45,6 +47,20 @@
|
|||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/chb_understand"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/alert_button_i_understand"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
app:buttonTint="@color/accent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayoutCompat"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fl_scan_card_container"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
@ -1,11 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/onboarding_main_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
android:visibility="visible">
|
||||
|
||||
<include
|
||||
android:id="@+id/onboarding_twins_welcome_bg"
|
||||
layout="@layout/view_bg_twins_welcome"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<include
|
||||
layout="@layout/view_onboarding_progress"
|
||||
android:layout_width="0dp"
|
||||
|
|
@ -38,12 +46,43 @@
|
|||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="@dimen/onboarding_card_margin_top"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:src="@drawable/ic_home_front_card"
|
||||
android:transitionName="imv_front_card"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/cards_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="@dimen/onboarding_card_margin_top"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:clipChildren="false"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_twin_back_card"
|
||||
android:layout_width="328dp"
|
||||
android:layout_height="174dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_white" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_twin_front_card"
|
||||
android:layout_width="328dp"
|
||||
android:layout_height="174dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_black" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/onboarding_tv_balance"
|
||||
layout="@layout/view_onboarding_tv_balance"
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
android:elevation="0dp"
|
||||
android:rotation="-21"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_home_back_card"
|
||||
android:src="@drawable/card_placeholder_white"
|
||||
android:translationY="@dimen/home_back_card_translationY"
|
||||
app:layout_constraintEnd_toEndOf="@+id/imv_front_card"
|
||||
app:layout_constraintStart_toStartOf="@+id/imv_front_card"
|
||||
|
|
@ -26,8 +26,7 @@
|
|||
android:elevation="1dp"
|
||||
android:rotation="-3"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_home_front_card"
|
||||
android:transitionName="imv_front_card"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
android:translationY="@dimen/home_front_card_translationY"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="-40dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="40dp"
|
||||
android:layout_marginTop="60dp"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_group_1258"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_twin_card_2"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_twin_card_1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="170dp"
|
||||
android:layout_marginTop="50dp"
|
||||
android:rotation="-19.5"
|
||||
android:src="@drawable/card_placeholder"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_twin_card_2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="170dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:rotation="-19.5"
|
||||
android:src="@drawable/card_placeholder"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_twin_card_1" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="-40dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="40dp"
|
||||
android:layout_marginTop="60dp"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_twin_cards_background_warning"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_twin_card_2"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_twin_card_1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="170dp"
|
||||
android:layout_marginTop="50dp"
|
||||
android:rotation="-19.5"
|
||||
android:src="@drawable/card_placeholder"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_twin_card_2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="170dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:rotation="-19.5"
|
||||
android:src="@drawable/card_placeholder"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_twin_card_1" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -6,6 +6,11 @@
|
|||
android:layout_height="match_parent"
|
||||
android:visibility="visible">
|
||||
|
||||
<include
|
||||
android:id="@+id/onboarding_twins_welcome_bg"
|
||||
layout="@layout/view_bg_twins_welcome"
|
||||
android:visibility="gone" />
|
||||
|
||||
<include
|
||||
layout="@layout/view_onboarding_progress"
|
||||
android:layout_width="0dp"
|
||||
|
|
@ -38,12 +43,43 @@
|
|||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="@dimen/onboarding_card_margin_top"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:src="@drawable/ic_home_front_card"
|
||||
android:transitionName="imv_front_card"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/cards_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="@dimen/onboarding_card_margin_top"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:clipChildren="false"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_twin_back_card"
|
||||
android:layout_width="328dp"
|
||||
android:layout_height="174dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_white" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_twin_front_card"
|
||||
android:layout_width="328dp"
|
||||
android:layout_height="174dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_black" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/onboarding_tv_balance"
|
||||
layout="@layout/view_onboarding_tv_balance"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@
|
|||
android:layout_height="match_parent"
|
||||
android:visibility="visible">
|
||||
|
||||
<include
|
||||
android:id="@+id/onboarding_twins_welcome_bg"
|
||||
layout="@layout/view_bg_twins_welcome"
|
||||
android:visibility="gone" />
|
||||
|
||||
<include
|
||||
layout="@layout/view_onboarding_progress"
|
||||
android:layout_width="0dp"
|
||||
|
|
@ -38,14 +43,45 @@
|
|||
android:layout_width="140dp"
|
||||
android:layout_height="88dp"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder"
|
||||
android:transitionName="imv_front_card"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
android:translationY="-30dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/cards_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="@dimen/onboarding_card_margin_top"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:clipChildren="false"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_twin_back_card"
|
||||
android:layout_width="328dp"
|
||||
android:layout_height="174dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_white" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_twin_front_card"
|
||||
android:layout_width="328dp"
|
||||
android:layout_height="174dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_black" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/onboarding_tv_balance"
|
||||
layout="@layout/view_onboarding_tv_balance"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@
|
|||
android:layout_height="match_parent"
|
||||
android:visibility="visible">
|
||||
|
||||
<include
|
||||
android:id="@+id/onboarding_twins_welcome_bg"
|
||||
layout="@layout/view_bg_twins_welcome"
|
||||
android:visibility="gone" />
|
||||
|
||||
<include
|
||||
layout="@layout/view_onboarding_progress"
|
||||
android:layout_width="0dp"
|
||||
|
|
@ -37,14 +42,45 @@
|
|||
android:layout_width="140dp"
|
||||
android:layout_height="88dp"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/ic_home_front_card"
|
||||
android:transitionName="imv_front_card"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
android:translationY="-30dp"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/cards_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="@dimen/onboarding_card_margin_top"
|
||||
android:clipChildren="false"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_twin_back_card"
|
||||
android:layout_width="328dp"
|
||||
android:layout_height="174dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_white" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_twin_front_card"
|
||||
android:layout_width="328dp"
|
||||
android:layout_height="174dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_black" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/onboarding_tv_balance"
|
||||
layout="@layout/view_onboarding_tv_balance"
|
||||
|
|
|
|||
116
app/src/main/res/layout/lp_onboarding_done_activation_twins.xml
Normal file
116
app/src/main/res/layout/lp_onboarding_done_activation_twins.xml
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/onboarding_main_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="visible">
|
||||
|
||||
<include
|
||||
android:id="@+id/onboarding_twins_welcome_bg"
|
||||
layout="@layout/view_bg_twins_welcome"
|
||||
android:visibility="gone" />
|
||||
|
||||
<include
|
||||
layout="@layout/view_onboarding_progress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="@dimen/onboarding_progress_margin_top"
|
||||
android:layout_marginEnd="32dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_card_background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="230dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="@dimen/onboarding_square_background_margin_top"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:background="@drawable/shape_rectangle_rounded_8"
|
||||
android:backgroundTint="@color/lightGray0"
|
||||
android:elevation="0dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/pb_state" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_front_card"
|
||||
android:layout_width="140dp"
|
||||
android:layout_height="88dp"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
android:translationY="-30dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/cards_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="@dimen/onboarding_card_margin_top"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:clipChildren="false"
|
||||
android:src="@drawable/card_placeholder_black"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_twin_back_card"
|
||||
android:layout_width="328dp"
|
||||
android:layout_height="174dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_white" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_twin_front_card"
|
||||
android:layout_width="328dp"
|
||||
android:layout_height="174dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="fitXY"
|
||||
android:src="@drawable/card_placeholder_black" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/onboarding_tv_balance"
|
||||
layout="@layout/view_onboarding_tv_balance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imv_card_background"
|
||||
app:layout_constraintEnd_toEndOf="@+id/imv_card_background"
|
||||
app:layout_constraintStart_toStartOf="@+id/imv_card_background"
|
||||
app:layout_constraintTop_toTopOf="@+id/imv_card_background" />
|
||||
|
||||
<include
|
||||
android:id="@+id/btn_refresh_balance"
|
||||
layout="@layout/view_onboarding_refresh_balance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imv_card_background"
|
||||
app:layout_constraintEnd_toEndOf="@+id/imv_card_background"
|
||||
app:layout_constraintStart_toStartOf="@+id/imv_card_background"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imv_card_background" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/imv_activation_success"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_activation_success"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imv_card_background"
|
||||
app:layout_constraintEnd_toEndOf="@+id/imv_card_background"
|
||||
app:layout_constraintStart_toStartOf="@+id/imv_card_background"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imv_card_background" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue