Updated on 2026-08-14

This commit is contained in:
Tangem 2022-10-20 00:20:17 +05:00
parent 5ec2a0c5d5
commit a74efa2951
5 changed files with 491 additions and 157 deletions

View file

@ -30,5 +30,16 @@ class OnboardingSaltPayHelper {
Result.Failure(error)
}
}
suspend fun testProceedToOnboarding(scanResponse: ScanResponse): Result<Boolean> {
return try {
val isBackupOnboardingCase = scanResponse.card.backupStatus?.isActive == false
Result.Success(isBackupOnboardingCase)
} catch (ex: Exception) {
Result.Failure(ex)
} catch (error: SaltPayRegistrationError){
Result.Failure(error)
}
}
}
}

View file

@ -0,0 +1,292 @@
package com.tangem.tap.features.onboarding.products.wallet.ui
import com.google.android.material.button.MaterialButton
import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupState
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.FIRST_BACKUP
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.ORIGIN
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.SECOND_BACKUP
import com.tangem.wallet.databinding.FragmentOnboardingWalletBinding
/**
[REDACTED_AUTHOR]
*/
interface BackupAnimator {
fun updateBackupState(backupState: BackupState)
fun setupCreateWalletState()
fun showBackupIntro(state: BackupState)
fun showScanOriginCard()
fun showAddBackupCards(state: BackupState, backupCardsCounts: Int)
fun showWritePrimaryCard(state: BackupState)
fun showWriteBackupCard(state: BackupState, backupCard: Int)
fun showSuccess(onEnd: () -> Unit)
}
class WalletBackupAnimator(
private val cardsWidget: WalletCardsWidget,
) : BackupAnimator {
private val viewState: State = when (cardsWidget.leapfrogWidget.getViewsCount()) {
2 -> State.TwoCards
3 -> State.TreeCards
else -> throw UnsupportedOperationException()
}
private var currentStep = 0
override fun updateBackupState(backupState: BackupState) {
}
override fun setupCreateWalletState() {
currentStep = STEP_CREATE_WALLET
cardsWidget.toFolded(false)
ORIGIN.alpha(1f)
FIRST_BACKUP.alpha(1f)
SECOND_BACKUP.alpha(1f)
}
override fun showBackupIntro(state: BackupState) {
currentStep = STEP_BACKUP_INTRO
ORIGIN.show()
FIRST_BACKUP.show()
SECOND_BACKUP.show()
cardsWidget.toWelcome()
}
override fun showScanOriginCard() {
currentStep = STEP_SCAN_ORIGIN_CARD
cardsWidget.toFolded()
}
override fun showAddBackupCards(state: BackupState, backupCardsCounts: Int) {
when (backupCardsCounts) {
0 -> {
when (currentStep) {
STEP_CREATE_WALLET -> {
cardsWidget.toFolded(false) { cardsWidget.toFan() }
FIRST_BACKUP.animateAlpha(0.6f, 0)
SECOND_BACKUP.animateAlpha(0.2f, 400)
}
STEP_BACKUP_INTRO -> {
cardsWidget.toFolded(duration = 200) { cardsWidget.toFan() }
FIRST_BACKUP.animateAlpha(0.6f, 400, 250)
SECOND_BACKUP.animateAlpha(0.2f, 400, 250)
}
STEP_SCAN_ORIGIN_CARD -> {
cardsWidget.toFan()
FIRST_BACKUP.animateAlpha(0.6f, 400, 250)
SECOND_BACKUP.animateAlpha(0.2f, 400, 250)
}
}
}
1 -> {
cardsWidget.toFan(false)
FIRST_BACKUP.alpha(1f)
SECOND_BACKUP.alpha(0.2f)
}
2 -> {
cardsWidget.toFan(false)
FIRST_BACKUP.alpha(1f)
SECOND_BACKUP.alpha(1f)
}
}
currentStep = STEP_ADD_BACKUP_CARDS
}
override fun showWritePrimaryCard(state: BackupState) {
currentStep = STEP_WRITE_PRIMARY_CARD
cardsWidget.toLeapfrog(
onEndFold = {
FIRST_BACKUP.alpha(0.4f)
SECOND_BACKUP.alpha(0.2f)
},
)
}
override fun showWriteBackupCard(state: BackupState, backupCard: Int) {
currentStep = STEP_WRITE_BACKUP_CARD
when (viewState) {
State.TwoCards -> {
FIRST_BACKUP.alpha(1f)
cardsWidget.leapfrogWidget.leap {
ORIGIN.alpha(0.4f)
}
}
State.TreeCards -> {
when (backupCard) {
1 -> {
FIRST_BACKUP.alpha(1f)
cardsWidget.leapfrogWidget.leap {
SECOND_BACKUP.alpha(0.4f)
ORIGIN.alpha(0.2f)
}
}
2 -> {
SECOND_BACKUP.alpha(1f)
cardsWidget.leapfrogWidget.leap {
ORIGIN.alpha(0.4f)
FIRST_BACKUP.alpha(0.2f)
}
}
}
}
}
}
override fun showSuccess(onEnd: () -> Unit) {
currentStep = STEP_SUCCESS
cardsWidget.leapfrogWidget.fold(onEnd = onEnd)
}
private enum class State {
TreeCards,
TwoCards
}
private fun BackupCardType.animateAlpha(
alpha: Float,
duration: Long = 0L,
startDelay: Long = 0L,
) {
when (this) {
ORIGIN -> {
cardsWidget.getOriginCardView().animate().apply {
alpha(alpha)
this.startDelay = startDelay
this.duration = duration
}
}
FIRST_BACKUP -> {
cardsWidget.getFirstBackupCardView().animate().apply {
alpha(alpha)
this.startDelay = startDelay
this.duration = duration
}
}
SECOND_BACKUP -> {
if (viewState != State.TreeCards) return
cardsWidget.getSecondBackupCardView().animate().apply {
alpha(alpha)
this.startDelay = startDelay
this.duration = duration
}
}
}
}
private fun BackupCardType.alpha(alpha: Float) {
when (this) {
ORIGIN -> cardsWidget.getOriginCardView().alpha = alpha
FIRST_BACKUP -> cardsWidget.getFirstBackupCardView().alpha = alpha
SECOND_BACKUP -> {
if (viewState != State.TreeCards) return
cardsWidget.getSecondBackupCardView().alpha = alpha
}
}
}
private fun BackupCardType.show() {
when (this) {
ORIGIN -> cardsWidget.getOriginCardView().show()
FIRST_BACKUP -> cardsWidget.getFirstBackupCardView().show()
SECOND_BACKUP -> {
if (viewState != State.TreeCards) return
cardsWidget.getSecondBackupCardView().show()
}
}
}
companion object {
private const val STEP_CREATE_WALLET = 0
private const val STEP_BACKUP_INTRO = 1
private const val STEP_SCAN_ORIGIN_CARD = 2
private const val STEP_ADD_BACKUP_CARDS = 3
private const val STEP_WRITE_PRIMARY_CARD = 4
private const val STEP_WRITE_BACKUP_CARD = 5
private const val STEP_SUCCESS = 6
}
}
class TestBackupAnimation(
private val animator: BackupAnimator,
binding: FragmentOnboardingWalletBinding,
) : BackupAnimator {
private lateinit var state: BackupState
private var steps = 0
init {
init(binding, this)
}
fun setStep(step: Int, onStepUpdate: (Int) -> Unit = {}) {
steps = step
when (steps) {
0 -> setupCreateWalletState()
1 -> showBackupIntro(state)
2 -> showScanOriginCard()
3 -> showAddBackupCards(state, 0)
4 -> showAddBackupCards(state, 1)
5 -> showAddBackupCards(state, 2)
6 -> showWritePrimaryCard(state)
7 -> showWriteBackupCard(state, 1)
8 -> showWriteBackupCard(state, 2)
else -> setStep(0)
}
onStepUpdate(steps)
}
fun onNext(onStepUpdate: (Int) -> Unit) {
steps++
setStep(steps, onStepUpdate)
}
override fun updateBackupState(backupState: BackupState) {
this.state = backupState
}
override fun setupCreateWalletState() {
animator.setupCreateWalletState()
}
override fun showBackupIntro(state: BackupState) {
animator.showBackupIntro(state)
}
override fun showScanOriginCard() {
animator.showScanOriginCard()
}
override fun showAddBackupCards(state: BackupState, backupCardsCounts: Int) {
animator.showAddBackupCards(state, backupCardsCounts)
}
override fun showWritePrimaryCard(state: BackupState) {
animator.showWritePrimaryCard(state)
}
override fun showWriteBackupCard(state: BackupState, backupCard: Int) {
animator.showWriteBackupCard(state, backupCard)
}
override fun showSuccess(onEnd: () -> Unit) {
animator.showSuccess(onEnd)
}
companion object {
fun init(binding: FragmentOnboardingWalletBinding, animator: TestBackupAnimation) {
val button = MaterialButton(binding.onboardingRoot.context).apply { text = "Next" }
button.setOnClickListener { animator.onNext { button.text = "$it" } }
binding.onboardingRoot.addView(button, 0)
}
}
}

