From a74efa295197c29cf8b32c94c3987f93fd4f53cb Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 20 Oct 2022 00:20:17 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../onboarding/OnboardingSaltPayHelper.kt | 11 + .../products/wallet/ui/BackupAnimator.kt | 292 ++++++++++++++++++ .../wallet/ui/OnboardingWalletFragment.kt | 196 ++++++------ .../products/wallet/ui/WalletCardsWidget.kt | 139 +++++---- .../main/res/layout/view_onboarding_card.xml | 10 + 5 files changed, 491 insertions(+), 157 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupAnimator.kt create mode 100644 app/src/main/res/layout/view_onboarding_card.xml diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingSaltPayHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingSaltPayHelper.kt index 8b56bbc65a..d874af4acc 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingSaltPayHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingSaltPayHelper.kt @@ -30,5 +30,16 @@ class OnboardingSaltPayHelper { Result.Failure(error) } } + + suspend fun testProceedToOnboarding(scanResponse: ScanResponse): Result { + return try { + val isBackupOnboardingCase = scanResponse.card.backupStatus?.isActive == false + Result.Success(isBackupOnboardingCase) + } catch (ex: Exception) { + Result.Failure(ex) + } catch (error: SaltPayRegistrationError){ + Result.Failure(error) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupAnimator.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupAnimator.kt new file mode 100644 index 0000000000..dd980fc2cd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupAnimator.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index 8a444373b5..2b22f898d7 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -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, 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), } } } -} \ No newline at end of file + + private fun getDeviceScaleFactor(): Float { + val typedValue = TypedValue() + resources.getValue(R.dimen.device_scale_factor_for_twins_welcome, typedValue, true) + return typedValue.float + } +} diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt index 833c3dfb81..7d4831c434 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt @@ -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 { + return getCardTypesTypes().map { createCardAnimator(it, propertyFactory(it)) } + } + + private fun getCardTypesTypes(): List = 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 } diff --git a/app/src/main/res/layout/view_onboarding_card.xml b/app/src/main/res/layout/view_onboarding_card.xml new file mode 100644 index 0000000000..e3086ff628 --- /dev/null +++ b/app/src/main/res/layout/view_onboarding_card.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file