diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 6f99403572..a7307e4e74 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index a604cbd643..b3a0c345e2 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -7,8 +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.twins.redux.TwinCardsAction -import com.tangem.tap.features.twins.ui.dialog.CreateWalletInterruptDialog +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 @@ -45,7 +45,7 @@ class DialogManager : StoreSubscriber { dialog = when (state.dialog) { is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context) is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context) - is TwinCardsAction.Wallet.InterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context) + is TwinCardsAction.Wallet.ShowInterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context) is WalletConnectDialog.UnsupportedCard -> SimpleAlertDialog.create( titleRes = R.string.wallet_connect, diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 135e0cbd9c..f96f16f5ee 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -16,12 +16,10 @@ 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.OnboardingTwinsFragment +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.twins.ui.CreateTwinWalletFragment -import com.tangem.tap.features.twins.ui.TwinWalletWarningFragment import com.tangem.tap.features.wallet.ui.WalletDetailsFragment import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.wallet.R @@ -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) } } } @@ -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() diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt index 884cef3f94..e83f7b8b92 100644 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt @@ -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() @@ -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() + 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() 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() 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 ) 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 diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index ed4dbe69aa..d797ff9010 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -8,10 +8,10 @@ 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 -import com.tangem.tap.features.twins.redux.TwinCardsReducer import com.tangem.tap.features.wallet.redux.reducers.WalletReducer import org.rekotlin.Action @@ -20,19 +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), - 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) + 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) ) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 98827674a0..39b221f517 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -16,14 +16,14 @@ 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 import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.redux.TokensMiddleware import com.tangem.tap.features.tokens.redux.TokensState -import com.tangem.tap.features.twins.redux.TwinCardsMiddleware -import com.tangem.tap.features.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.middlewares.WalletMiddleware import org.rekotlin.Middleware diff --git a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt index 36e1a749a5..4334d55f59 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt @@ -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() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/FragmentShareTransition.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/FragmentShareTransition.kt new file mode 100644 index 0000000000..69547d020f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/FragmentShareTransition.kt @@ -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, + 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 + 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" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt index 331dc07e1a..d2147ad3c6 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt @@ -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) : NavigationAction() object ActivityDestroyed : NavigationAction() -} - -data class FragmentShareTransition( - val shareElements: List, - val enterTransitionSet: TransitionSet, - val exitTransitionSet: TransitionSet -) - -data class ShareElement(val wView: WeakReference, val name: String) \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt index 4fa02150f0..61e3d263da 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt @@ -5,8 +5,8 @@ import org.rekotlin.StateType import java.lang.ref.WeakReference data class NavigationState( - val backStack: List = listOf(AppScreen.Home), - val activity: WeakReference? = null + val backStack: List = listOf(AppScreen.Home), + val activity: WeakReference? = null ) : StateType enum class AppScreen { @@ -16,7 +16,6 @@ enum class AppScreen { Wallet, WalletDetails, Send, Details, DetailsConfirm, DetailsSecurity, - CreateTwinWalletWarning, CreateTwinWallet, AddTokens, WalletConnectSessions, QrScan diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt index e08ba924b5..33aa1e1b4d 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt @@ -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) } } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt deleted file mode 100644 index 22a7e5ca87..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.tangem.tap.domain.tasks - -import com.tangem.* -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.TwinsHelper - -@Deprecated("Use ScanProductTask instead") -class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { - - override fun run( - session: CardSession, - callback: (result: CompletionResult) -> 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 (TwinsHelper.getTwinCardNumber(card.cardId) != null) { - dealWithTwinCard(card, session, callback) - } else if (!isTangemNote(card) && 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) -> 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) -> 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 = TwinsHelper.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 multi-currency HD wallet cards on the old version of the app -// if (card.isMultiCurrencyWallet()) return UpdateAppToUseThisCard() - return null - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt index 0024a37cd8..e16ccd79cb 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt @@ -4,44 +4,45 @@ 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.operations.wallet.CreateWalletResponse import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler 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, assetReader: AssetReader) { +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(assetReader, scanResponse.card.issuer.publicKey.toHexString()) + private val issuerKeyPair: KeyPair = getIssuerKeys(assetReader, card.issuer.publicKey.toHexString()) - suspend fun createFirstWallet(message: Message): SimpleResult { + suspend fun createFirstWallet(message: Message): CompletionResult { val response = tangemSdkManager.runTaskAsync( CreateFirstTwinWalletTask(), currentCardId, message ) when (response) { is CompletionResult.Success -> { currentCardPublicKey = response.data.wallet.publicKey.toHexString() - return SimpleResult.Success + return response } 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 } } @@ -52,7 +53,7 @@ class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: Asse initialMessage: Message, preparingMessage: Message, creatingWalletMessage: Message, - ): SimpleResult { + ): CompletionResult { val task = CreateSecondTwinWalletTask( firstPublicKey = currentCardPublicKey!!, firstCardId = currentCardId, @@ -64,20 +65,18 @@ class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: Asse 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 { @@ -92,7 +91,7 @@ class TwinCardsManager(private val scanResponse: ScanResponse, assetReader: Asse FirebaseAnalyticsHandler.logCardSdkError( error, FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData, - card = scanResponse.card + card = card ) } Result.failure(response.error) diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt index 63f891eb4d..c8f3c02952 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt @@ -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, diff --git a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/BaseFragment.kt similarity index 56% rename from app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt rename to app/src/main/java/com/tangem/tap/features/BaseFragment.kt index bb3046db5e..8e1c2e098a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/BaseFragment.kt @@ -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>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -40,26 +40,32 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - toolbar?.setNavigationOnClickListener { activity?.onBackPressed() } + addBackPressHandler(this) } - override fun onStart() { - super.onStart() - subscribeToStore() + override fun handleOnBackPressed() { + store.dispatch(NavigationAction.PopBackTo()) } - - 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(R.id.toolbar)?.setNavigationOnClickListener { activity?.onBackPressed() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/BaseStoreFragment.kt new file mode 100644 index 0000000000..b4f486a1a2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/BaseStoreFragment.kt @@ -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>() + + override fun onStart() { + super.onStart() + subscribeToStore() + } + + override fun onStop() { + storeSubscribersList.forEach { store.unsubscribe(it) } + storeSubscribersList.clear() + super.onStop() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 9192ef3043..22bbd11511 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -11,8 +11,8 @@ 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.disclaimer.redux.DisclaimerAction -import com.tangem.tap.features.onboarding.twins.redux.CreateTwinWalletMode -import com.tangem.tap.features.onboarding.twins.redux.TwinCardsAction +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 diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 271f51cc97..40f409c680 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -6,7 +6,7 @@ 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.onboarding.twins.redux.TwinCardsState +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 diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 31d11dbc31..cb51e326dd 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -6,7 +6,6 @@ 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 @@ -16,8 +15,6 @@ 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.feedback.FeedbackEmail -import com.tangem.tap.features.twins.redux.CreateTwinWalletMode -import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_details.* @@ -91,17 +88,10 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber() { 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() { 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() { oldState.homeState == newState.homeState }.select { it.homeState } } + storeSubscribersList.add(this) } override fun newState(state: HomeState) { diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 4de7177c34..bd5cdcffff 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -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 @@ -10,10 +12,10 @@ import com.tangem.tap.common.redux.navigation.AppScreen 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.domain.twins.getTwinCardNumber 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.features.twins.redux.TwinCardsAction import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store @@ -58,21 +60,24 @@ private fun handleReadCard() { } else { changeButtonState(ButtonState.PROGRESS) store.dispatch(GlobalAction.ScanCard({ scanResponse -> - if (scanResponse.isTangemTwins()) { - scanResponse.card.getTwinCardNumber()?.let { - val configManager = store.state.globalState.configManager - val isCreatingTwinsAllowed = configManager?.config?.isCreatingTwinCardsAllowed ?: false - store.dispatch(TwinCardsAction.SetTwinCard(it, null, isCreatingTwinsAllowed)) - } - } + 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) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt index 7cdbff7733..b33b3118f5 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt @@ -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 : BaseStoreFragment(R.layout.fragment_onboarding_main), StoreSubscriber { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - configureTransitions() - } - override fun configureTransitions() { enterTransition = HomeToOnboardingTransition() exitTransition = HomeToOnboardingTransition() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index 173662c65e..80b671393f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -16,18 +16,18 @@ 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.onboarding.products.BaseOnboardingFragment import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction -import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteResources 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.* @@ -36,23 +36,26 @@ import kotlinx.android.synthetic.main.view_onboarding_tv_balance.* */ class OnboardingNoteFragment : BaseOnboardingFragment() { - 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) + imv_front_card.transitionName = ShareElement.imvFrontCard + startPostponedEnterTransition() + toolbar.setTitle(R.string.onboarding_title) btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container) - val resources = OnboardingNoteResources( - OnboardingNoteResources.Strings( - R.string.copy_toast_msg - ) - ) - store.dispatch(OnboardingNoteAction.SetResources(resources)) store.dispatch(OnboardingNoteAction.LoadCardArtwork) store.dispatch(OnboardingNoteAction.DetermineStepOfScreen) } @@ -81,7 +84,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { } setBalance(state) showConfetti(state.showConfetti) - currentStep = state.currentStep + previousStep = state.currentStep } private fun setBalance(state: OnboardingNoteState) { @@ -166,7 +169,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { } 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) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt index 859e1104fa..73b588ee69 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -10,7 +10,6 @@ import org.rekotlin.Action */ sealed class OnboardingNoteAction : Action { // from user, ui - data class SetResources(val resources: OnboardingNoteResources) : OnboardingNoteAction() object LoadCardArtwork : OnboardingNoteAction() object CreateWallet : OnboardingNoteAction() object TopUp : OnboardingNoteAction() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 8267514eb0..0ef12f9c8f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -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 @@ -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 @@ -128,7 +130,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 +145,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)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt index c88906cb20..206a4893ab 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -23,9 +23,6 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt 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) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt index 33c5cbdd7d..23def7368f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -10,7 +10,6 @@ import org.rekotlin.StateType */ data class OnboardingNoteState( val walletManager: WalletManager? = null, - val resources: OnboardingNoteResources = OnboardingNoteResources(), // UI val cardArtwork: Artwork? = null, val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(), @@ -25,12 +24,4 @@ data class OnboardingNoteState( enum class OnboardingNoteStep { None, CreateWallet, TopUpWallet, Done -} - -data class OnboardingNoteResources( - val strings: Strings = Strings() -) { - data class Strings( - val addressWasCopied: Int = -1 - ) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt index abfb32f6d5..f57e92fe03 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt @@ -8,16 +8,18 @@ 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.onboarding.products.BaseOnboardingFragment +import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_onboarding_main.* -import kotlinx.android.synthetic.main.layout_onboarding_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.* /** @@ -25,11 +27,19 @@ import kotlinx.android.synthetic.main.view_onboarding_progress.* */ class OnboardingOtherCardsFragment : BaseOnboardingFragment() { - 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) + imv_front_card.transitionName = ShareElement.imvFrontCard + startPostponedEnterTransition() + toolbar.setTitle(R.string.onboarding_title) store.dispatch(OnboardingOtherCardsAction.LoadCardArtwork) store.dispatch(OnboardingOtherCardsAction.DetermineStepOfScreen) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index 6fea776c74..fd2dbee87d 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -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 @@ -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)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt deleted file mode 100644 index 684d05324e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/OnboardingTwinsFragment.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.tangem.tap.features.onboarding.products.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.extensions.dispatchDebugErrorNotification -import com.tangem.tap.common.extensions.withMainContext -import com.tangem.tap.common.redux.navigation.AppScreen -import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.twins.redux.TwinCardsAction -import com.tangem.tap.features.wallet.redux.Artwork -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_twin_cards_welcome.* -import kotlinx.android.synthetic.main.layout_twin_cards.* -import kotlinx.coroutines.launch - -class OnboardingTwinsFragment : Fragment(R.layout.fragment_twin_cards_welcome) { - - 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(TwinCardsAction.SetOnboardingShown) - val secondTwinNumber = store.state.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 { - scope.launch { - val global = store.state.globalState - val scanResponse = global.onboardingManager?.scanResponse ?: return@launch - global.tapWalletManager.onCardScanned(scanResponse) - withMainContext { - store.dispatchDebugErrorNotification("Делаем онбоардинг") - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt new file mode 100644 index 0000000000..7d48937c17 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsAction.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt new file mode 100644 index 0000000000..f56502e209 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -0,0 +1,275 @@ +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 = { 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.Warning -> { + } + TwinCardsStep.CreateFirstWallet -> { + } + TwinCardsStep.CreateSecondWallet -> { + } + TwinCardsStep.CreateThirdWallet -> { + } + 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()) + } + } + } + } + } + } +} diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt new file mode 100644 index 0000000000..1d18eb4dec --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsReducer.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt new file mode 100644 index 0000000000..ac731df22a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt @@ -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 + 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 +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt new file mode 100644 index 0000000000..3a1f683cca --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt @@ -0,0 +1,366 @@ +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.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() { + + 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) + + 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() ?: "" +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/dialog/CreateWalletInterruptDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/dialog/CreateWalletInterruptDialog.kt similarity index 66% rename from app/src/main/java/com/tangem/tap/features/twins/ui/dialog/CreateWalletInterruptDialog.kt rename to app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/dialog/CreateWalletInterruptDialog.kt index ab34b78039..0020137a82 100644 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/dialog/CreateWalletInterruptDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/dialog/CreateWalletInterruptDialog.kt @@ -1,10 +1,10 @@ -package com.tangem.tap.features.twins.ui.dialog +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.twins.redux.TwinCardsAction +import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.store import com.tangem.wallet.R @@ -13,10 +13,10 @@ import com.tangem.wallet.R */ class CreateWalletInterruptDialog { companion object { - fun create(state: TwinCardsAction.Wallet.InterruptDialog, context: Context): AlertDialog { + fun create(state: TwinCardsAction.Wallet.ShowInterruptDialog, context: Context): AlertDialog { return MaterialAlertDialogBuilder(context) - .setMessage(R.string.details_twins_recreate_alert) - .setPositiveButton(R.string.common_ok) { _, _ -> state.interrupt() } + .setMessage(R.string.twins_recreate_alert) + .setPositiveButton(R.string.common_ok) { _, _ -> state.onOk() } .setNegativeButton(R.string.common_cancel) { _, _ -> } .setOnDismissListener { store.dispatchDialogHide() } .create() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt index d0463040bd..69c66581a6 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index bb675cf3fe..4c35c25b24 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -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,7 @@ 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.send.redux.* import com.tangem.tap.features.send.redux.AddressPayIdActionUi.* import com.tangem.tap.features.send.redux.AmountActionUi.* @@ -63,23 +62,6 @@ 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) @@ -97,7 +79,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 +106,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 +263,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) diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt index aa129ebbb5..17dfb7a5fc 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 2471b257de..a8987dcd0e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -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) } diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt deleted file mode 100644 index 383481a905..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsAction.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.tangem.tap.features.twins.redux - -import com.tangem.Message -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.TwinCardNumber -import com.tangem.tap.domain.twins.TwinCardsManager -import org.rekotlin.Action - -/** -[REDACTED_AUTHOR] - */ -sealed class TwinCardsAction : Action { - - data class SetResources(val resources: TwinCardsResources) : TwinCardsAction() - - data class SetTwinCard( - val number: TwinCardNumber, - val secondCardId: String?, - val isCreatingTwinCardsAllowed: Boolean, - ) : TwinCardsAction() - - sealed class CardsManager { - data class Set(val manager: TwinCardsManager) : TwinCardsAction() - object Release : TwinCardsAction() - } - - object ShowOnboarding : TwinCardsAction() - object SetOnboardingShown : TwinCardsAction() - object ProceedToCreateWallet : TwinCardsAction() - - sealed class Wallet : TwinCardsAction() { - object HandleOnBackPressed : TwinCardsAction() - - data class Create( - val number: TwinCardNumber, - val createTwinWalletMode: CreateTwinWalletMode - ) : TwinCardsAction() - - data class InterruptDialog(val interrupt: () -> Unit) : TwinCardsAction(), StateDialog - - data class LaunchFirstStep( - val initialMessage: Message, - val reader: AssetReader - ) : TwinCardsAction() { - object Success : TwinCardsAction() - } - - data class LaunchSecondStep( - val initialMessage: Message, - val preparingMessage: Message, - val creatingWalletMessage: Message, - ) : TwinCardsAction() { - object Success : TwinCardsAction() - } - - data class LaunchThirdStep(val message: Message) : TwinCardsAction() { - data class Success(val scanResponse: ScanResponse) : TwinCardsAction() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt deleted file mode 100644 index 84c837d778..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsMiddleware.kt +++ /dev/null @@ -1,139 +0,0 @@ -package com.tangem.tap.features.twins.redux - -import com.tangem.blockchain.extensions.Result -import com.tangem.blockchain.extensions.SimpleResult -import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.extensions.dispatchNotification -import com.tangem.tap.common.redux.AppState -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.preferencesStorage -import com.tangem.tap.scope -import com.tangem.tap.store -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import org.rekotlin.Action -import org.rekotlin.DispatchFunction -import org.rekotlin.Middleware - -class TwinCardsMiddleware { - companion object { - val handler = twinsWalletMiddleware - } -} - -private val twinsWalletMiddleware: Middleware = { dispatch, state -> - { next -> - { action -> - handle(action, dispatch) - next(action) - } - } -} - -private fun handle(action: Action, dispatch: DispatchFunction) { - val action = action as? TwinCardsAction ?: return - - val twinCardsState = store.state.twinCardsState - - when (action) { - is TwinCardsAction.SetTwinCard -> { - val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() - if (showOnboarding) store.dispatch(TwinCardsAction.ShowOnboarding) - } - TwinCardsAction.SetOnboardingShown -> { - preferencesStorage.saveTwinsOnboardingShown() - } - is TwinCardsAction.ProceedToCreateWallet -> { - store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWallet)) - } - is TwinCardsAction.Wallet.HandleOnBackPressed -> { - if (twinCardsState.createWalletState?.showAlert == true) { - val stateDialog = TwinCardsAction.Wallet.InterruptDialog { - store.dispatch(TwinCardsAction.CardsManager.Release) - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - } - store.dispatchDialogShow(stateDialog) - } else { - store.dispatch(TwinCardsAction.CardsManager.Release) - store.dispatch(NavigationAction.PopBackTo()) - } - } - is TwinCardsAction.Wallet.Create -> { - val wallet = store.state.walletState.walletManagers.map { it.wallet }.firstOrNull() - if (wallet == null) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) - return - } - val notEmpty = wallet.recentTransactions.isNotEmpty() || wallet.amounts.toSendableAmounts().isNotEmpty() - if (notEmpty) { - store.dispatchNotification(twinCardsState.resources.strings.walletIsNotEmpty) - } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) - } - } - is TwinCardsAction.Wallet.LaunchFirstStep -> { - val scanResponse = store.state.globalState.scanResponse ?: return - - val manager = TwinCardsManager(scanResponse, action.reader) - store.dispatch(TwinCardsAction.CardsManager.Set(manager)) - - scope.launch { - val result = manager.createFirstWallet(action.initialMessage) - withContext(Dispatchers.Main) { - when (result) { - SimpleResult.Success -> { - store.dispatch(TwinCardsAction.Wallet.LaunchFirstStep.Success) - } - is SimpleResult.Failure -> { - } - } - } - } - } - is TwinCardsAction.Wallet.LaunchSecondStep -> { - val manager = twinCardsState.twinCardsManager ?: return - - scope.launch { - val result = manager.createSecondWallet(action.initialMessage, - action.preparingMessage, action.creatingWalletMessage) - withContext(Dispatchers.Main) { - when (result) { - SimpleResult.Success -> { - store.dispatch(TwinCardsAction.Wallet.LaunchSecondStep.Success) - } - is SimpleResult.Failure -> { - } - } - } - } - } - is TwinCardsAction.Wallet.LaunchThirdStep -> { - val manager = twinCardsState.twinCardsManager ?: return - - scope.launch { - val result = manager.complete(action.message) - withContext(Dispatchers.Main) { - when (result) { - is Result.Success -> { - store.dispatch(TwinCardsAction.Wallet.LaunchThirdStep.Success(result.data)) - } - is Result.Failure -> { - - } - } - } - } - } - is TwinCardsAction.Wallet.LaunchThirdStep.Success -> { - scope.launch { - store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse) - } - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt deleted file mode 100644 index 89978790a1..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsReducer.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.tangem.tap.features.twins.redux - -import com.tangem.tap.common.redux.AppState -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 - - val state = state.twinCardsState - return when (action) { - is TwinCardsAction.SetResources -> { - state.copy(resources = action.resources) - } - is TwinCardsAction.SetTwinCard -> { - state.copy( - cardNumber = action.number, - secondCardId = action.secondCardId, - showTwinOnboarding = state.showTwinOnboarding, - isCreatingTwinCardsAllowed = action.isCreatingTwinCardsAllowed, - ) - } - is TwinCardsAction.CardsManager.Set -> { - state.copy(twinCardsManager = action.manager) - } - TwinCardsAction.CardsManager.Release -> { - state.copy(twinCardsManager = null) - } - is TwinCardsAction.ShowOnboarding -> { - state.copy(showTwinOnboarding = true) - } - is TwinCardsAction.SetOnboardingShown -> { - state.copy(showTwinOnboarding = false) - } - is TwinCardsAction.Wallet.Create -> { - val prevState = state.createWalletState - state.copy(createWalletState = CreateTwinWalletState( - prevState?.scanResponse, - prevState?.number ?: action.number, - prevState?.mode ?: action.createTwinWalletMode, - )) - } - TwinCardsAction.Wallet.LaunchFirstStep.Success -> { - state.copy(createWalletState = state.createWalletState?.copy( - step = CreateTwinWalletStep.SecondStep - )) - } - TwinCardsAction.Wallet.LaunchSecondStep.Success -> - state.copy(createWalletState = state.createWalletState?.copy( - step = CreateTwinWalletStep.ThirdStep - )) - else -> state - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt b/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt deleted file mode 100644 index f23ac31930..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/redux/TwinCardsState.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.tangem.tap.features.twins.redux - -import com.tangem.tap.domain.tasks.product.ScanResponse -import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.tap.domain.twins.TwinCardsManager -import org.rekotlin.StateType - -/** -[REDACTED_AUTHOR] - */ -data class TwinCardsState( - val twinCardsManager: TwinCardsManager? = null, - // if cardNumber is not set -> the scanned card is not Twin - val cardNumber: TwinCardNumber? = null, - val secondCardId: String? = null, - val showTwinOnboarding: Boolean = false, - val isCreatingTwinCardsAllowed: Boolean = false, - val createWalletState: CreateTwinWalletState? = null, - val resources: TwinCardsResources = TwinCardsResources(), -) : StateType - -data class CreateTwinWalletState( - val scanResponse: ScanResponse?, - val number: TwinCardNumber = TwinCardNumber.First, - val mode: CreateTwinWalletMode = CreateTwinWalletMode.CreateWallet, - val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, -) { - val showAlert: Boolean - get() = step != CreateTwinWalletStep.FirstStep -} - -enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep } - -enum class CreateTwinWalletMode { CreateWallet, RecreateWallet } - -data class TwinCardsResources( - val strings: Strings = Strings() -) { - //TODO: create global structure for StringRes. Set it from the MainActivity. - data class Strings( - val walletIsNotEmpty: Int = -1 - ) -} diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt deleted file mode 100644 index 94c5a099bc..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/CreateTwinWalletFragment.kt +++ /dev/null @@ -1,162 +0,0 @@ -package com.tangem.tap.features.twins.ui - -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.squareup.picasso.Picasso -import com.tangem.Message -import com.tangem.tap.common.extensions.readAssetAsString -import com.tangem.tap.domain.twins.AssetReader -import com.tangem.tap.domain.twins.TwinCardNumber -import com.tangem.tap.features.twins.redux.* -import com.tangem.tap.features.wallet.redux.Artwork -import com.tangem.tap.store -import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_twin_cards_create.* -import org.rekotlin.StoreSubscriber - -class CreateTwinWalletFragment : Fragment(R.layout.fragment_twin_cards_create), - StoreSubscriber { - - private val assetReader: AssetReader by lazy { - object : AssetReader { - override fun readAssetAsString(name: String): String = requireContext().readAssetAsString(name) - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed) - } - }) - - initResources() - val inflater = TransitionInflater.from(requireContext()) - enterTransition = inflater.inflateTransition(R.transition.slide_right) - exitTransition = inflater.inflateTransition(R.transition.fade) - } - - private fun initResources() { - val resources = TwinCardsResources( - TwinCardsResources.Strings( - R.string.details_notification_erase_wallet_not_possible - ) - ) - store.dispatch(TwinCardsAction.SetResources(resources)) - } - - override fun onStart() { - super.onStart() - store.subscribe(this) { state -> - state.skipRepeats { oldState, newState -> - oldState.twinCardsState == newState.twinCardsState - }.select { it.twinCardsState } - } - } - - override fun onStop() { - super.onStop() - store.unsubscribe(this) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - toolbar.setNavigationOnClickListener { store.dispatch(TwinCardsAction.Wallet.HandleOnBackPressed) } - - 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: TwinCardsState) { - if (activity == null) return - - toolbar.title = when (state.createWalletState?.mode) { - CreateTwinWalletMode.CreateWallet -> getText(R.string.wallet_button_create_wallet) - CreateTwinWalletMode.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.createWalletState?.number ?: TwinCardNumber.First - - val cardNumber = when (state.createWalletState?.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(TwinCardsAction.Wallet.LaunchFirstStep( - Message(getString( - R.string.details_twins_recreate_title_format, - twinCardNumberString - )), - assetReader - )) - } - 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(TwinCardsAction.Wallet.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(TwinCardsAction.Wallet.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) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt b/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt deleted file mode 100644 index 18f779a400..0000000000 --- a/app/src/main/java/com/tangem/tap/features/twins/ui/TwinWalletWarningFragment.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.tangem.tap.features.twins.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.NavigationAction -import com.tangem.tap.features.twins.redux.CreateTwinWalletMode -import com.tangem.tap.features.twins.redux.TwinCardsAction -import com.tangem.tap.features.wallet.redux.Artwork -import com.tangem.tap.store -import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_twin_cards_warning.* -import kotlinx.android.synthetic.main.layout_twin_cards_orange.* - -class TwinWalletWarningFragment : Fragment(R.layout.fragment_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.twinCardsState.createWalletState?.mode - if (createTwinWallet == CreateTwinWalletMode.CreateWallet) { - tv_twin_cards_description.text = getText(R.string.details_twins_recreate_subtitle) - } else if (createTwinWallet == CreateTwinWalletMode.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(TwinCardsAction.ProceedToCreateWallet) } - 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) - } - - -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt index 29fe72df99..a449bb17ea 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt @@ -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 ) } @@ -66,4 +67,8 @@ fun Wallet.getPendingTransactions(): List { fun Wallet.hasPendingTransactions(): Boolean { return getPendingTransactions().isNotEmpty() +} + +fun Wallet.hasSendableAmountsOrPendingTransactions(): Boolean { + return hasPendingTransactions() || amounts.toSendableAmounts().isNotEmpty() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index aa6094f26c..54e7f12586 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -12,7 +12,7 @@ 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.features.twins.redux.TwinCardsState +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 diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index bdf79de67a..513627f250 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -19,8 +19,6 @@ import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen -import com.tangem.tap.features.twins.redux.CreateTwinWalletMode -import com.tangem.tap.features.twins.redux.TwinCardsAction import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.network.NetworkStateChanged import kotlinx.coroutines.launch @@ -96,31 +94,22 @@ class WalletMiddleware { } } is WalletAction.CreateWallet -> { - if (walletState.isTangemTwins) { - store.dispatch(TwinCardsAction.Wallet.Create( - walletState.twinCardsState.cardNumber!!, - CreateTwinWalletMode.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 - ) - } + 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 + ) } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index 8e0e9df126..ba5d6c7d56 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -38,13 +38,12 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.ResetState -> newState = WalletState() is WalletAction.SetIfTestnetCard -> newState = newState.copy(isTestnet = action.isTestnet) is WalletAction.EmptyWallet -> { - val creatingWalletAllowed = state.twinCardsState.isCreatingTwinCardsAllowed newState = newState.copy( state = ProgressState.Done, wallets = listOf( WalletData( currencyData = BalanceWidgetData(BalanceStatus.EmptyCard), - mainButton = WalletMainButton.CreateWalletButton(creatingWalletAllowed), + mainButton = WalletMainButton.CreateWalletButton(true), ) ) ) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index b12080f1bd..f7978e35d8 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -150,8 +150,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber tv_twin_card_number.show() @@ -107,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( diff --git a/app/src/main/res/drawable/card_placeholder.xml b/app/src/main/res/drawable/card_placeholder.xml deleted file mode 100644 index 1cfef76e78..0000000000 --- a/app/src/main/res/drawable/card_placeholder.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/card_placeholder_black.xml b/app/src/main/res/drawable/card_placeholder_black.xml new file mode 100644 index 0000000000..0fef87ee39 --- /dev/null +++ b/app/src/main/res/drawable/card_placeholder_black.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/card_placeholder_white.xml b/app/src/main/res/drawable/card_placeholder_white.xml new file mode 100644 index 0000000000..d980dc5599 --- /dev/null +++ b/app/src/main/res/drawable/card_placeholder_white.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_home_back_card.xml b/app/src/main/res/drawable/ic_home_back_card.xml deleted file mode 100644 index f15c47ba3f..0000000000 --- a/app/src/main/res/drawable/ic_home_back_card.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_home_front_card.xml b/app/src/main/res/drawable/ic_home_front_card.xml deleted file mode 100644 index 19969e806e..0000000000 --- a/app/src/main/res/drawable/ic_home_front_card.xml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml b/app/src/main/res/layout-h680dp/layout_onboarding_container_bottom.xml similarity index 83% rename from app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml rename to app/src/main/res/layout-h680dp/layout_onboarding_container_bottom.xml index a603013d47..e635e3ee6f 100644 --- a/app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml +++ b/app/src/main/res/layout-h680dp/layout_onboarding_container_bottom.xml @@ -7,6 +7,7 @@ android:clipChildren="false"> + + diff --git a/app/src/main/res/layout/fragment_twin_cards_create.xml b/app/src/main/res/layout/fragment_twin_cards_create.xml deleted file mode 100644 index 7549f32998..0000000000 --- a/app/src/main/res/layout/fragment_twin_cards_create.xml +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_twin_cards_warning.xml b/app/src/main/res/layout/fragment_twin_cards_warning.xml deleted file mode 100644 index 6ca8d8f68c..0000000000 --- a/app/src/main/res/layout/fragment_twin_cards_warning.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_twin_cards_welcome.xml b/app/src/main/res/layout/fragment_twin_cards_welcome.xml deleted file mode 100644 index c92e2adeb5..0000000000 --- a/app/src/main/res/layout/fragment_twin_cards_welcome.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml index 3733c23089..bc98ff9e41 100644 --- a/app/src/main/res/layout/fragment_wallet.xml +++ b/app/src/main/res/layout/fragment_wallet.xml @@ -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" /> + + + + + + + + + + + + - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_twin_cards_orange.xml b/app/src/main/res/layout/layout_twin_cards_orange.xml deleted file mode 100644 index f524c5dd49..0000000000 --- a/app/src/main/res/layout/layout_twin_cards_orange.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_create_wallet.xml b/app/src/main/res/layout/lp_onboarding_create_wallet.xml index fa4505f353..3c5cd91655 100644 --- a/app/src/main/res/layout/lp_onboarding_create_wallet.xml +++ b/app/src/main/res/layout/lp_onboarding_create_wallet.xml @@ -6,6 +6,11 @@ android:layout_height="match_parent" android:visibility="visible"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml index 65705f3fce..6b924ea771 100644 --- a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml +++ b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml @@ -6,6 +6,11 @@ android:layout_height="match_parent" android:visibility="visible"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/test_leapfrog_widget.xml b/app/src/main/res/layout/test_leapfrog_widget.xml index 3fbf992180..c4fddb3183 100644 --- a/app/src/main/res/layout/test_leapfrog_widget.xml +++ b/app/src/main/res/layout/test_leapfrog_widget.xml @@ -11,13 +11,12 @@ + android:src="@drawable/card_placeholder_black" + android:tint="@color/colorPrimary" /> + android:src="@drawable/card_placeholder_black" /> \ No newline at end of file diff --git a/app/src/main/res/layout/view_bg_twins_welcome.xml b/app/src/main/res/layout/view_bg_twins_welcome.xml new file mode 100644 index 0000000000..5202b1851b --- /dev/null +++ b/app/src/main/res/layout/view_bg_twins_welcome.xml @@ -0,0 +1,46 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_onboarding_progress.xml b/app/src/main/res/layout/view_onboarding_progress.xml index 415f8bd19c..8b2e151d1a 100644 --- a/app/src/main/res/layout/view_onboarding_progress.xml +++ b/app/src/main/res/layout/view_onboarding_progress.xml @@ -1,10 +1,13 @@ - + + + + diff --git a/app/src/main/res/layout/view_onboarding_tv_balance.xml b/app/src/main/res/layout/view_onboarding_tv_balance.xml index 5dd54d7067..6577933837 100644 --- a/app/src/main/res/layout/view_onboarding_tv_balance.xml +++ b/app/src/main/res/layout/view_onboarding_tv_balance.xml @@ -12,6 +12,7 @@ android:id="@+id/tv_balance" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:elevation="0dp" android:fontFamily="sans-serif-medium" android:text="@string/onboarding_balance_title" android:textAllCaps="true" diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 17edb1b769..0d8661c471 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -14,25 +14,22 @@ Tangem Twin One wallet. Two cards. - This one that you are holding in your hands -and the other one with number %s.\n\nBoth cards can be used to extract funds from -this wallet. - + This one that you are holding in your handsand the other one with number #%s. Card %s of 2 Re-create twin wallet - This action is irreversible. You will not have access to the old wallet.\nThe wallet re-creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning. - Re-create wallet - Step %s - Tap the #%s twin card - Preparing card - Creating wallet - The wallet creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning. - Tap the card #%s - You have already started the process of recreating twin wallet. If you interrupt it, you won\'t be able to use your twin cards until you start it again and complete recreating the wallet. Card terms of use + Tangem Twin + This action is irreversible. You will not have access to the old wallet. + Tap the #%s twin card + Preparing card + Creating wallet + The wallet creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning. + Tap the card #%s + You have already started the process of recreating twin wallet. If you interrupt it, you won\'t be able to use your twin cards until you start it again and complete recreating the wallet. + The twin address was successfully created //Common @@ -188,11 +185,13 @@ this wallet. Show the wallet’s address Scan the address to top up your wallet - Done! + Success! Your crypto card is activated and ready to be used Continue Balance + If the process of re-creating the wallet gets interrupted in any way, you’ll have to start over. + Internal error: can\'t create wallet manager diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index f008a00608..fe7f02a527 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -5,8 +5,7 @@ @color/colorPrimary @color/colorSecondary @color/darkGray6 - @color/backgroundLightGray - false + true true true @null