View file

@ -5,6 +5,7 @@ import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.ProgressBar
import androidx.appcompat.app.AppCompatActivity
@ -30,6 +31,7 @@ import com.tangem.tap.common.extensions.configureSettings
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.inflate
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.stop
import com.tangem.tap.common.feedback.SupportInfo
@ -63,7 +65,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
StoreSubscriber<OnboardingWalletState>, FragmentOnBackPressedHandler {
private val pbBinding: ViewOnboardingProgressBinding by viewBinding(ViewOnboardingProgressBinding::bind)
private val binding: FragmentOnboardingWalletBinding by viewBinding(FragmentOnboardingWalletBinding::bind)
val binding: FragmentOnboardingWalletBinding by viewBinding(FragmentOnboardingWalletBinding::bind)
private val bindingSaltPay: LayoutOnboardingSaltpayBinding by lazy { binding.onboardingSaltpayContainer }
private val saltPayView: SaltPayView = SaltPayView(this)
@ -71,9 +73,10 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
private lateinit var cardsWidget: WalletCardsWidget
private var accessCodeDialog: AccessCodeDialog? = null
private lateinit var animator: BackupAnimator
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postponeEnterTransition()
setHasOptionsMenu(true)
val inflater = TransitionInflater.from(requireContext())
@ -84,16 +87,8 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val typedValue = TypedValue()
resources.getValue(R.dimen.device_scale_factor_for_twins_welcome, typedValue, true)
val deviceScaleFactor = typedValue.float
val leapfrogCalculator = PropertyCalculator(
yTranslationFactor = 25f * deviceScaleFactor,
)
val leapfrog = LeapfrogWidget(binding.flCardsContainer, leapfrogCalculator)
cardsWidget = WalletCardsWidget(leapfrog, deviceScaleFactor) { 200f * deviceScaleFactor }
startPostponedEnterTransition()
val scaleFactor = getDeviceScaleFactor()
initCardsWidget(createLeapfrogWidget(binding.flCardsContainer, scaleFactor), scaleFactor)
binding.viewPagerBackupInfo.adapter = BackupInfoAdapter()
TabLayoutMediator(
@ -113,6 +108,37 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
store.dispatch(OnboardingWalletAction.LoadArtwork)
}
private fun reInitCardsWidget(backupCardsCounts: Int) = with(binding) {
val viewBackupCount = flCardsContainer.childCount - 1
if (viewBackupCount <= 0) return@with
if (viewBackupCount == backupCardsCounts) return@with
cardsWidget.toFolded(false)
if (viewBackupCount > backupCardsCounts) {
flCardsContainer.removeViewAt(viewBackupCount - 1)
} else {
flCardsContainer.inflate(R.layout.view_onboarding_card, true)
}
val scaleFactor = getDeviceScaleFactor()
initCardsWidget(createLeapfrogWidget(flCardsContainer, scaleFactor), scaleFactor)
}
private fun createLeapfrogWidget(container: FrameLayout, deviceScaleFactor: Float): LeapfrogWidget {
val leapfrogCalculator = PropertyCalculator(yTranslationFactor = 25f * deviceScaleFactor)
return LeapfrogWidget(container, leapfrogCalculator)
}
private fun initCardsWidget(leapfrogWidget: LeapfrogWidget, deviceScaleFactor: Float, isTest: Boolean = false) {
cardsWidget = WalletCardsWidget(leapfrogWidget, deviceScaleFactor) { 200f * deviceScaleFactor }
animator = if (isTest) {
TestBackupAnimation(WalletBackupAnimator(cardsWidget), binding)
} else {
WalletBackupAnimator(cardsWidget)
}
}
override fun onStart() {
super.onStart()
store.subscribe(this) { state ->
@ -129,7 +155,9 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
override fun newState(state: OnboardingWalletState) {
if (activity == null || view == null) return
if (state.isSaltPay) reInitCardsWidget(1)
animator.updateBackupState(state.backupState)
requireActivity().invalidateOptionsMenu()
pbBinding.pbState.max = state.getMaxProgress()
@ -171,7 +199,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
tvHeader.setText(R.string.onboarding_create_wallet_header)
tvBody.setText(R.string.onboarding_create_wallet_body)
cardsWidget.toFolded(false) { startPostponedEnterTransition() }
animator.setupCreateWalletState()
}
private fun setBackupState(state: BackupState, isSaltPay: Boolean) {
@ -192,8 +220,6 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
imvFirstBackupCard.show()
imvSecondBackupCard.show()
cardsWidget.toWelcome()
tvHeader.hide()
tvBody.hide()
viewPagerBackupInfo.show()
@ -207,15 +233,11 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
btnAlternativeAction.setOnClickListener { store.dispatch(BackupAction.DismissBackup) }
btnAlternativeAction.show(state.canSkipBackup && !isSaltPay)
}
startPostponedEnterTransition()
animator.showBackupIntro(state)
}
private fun showScanOriginCard() = with(binding) {
prepareBackupView()
cardsWidget.toFolded()
tvHeader.text = getText(R.string.onboarding_title_scan_origin_card)
tvBody.text = getString(
R.string.onboarding_subtitle_scan_origin_card,
@ -226,18 +248,8 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
btnAlternativeAction.hide()
btnMainAction.setOnClickListener { store.dispatch(BackupAction.ScanPrimaryCard) }
}
}
private fun prepareBackupView() = with(binding) {
toolbar.title = getText(R.string.onboarding_navbar_title_creating_backup)
tvHeader.show()
tvBody.show()
viewPagerBackupInfo.hide()
tabLayoutBackupInfo.hide()
imvFirstBackupCard.show()
imvSecondBackupCard.show()
animator.showScanOriginCard()
}
private fun showAddBackupCards(state: BackupState, isSaltPay: Boolean) = with(binding) {
@ -261,35 +273,37 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
if (isSaltPay) {
saltPayView.showAddBackupCards(state)
return@with
}
when (state.backupCardsNumber) {
0 -> {
cardsWidget.toFan {
cardsWidget.getFirstBackupCardView().animate().alpha(0.6f).duration = 200
cardsWidget.getSecondBackupCardView().animate().alpha(0.2f).duration = 200
} else {
when (state.backupCardsNumber) {
0 -> {
tvHeader.text = getText(R.string.onboarding_title_no_backup_cards)
tvBody.text = getText(R.string.onboarding_subtitle_no_backup_cards)
}
tvHeader.text = getText(R.string.onboarding_title_no_backup_cards)
tvBody.text = getText(R.string.onboarding_subtitle_no_backup_cards)
}
1 -> {
tvHeader.text = getText(R.string.onboarding_title_one_backup_card)
tvBody.text = getText(R.string.onboarding_subtitle_one_backup_card)
cardsWidget.toFan(false)
cardsWidget.getFirstBackupCardView().animate().alpha(1f).duration = 400
cardsWidget.getSecondBackupCardView().alpha = 0.2f
}
2 -> {
tvHeader.text = getText(R.string.onboarding_title_two_backup_cards)
tvBody.text = getText(R.string.onboarding_subtitle_two_backup_cards)
cardsWidget.toFan(false)
cardsWidget.getFirstBackupCardView().alpha = 1f
cardsWidget.getSecondBackupCardView().animate().alpha(1f).duration = 400
1 -> {
tvHeader.text = getText(R.string.onboarding_title_one_backup_card)
tvBody.text = getText(R.string.onboarding_subtitle_one_backup_card)
}
2 -> {
tvHeader.text = getText(R.string.onboarding_title_two_backup_cards)
tvBody.text = getText(R.string.onboarding_subtitle_two_backup_cards)
}
else -> {}
}
}
animator.showAddBackupCards(state, state.backupCardsNumber)
}
private fun prepareBackupView() = with(binding) {
toolbar.title = getText(R.string.onboarding_navbar_title_creating_backup)
tvHeader.show()
tvBody.show()
viewPagerBackupInfo.hide()
tabLayoutBackupInfo.hide()
imvFirstBackupCard.show()
imvSecondBackupCard.show()
}
private fun showSetAccessCode() {
@ -323,13 +337,8 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
private fun showWritePrimaryCard(state: BackupState, isSaltPay: Boolean) = with(binding) {
accessCodeDialog?.dismiss()
reInitCardsWidget(state.backupCardsNumber)
prepareViewForFinalizeStep()
cardsWidget.getSecondBackupCardView().show(state.backupCardsNumber == 2)
cardsWidget.toLeapfrog {
cardsWidget.getFirstBackupCardView().alpha = 0.6f
cardsWidget.getSecondBackupCardView().alpha = 0.2f
}
val cardIdFormatter = CardIdFormatter(CardIdDisplayFormat.LastMasked(4))
if (isSaltPay) {
@ -344,52 +353,33 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
)
layoutButtonsCommon.btnMainAction.text = getText(R.string.onboarding_button_backup_origin)
}
layoutButtonsCommon.btnMainAction.setOnClickListener { store.dispatch(BackupAction.WritePrimaryCard) }
animator.showWritePrimaryCard(state)
}
private fun prepareViewForFinalizeStep() = with(binding) {
layoutButtonsAddCards.root.hide()
layoutButtonsCommon.root.show()
imvFirstBackupCard.show()
imvSecondBackupCard.show()
toolbar.title = getText(R.string.onboarding_button_finalize_backup)
imvCardBackground.hide()
cardsWidget.toLeapfrog()
layoutButtonsAddCards.root.hide()
layoutButtonsCommon.root.show()
layoutButtonsCommon.btnAlternativeAction.hide()
imvCardBackground.hide()
imvFirstBackupCard.show()
imvSecondBackupCard.show()
}
private fun showWriteBackupCard(state: BackupState, isSaltPay: Boolean) = with(binding) {
prepareViewForFinalizeStep()
cardsWidget.getSecondBackupCardView().show(state.backupCardsNumber == 2)
val cardNumber = (state.backupStep as? BackupStep.WriteBackupCard)?.cardNumber ?: 1
when (cardNumber) {
1 -> cardsWidget.leapfrogWidget.leap {
cardsWidget.getOriginCardView().alpha = 0.4f
cardsWidget.getFirstBackupCardView().alpha = 0.2f
cardsWidget.getSecondBackupCardView().alpha = 1f
}
2 -> cardsWidget.leapfrogWidget.leap {
cardsWidget.getOriginCardView().alpha = 0.4f
cardsWidget.getFirstBackupCardView().alpha = 0.2f
cardsWidget.getSecondBackupCardView().alpha = 1f
}
}
val cardIdFormatter = CardIdFormatter(CardIdDisplayFormat.LastMasked(4))
if (isSaltPay) {
tvHeader.text = getString(R.string.onboarding_saltpay_title_backup_card)
tvBody.text = getString(R.string.onboarding_twins_interrupt_warning)
layoutButtonsCommon.btnMainAction.text = getString(R.string.onboarding_saltpay_title_backup_card)
} else {
val cardIdFormatter = CardIdFormatter(CardIdDisplayFormat.LastMasked(4))
tvHeader.text = getString(R.string.onboarding_title_backup_card_format, cardNumber)
tvBody.text = getString(
R.string.onboarding_subtitle_scan_backup_card_format,
@ -403,6 +393,8 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
layoutButtonsCommon.btnMainAction.setOnClickListener {
store.dispatch(BackupAction.WriteBackupCard(cardNumber))
}
animator.showWriteBackupCard(state, cardNumber)
}
private fun showSuccess() = with(binding) {
@ -423,16 +415,14 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
store.dispatch(OnboardingWalletAction.FinishOnboarding)
}
cardsWidget.leapfrogWidget.fold {
animator.showSuccess {
flCardsContainer.hide()
imvCardBackground.hide()
vConfetti.lavConfetti.show()
vConfetti.lavConfetti.playAnimation()
imvSuccess.alpha = 0f
imvSuccess.show()
imvSuccess.animate()
?.alpha(1f)?.duration = 400
imvSuccess.animate()?.alpha(1f)?.duration = 400
}
}
@ -450,6 +440,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
fun newState(state: OnboardingWalletState) {
handleCardArtworks(state)
when (state.step) {
OnboardingWalletStep.SaltPay -> setSaltPayStep(state.onboardingSaltPayState)
OnboardingWalletStep.Backup -> {
@ -489,24 +480,20 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
//TODO: at now we can hide the image only by changing alpha channel to 0, because
// the OnboardingWalletFragment and WalletCardsWidget manipulate it visibility through changing
// View.VISIBILITY states.
imvSecondBackupCard.alpha = 0f
// imvSecondBackupCard.alpha = 0f
}
fun showAddBackupCards(state: BackupState) = with(walletFragment.binding) {
walletFragment.cardsWidget.getSecondBackupCardView().alpha = 0f
when (state.backupCardsNumber) {
0 -> {
tvHeader.text = walletFragment.getText(R.string.onboarding_saltpay_title_no_backup_card)
tvBody.text = walletFragment.getText(R.string.onboarding_saltpay_subtitle_no_backup_cards)
walletFragment.cardsWidget.getFirstBackupCardView().alpha = 0.6f
walletFragment.cardsWidget.toFan()
}
1 -> {
tvHeader.text = walletFragment.getText(R.string.onboarding_saltpay_title_one_backup_card)
tvBody.text = walletFragment.getText(R.string.onboarding_saltpay_subtitle_one_backup_card)
walletFragment.cardsWidget.toFan(false)
walletFragment.cardsWidget.getFirstBackupCardView().animate().alpha(1f).duration = 400
}
else -> {}
}
}
@ -614,7 +601,6 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
}
private fun showOnlyView(view: View, onShowListener: (() -> Unit)? = null) {
// onboardingSaltpayContainer.beginDelayedTransition()
walletFragment.bindingSaltPay.onboardingSaltpayContainer.children
.filter { it.id != view.id }
.forEach { it.hide() }
@ -639,4 +625,10 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet),
}
}
}
}
private fun getDeviceScaleFactor(): Float {
val typedValue = TypedValue()
resources.getValue(R.dimen.device_scale_factor_for_twins_welcome, typedValue, true)
return typedValue.float
}
}

View file

@ -16,87 +16,100 @@ class WalletCardsWidget(
val getTopOfAnchorViewForActivateState: () -> Float,
) {
val animDuration: Long = 400
private val animDuration: Long = 400
var currentState: WidgetState? = null
private set
fun toWelcome(animate: Boolean = true, onEnd: () -> Unit = {}) {
fun toWelcome(
animate: Boolean = true,
onEnd: () -> Unit = {},
) {
if (currentState == WidgetState.WELCOME) return
currentState = WidgetState.WELCOME
val animator = createAnimator(animate, onEnd)
animator.playTogether(
createAnimator(BackupCardType.ORIGIN, createWelcomeProperties(BackupCardType.ORIGIN)),
createAnimator(BackupCardType.FIRST_BACKUP, createWelcomeProperties(BackupCardType.FIRST_BACKUP)),
createAnimator(BackupCardType.SECOND_BACKUP, createWelcomeProperties(BackupCardType.SECOND_BACKUP)),
)
val animator = createAnimator(animate, onEnd = onEnd)
animator.playTogether(createAnimators(::createWelcomeProperties))
leapfrogWidget.fold { animator.start() }
}
fun toFolded(animate: Boolean = true, onEnd: () -> Unit = {}) {
fun toFolded(
animate: Boolean = true,
duration: Long = animDuration,
onEnd: () -> Unit = {},
) {
if (currentState == WidgetState.FOLDED) return
currentState = WidgetState.FOLDED
val animator = createAnimator(animate, onEnd)
animator.playTogether(
createAnimator(BackupCardType.ORIGIN, createLeapfrogProperties(BackupCardType.ORIGIN)),
createAnimator(BackupCardType.FIRST_BACKUP, createLeapfrogProperties(BackupCardType.FIRST_BACKUP)),
createAnimator(BackupCardType.SECOND_BACKUP, createLeapfrogProperties(BackupCardType.SECOND_BACKUP)),
)
leapfrogWidget.fold(animate) { animator.start() }
val animator = createAnimator(animate, duration) {
leapfrogWidget.fold(animate, onEnd)
}
animator.playTogether(createAnimators(::createLeapfrogProperties))
animator.start()
}
fun toFan(animate: Boolean = true, onEnd: () -> Unit = {}) {
fun toFan(
animate: Boolean = true,
onEnd: () -> Unit = {},
) {
if (currentState == WidgetState.FAN) return
currentState = WidgetState.FAN
val animator = createAnimator(animate, onEnd)
animator.playTogether(
createAnimator(BackupCardType.ORIGIN, createFanProperties(BackupCardType.ORIGIN)),
createAnimator(BackupCardType.FIRST_BACKUP, createFanProperties(BackupCardType.FIRST_BACKUP)),
createAnimator(BackupCardType.SECOND_BACKUP, createFanProperties(BackupCardType.SECOND_BACKUP)),
)
val animator = createAnimator(animate, onEnd = onEnd)
animator.playTogether(createAnimators(::createFanProperties))
leapfrogWidget.fold { animator.start() }
}
fun toLeapfrog(animate: Boolean = true, onEnd: () -> Unit = {}) {
fun toLeapfrog(
animate: Boolean = true,
onEndFold: () -> Unit = {},
onEnd: () -> Unit = {},
) {
if (currentState == WidgetState.LEAPFROG) return
currentState = WidgetState.LEAPFROG
val animator = createAnimator(animate, onEnd)
animator.playTogether(
createAnimator(BackupCardType.ORIGIN, createLeapfrogProperties(BackupCardType.ORIGIN)),
createAnimator(BackupCardType.FIRST_BACKUP, createLeapfrogProperties(BackupCardType.FIRST_BACKUP)),
createAnimator(BackupCardType.SECOND_BACKUP, createLeapfrogProperties(BackupCardType.SECOND_BACKUP)),
)
leapfrogWidget.fold {
animator.doOnEnd {
val onAnimatorEnd = {
leapfrogWidget.fold(animate) {
leapfrogWidget.initViews()
leapfrogWidget.unfold()
onEndFold()
leapfrogWidget.unfold(animate, onEnd)
}
animator.start()
}
val animator = createAnimator(animate, onEnd = onAnimatorEnd)
animator.playTogether(createAnimators(::createLeapfrogProperties))
animator.start()
}
private fun createAnimator(animate: Boolean, onEnd: () -> Unit): AnimatorSet {
private fun createAnimators(propertyFactory: (BackupCardType) -> CardProperties): List<ObjectAnimator> {
return getCardTypesTypes().map { createCardAnimator(it, propertyFactory(it)) }
}
private fun getCardTypesTypes(): List<BackupCardType> = when (leapfrogWidget.getViewsCount()) {
2 -> listOf(BackupCardType.ORIGIN, BackupCardType.FIRST_BACKUP)
3 -> listOf(BackupCardType.ORIGIN, BackupCardType.FIRST_BACKUP, BackupCardType.SECOND_BACKUP)
else -> throw UnsupportedOperationException()
}
private fun createAnimator(animate: Boolean, duration: Long = animDuration, onEnd: () -> Unit): AnimatorSet {
return AnimatorSet().apply {
duration = if (animate) animDuration else 0
this.duration = if (animate) duration else 0
doOnEnd { onEnd() }
}
}
private fun createAnimator(
private fun createCardAnimator(
cardType: BackupCardType,
properties: CardProperties,
): ObjectAnimator {
val view = getLeapViewByCardNumber(cardType).view
val animator =
ObjectAnimator.ofPropertyValuesHolder(
view,
*properties.createValuesHolders().toTypedArray(),
)
val view = getLeapViewByCard(cardType).view
val animator = ObjectAnimator.ofPropertyValuesHolder(
view,
*properties.createValuesHolders().toTypedArray(),
)
view.elevation = properties.elevation
return animator
}
@ -181,7 +194,7 @@ class WalletCardsWidget(
private fun createActivateProperties(cardType: BackupCardType): CardProperties {
val topOfAnchorView = getTopOfAnchorViewForActivateState()
val twinProperties = CardProperties.from(getLeapViewByCardNumber(cardType).state)
val twinProperties = CardProperties.from(getLeapViewByCard(cardType).state)
return twinProperties.copy(
xTranslation = twinProperties.xTranslation,
yTranslation = twinProperties.yTranslation - topOfAnchorView,
@ -190,24 +203,40 @@ class WalletCardsWidget(
)
}
private fun getLeapViewByCardNumber(cardType: BackupCardType): LeapView {
return when (cardType) {
BackupCardType.ORIGIN -> leapfrogWidget.getViewByPosition(0)
BackupCardType.FIRST_BACKUP -> leapfrogWidget.getViewByPosition(1)
BackupCardType.SECOND_BACKUP -> leapfrogWidget.getViewByPosition(2)
}
}
@Throws(UnsupportedOperationException::class)
fun getOriginCardView(): ImageView {
return getLeapViewByCardNumber(BackupCardType.ORIGIN).view as ImageView
return getLeapViewByCard(BackupCardType.ORIGIN).view as ImageView
}
@Throws(UnsupportedOperationException::class)
fun getFirstBackupCardView(): ImageView {
return getLeapViewByCardNumber(BackupCardType.FIRST_BACKUP).view as ImageView
return getLeapViewByCard(BackupCardType.FIRST_BACKUP).view as ImageView
}
@Throws(UnsupportedOperationException::class)
fun getSecondBackupCardView(): ImageView {
return getLeapViewByCardNumber(BackupCardType.SECOND_BACKUP).view as ImageView
return getLeapViewByCard(BackupCardType.SECOND_BACKUP).view as ImageView
}
@Throws(UnsupportedOperationException::class)
private fun getLeapViewByCard(cardType: BackupCardType): LeapView {
fun getByIndex(index: Int): LeapView {
return leapfrogWidget.getViewByPosition(leapfrogWidget.getViewPositionByIndex(index))
}
return when (leapfrogWidget.getViewsCount()) {
2 -> when (cardType) {
BackupCardType.ORIGIN -> getByIndex(1)
BackupCardType.FIRST_BACKUP -> getByIndex(0)
BackupCardType.SECOND_BACKUP -> throw UnsupportedOperationException()
}
3 -> when (cardType) {
BackupCardType.ORIGIN -> getByIndex(2)
BackupCardType.FIRST_BACKUP -> getByIndex(1)
BackupCardType.SECOND_BACKUP -> getByIndex(0)
}
else -> throw UnsupportedOperationException()
}
}
enum class WidgetState { WELCOME, FOLDED, FAN, LEAPFROG }

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.AppCompatImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:scaleType="fitCenter"
android:src="@drawable/card_placeholder_black"
android:transitionName="imv_front_card" />