Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-30 12:51:57 +03:00
commit 2541461d44
954 changed files with 22834 additions and 21200 deletions

View file

@ -9,9 +9,11 @@ import com.tangem.tap.common.ui.*
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
import com.tangem.tap.features.onboarding.OnboardingDialog
import com.tangem.tap.features.onboarding.products.twins.ui.dialog.TwinningProcessNotCompletedDialog
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.*
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDiscardingBackupDialog
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.UnfinishedBackupFoundDialog
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.WalletActivationErrorDialog
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.WalletAlreadyWasUsedDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import org.rekotlin.StoreSubscriber
@ -56,10 +58,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
messageRes = R.string.nfc_error_unavailable,
context = context,
)
is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context)
is OnboardingDialog.TwinningProcessNotCompleted -> TwinningProcessNotCompletedDialog.create(context)
is OnboardingDialog.InterruptOnboarding -> InterruptOnboardingDialog.create(context, state.dialog)
is OnboardingDialog.WalletActivationError -> WalletActivationErrorDialog.create(context, state.dialog)
is WalletConnectDialog.UnsupportedCard ->
SimpleAlertDialog.create(
@ -143,9 +141,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
messageRes = R.string.unsupported_wc_version,
context = context,
)
is BackupDialog.AttestationFailed -> AttestationFailedDialog.create(context)
is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context)
is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context)
is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(
context = context,
scanResponse = state.dialog.scanResponse,
@ -154,10 +149,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
context = context,
unfinishedBackupScanResponse = state.dialog.scanResponse,
)
is BackupDialog.ResetBackupCard -> ResetBackupCardDialog.create(
context = context,
cardId = state.dialog.cardId,
)
is AppDialog.TokensAreLinkedDialog -> SimpleAlertDialog.create(
title = context.getString(state.dialog.titleRes, state.dialog.currencySymbol),
message = context.getString(

View file

@ -1,16 +1,5 @@
package com.tangem.tap.common
import android.content.Context
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.appcompat.widget.LinearLayoutCompat
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.store
/**
[REDACTED_AUTHOR]
*/
@ -21,41 +10,4 @@ object TestActions {
var testAmountInjectionForWalletManagerEnabled = false
}
typealias TestAction = Pair<String, () -> Unit>
class TestActionsBottomSheetDialog(
private val appDialog: AppDialog.TestActionsDialog,
context: Context,
) : BottomSheetDialog(context) {
@Suppress("MagicNumber")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(createContentView())
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.peekHeight = 400
setOnCancelListener {
store.dispatchDialogHide()
}
}
private fun createContentView(): View {
val actionButtons = mutableListOf<View>()
appDialog.actionsList.forEach { (actionName, action) ->
val view = Button(context).apply {
text = actionName
setOnClickListener {
action()
cancel()
}
}
actionButtons.add(view)
}
return LinearLayoutCompat(context).apply {
orientation = LinearLayoutCompat.VERTICAL
actionButtons.forEach { addView(it) }
}
}
}
typealias TestAction = Pair<String, () -> Unit>

View file

@ -1,60 +0,0 @@
package com.tangem.tap.common.extensions
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.tangem.utils.Provider
import com.tangem.wallet.R
import timber.log.Timber
fun FragmentManager.showFragmentAllowingStateLoss(name: String, fragmentProvider: Provider<Fragment>) {
if (backStackEntryCount > 0) {
val currentFragmentName = getBackStackEntryAt(backStackEntryCount - 1).name
if (name == currentFragmentName) {
Timber.i("Fragment $name is already at the top of the stack")
return
}
}
Timber.i("Showing $name route")
val isPoppedBack = popBackStackImmediate(name, 0)
if (!isPoppedBack) {
val fragment = fragmentProvider()
if (fragment is DialogFragment) {
fragment.showDialog(fragmentManager = this, name)
} else {
fragment.showFragment(fragmentManager = this, name)
}
Timber.i("Route $name is shown")
} else {
Timber.i("Route $name is found in backstack and shown")
}
}
private fun DialogFragment.showDialog(fragmentManager: FragmentManager, name: String) {
val transaction = fragmentManager.beginTransaction()
try {
transaction.addToBackStack(name)
show(transaction, name)
} catch (e: IllegalStateException) {
transaction.add(this, name)
transaction.addToBackStack(name)
transaction.commitAllowingStateLoss()
}
}
private fun Fragment.showFragment(fragmentManager: FragmentManager, name: String) {
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, this, name)
transaction.addToBackStack(name)
transaction.commitAllowingStateLoss()
}

View file

@ -1,6 +1,8 @@
package com.tangem.tap.common.extensions
import com.tangem.common.routing.AppRouter
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.redux.StateDialog
import com.tangem.domain.wallets.models.UserWallet
@ -43,20 +45,16 @@ suspend fun Store<AppState>.onUserWalletSelected(userWallet: UserWallet) {
state.globalState.tapWalletManager.onWalletSelected(userWallet)
}
fun Store<*>.dispatchErrorNotification(error: TapError) {
dispatchOnMain(GlobalAction.ShowErrorNotification(error))
}
/**
* @param fatal used to indicate errors that should not normally occur
*/
fun Store<*>.dispatchDebugErrorNotification(message: String, fatal: Boolean = false) {
fun Store<AppState>.dispatchDebugErrorNotification(message: String, fatal: Boolean = false) {
val prefix = if (fatal) "FATAL ERROR: " else "DEBUG ERROR: "
dispatchDebugErrorNotification(TapError.CustomError("$prefix $message"))
}
fun Store<*>.dispatchDebugErrorNotification(error: TapError) {
dispatchOnMain(GlobalAction.DebugShowErrorNotification(error))
fun Store<AppState>.dispatchDebugErrorNotification(error: TapError) {
inject(DaggerGraphState::uiMessageSender).send(SnackbarMessage(stringReference(error.message ?: "debug error")))
}
fun Store<*>.dispatchDialogShow(dialog: StateDialog) {

View file

@ -1,8 +1,11 @@
package com.tangem.tap.common.images
import android.content.Context
import android.os.Build
import android.util.Log
import coil.ImageLoader
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import coil.memory.MemoryCache
import coil.request.CachePolicy
import coil.util.Logger
@ -25,6 +28,15 @@ fun createCoilImageLoader(context: Context, logEnabled: Boolean = false): ImageL
.build()
}
}
.components {
// According to Coil ImageDecoder API is faster and supports animated WebP and HEIF
// https://coil-kt.github.io/coil/gifs/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
add(ImageDecoderDecoder.Factory())
} else {
add(GifDecoder.Factory())
}
}
.memoryCachePolicy(CachePolicy.ENABLED)
.memoryCache {
MemoryCache.Builder(context)

View file

@ -2,9 +2,6 @@ package com.tangem.tap.common.redux
import com.tangem.common.extensions.VoidCallback
import com.tangem.domain.redux.StateDialog
import com.tangem.tap.common.TestAction
import com.tangem.tap.domain.model.Currency
import com.tangem.tap.domain.model.WalletAddressData
import com.tangem.wallet.R
/**
@ -18,15 +15,6 @@ sealed class AppDialog : StateDialog {
val onOk: VoidCallback? = null,
) : AppDialog()
internal data class AddressInfoDialog(
val currency: Currency,
val addressData: WalletAddressData,
) : AppDialog()
data class TestActionsDialog(
val actionsList: List<TestAction>,
) : AppDialog()
data class RemoveWalletDialog(
val currencyTitle: String,
val onOk: () -> Unit,

View file

@ -4,11 +4,6 @@ import com.tangem.tap.common.redux.global.globalReducer
import com.tangem.tap.features.details.redux.DetailsReducer
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer
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.saveWallet.redux.SaveWalletReducer
import com.tangem.tap.features.welcome.redux.WelcomeReducer
import com.tangem.tap.proxy.redux.DaggerGraphReducer
import org.rekotlin.Action
@ -20,14 +15,9 @@ fun appReducer(action: Action, state: AppState?): AppState {
return AppState(
globalState = globalReducer(action, state),
homeState = HomeReducer.reduce(action, state),
onboardingNoteState = OnboardingNoteReducer.reduce(action, state),
onboardingWalletState = OnboardingWalletReducer.reduce(action, state),
onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state),
twinCardsState = TwinCardsReducer.reduce(action, state),
detailsState = DetailsReducer.reduce(action, state),
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState),
welcomeState = WelcomeReducer.reduce(action, state),
saveWalletState = SaveWalletReducer.reduce(action, state),
daggerGraphState = DaggerGraphReducer.reduce(action, state),
)
}

View file

@ -9,17 +9,7 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectMiddlewa
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
import com.tangem.tap.features.home.redux.HomeMiddleware
import com.tangem.tap.features.home.redux.HomeState
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMiddleware
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.BackupMiddleware
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.saveWallet.redux.SaveWalletMiddleware
import com.tangem.tap.features.saveWallet.redux.SaveWalletState
import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware
import com.tangem.tap.features.welcome.redux.WelcomeMiddleware
import com.tangem.tap.features.welcome.redux.WelcomeState
@ -31,14 +21,9 @@ import org.rekotlin.StateType
data class AppState(
val globalState: GlobalState = GlobalState(),
val homeState: HomeState = HomeState(),
val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(),
val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(),
val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(),
val twinCardsState: TwinCardsState = TwinCardsState(),
val detailsState: DetailsState = DetailsState(),
val walletConnectState: WalletConnectState = WalletConnectState(),
val welcomeState: WelcomeState = WelcomeState(),
val saveWalletState: SaveWalletState = SaveWalletState(),
val daggerGraphState: DaggerGraphState = DaggerGraphState(),
) : StateType {
@ -46,18 +31,12 @@ data class AppState(
fun getMiddleware(): List<Middleware<AppState>> {
return listOf(
logMiddleware,
notificationsMiddleware,
GlobalMiddleware.handler,
HomeMiddleware.handler,
OnboardingNoteMiddleware.handler,
OnboardingWalletMiddleware.handler,
OnboardingOtherCardsMiddleware.handler,
TwinCardsMiddleware.handler,
DetailsMiddleware().detailsMiddleware,
WalletConnectMiddleware().walletConnectMiddleware,
BackupMiddleware().backupMiddleware,
WelcomeMiddleware().middleware,
SaveWalletMiddleware().middleware,
LockUserWalletsTimerMiddleware().middleware,
AccessCodeRequestPolicyMiddleware().middleware,
DaggerGraphMiddleware.daggerGraphMiddleware,

View file

@ -1,141 +0,0 @@
package com.tangem.tap.common.redux
import android.content.Context
import android.widget.Toast
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.snackbar.Snackbar
import com.tangem.tap.common.extensions.getColor
import com.tangem.tap.domain.ArgError
import com.tangem.tap.domain.MultiMessageError
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.assembleErrors
import com.tangem.tap.notificationsHandler
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import org.rekotlin.Action
import org.rekotlin.Middleware
import java.lang.ref.WeakReference
class NotificationsHandler(coordinatorLayout: CoordinatorLayout) {
private val basicCoordinatorLayout = WeakReference(coordinatorLayout)
private var baseLayout = basicCoordinatorLayout
private fun showNotification(message: String) {
baseLayout.get()?.let { layout ->
Snackbar.make(layout, message, Snackbar.LENGTH_LONG)
.also { snackbar -> snackbar.show() }
}
}
private fun showDebugNotification(message: String) {
baseLayout.get()?.let { layout ->
Snackbar.make(layout, message, Snackbar.LENGTH_LONG)
.also { snackbar ->
snackbar.setBackgroundTint(layout.getColor(R.color.warning_warning))
snackbar.show()
}
}
}
fun showNotification(message: Int, args: List<Any>? = null) {
baseLayout.get()?.let {
showNotification(getMessageString(it.context, message, args))
}
}
fun showToastNotification(message: Int, args: List<Any>? = null) {
baseLayout.get()?.let {
Toast.makeText(it.context, getMessageString(it.context, message, args), Toast.LENGTH_LONG).show()
}
}
fun showNotification(errorList: List<Pair<Int, List<Any>?>>, builder: (List<String>) -> String) {
val context = baseLayout.get()?.context ?: return
val message = builder(errorList.map { getMessageString(context, it.first, it.second) })
showNotification(message)
}
fun showDebugErrorNotification(message: Int, args: List<Any>? = null) {
baseLayout.get()?.let {
showDebugNotification(getMessageString(it.context, message, args))
}
}
}
fun getMessageString(context: Context, message: Int, args: List<Any>?): String {
return if (args.isNullOrEmpty()) {
context.getString(message)
} else {
context.getString(message, *args.toTypedArray())
}
}
val notificationsMiddleware: Middleware<AppState> = { dispatch, state ->
{ next ->
{ action ->
handleNotificationAction(action)
next(action)
}
}
}
@Suppress("ComplexMethod")
private fun handleNotificationAction(action: Action) {
if (action is Debug && !BuildConfig.DEBUG) return
when (action) {
is NotificationAction -> {
notificationsHandler?.showNotification(action.messageResource)
}
is ToastNotificationAction -> notificationsHandler?.showToastNotification(action.messageResource)
is ErrorAction -> {
when (action) {
is Debug -> {
val args = (action.error as? ArgError)?.args ?: listOf()
when (action) {
is DebugNotification -> {
notificationsHandler?.showNotification(action.error.messageResource, args)
}
is DebugToastNotification -> {
notificationsHandler?.showToastNotification(action.error.messageResource, args)
}
is DebugErrorAction -> {
notificationsHandler?.showDebugErrorNotification(action.error.messageResource, args)
}
}
}
else -> {
when (action.error) {
is MultiMessageError -> {
val multiError = action.error as MultiMessageError
notificationsHandler?.showNotification(multiError.assembleErrors(), multiError.builder)
}
else -> {
val args = (action.error as? ArgError)?.args ?: listOf()
notificationsHandler?.showNotification(action.error.messageResource, args)
}
}
}
}
}
}
}
interface ToastNotificationAction : Action {
val messageResource: Int
}
interface NotificationAction : Action {
val messageResource: Int
}
interface ErrorAction : Action {
val error: TapError
}
// Processed only in the debug builds
interface Debug
interface DebugNotification : Debug, NotificationAction
interface DebugToastNotification : Debug, ToastNotificationAction
interface DebugErrorAction : Debug, ErrorAction

View file

@ -5,45 +5,14 @@ import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.redux.StateDialog
import com.tangem.tap.common.redux.DebugErrorAction
import com.tangem.tap.common.redux.ErrorAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
import org.rekotlin.Action
sealed class GlobalAction : Action {
// notifications
data class ShowErrorNotification(override val error: TapError) : GlobalAction(), ErrorAction
data class DebugShowErrorNotification(override val error: TapError) : GlobalAction(), DebugErrorAction
// dialogs
data class ShowDialog(val stateDialog: StateDialog) : GlobalAction()
object HideDialog : GlobalAction()
sealed class Onboarding : GlobalAction() {
/**
* Initiate an onboarding process.
* For resuming unfinished backup of standard Wallet see
* BackupAction.CheckForUnfinishedBackup, GlobalAction.Onboarding.StartForUnfinishedBackup
*/
data class Start(
val scanResponse: ScanResponse,
val source: BackupStartedSource,
val canSkipBackup: Boolean = true,
) : Onboarding()
/**
* Initiate resuming of unfinished backup for standard Wallet.
* See more BackupAction.CheckForUnfinishedBackup
*/
data class StartForUnfinishedBackup(val addedBackupCardsCount: Int) : Onboarding()
object Stop : Onboarding()
data class ShouldResetCardOnCreate(val shouldReset: Boolean) : Onboarding()
}
object ScanFailsCounter {
data class ChooseBehavior(
val result: CompletionResult<ScanResponse>,

View file

@ -1,7 +1,6 @@
package com.tangem.tap.common.redux.global
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.onboarding.OnboardingManager
import com.tangem.utils.extensions.replaceBy
import org.rekotlin.Action
@ -12,21 +11,6 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
val globalState = state.globalState
return when (action) {
is GlobalAction.Onboarding.Start -> {
val onboardingManager = OnboardingManager(action.scanResponse)
globalState.copy(onboardingState = OnboardingState(true, onboardingManager))
}
is GlobalAction.Onboarding.StartForUnfinishedBackup -> {
globalState.copy(onboardingState = OnboardingState(true, null))
}
is GlobalAction.Onboarding.Stop -> {
globalState.copy(onboardingState = OnboardingState(false))
}
is GlobalAction.Onboarding.ShouldResetCardOnCreate -> {
globalState.copy(
onboardingState = globalState.onboardingState.copy(shouldResetOnCreate = action.shouldReset),
)
}
is GlobalAction.ScanFailsCounter.Increment -> {
globalState.copy(scanCardFailsCounter = globalState.scanCardFailsCounter + 1)
}

View file

@ -5,10 +5,8 @@ import com.tangem.domain.redux.LegacyAction
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.details.redux.AppSettingsState
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
@ -26,15 +24,6 @@ internal object LegacyMiddleware {
{ next ->
{ action ->
when (action) {
is LegacyAction.StartOnboardingProcess -> {
store.dispatch(
GlobalAction.Onboarding.Start(
scanResponse = action.scanResponse,
source = BackupStartedSource.CreateBackup,
canSkipBackup = action.canSkipBackup,
),
)
}
is LegacyAction.PrepareDetailsScreen -> {
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
val walletsRepository = store.inject(DaggerGraphState::walletsRepository)

View file

@ -1,118 +0,0 @@
package com.tangem.tap.common.toggleWidget
import android.view.View
import android.view.ViewGroup
import android.view.animation.*
import android.widget.ViewSwitcher
import com.tangem.tap.common.entities.ProgressState
import com.tangem.wallet.R
/**
[REDACTED_AUTHOR]
*/
@Suppress("MagicNumber")
class RefreshBalanceWidget(root: ViewGroup) : ViewStateWidget {
var isShowing: Boolean? = null
private set
override val mainView: View = root.findViewById(R.id.btn_refresh_balance)
private val viewSwitcher: ViewSwitcher by lazy { mainView.findViewById(R.id.switcher_refresh_arrow_to_progress) }
private val arrowView = mainView.findViewById<View>(R.id.imv_arrow_refresh)
private var progressViewAnimation: Animation? = null
init {
viewSwitcher.inAnimation = AlphaAnimation(0f, 1f).apply {
duration = 400
interpolator = LinearInterpolator()
}
viewSwitcher.outAnimation = AlphaAnimation(1f, 0f).apply {
duration = 400
interpolator = LinearInterpolator()
}
}
override fun changeState(state: WidgetState) {
val progressState = state as? ProgressState ?: return
val currentStateByView = getState()
when {
currentStateByView == progressState -> return
currentStateByView == ProgressState.Done -> if (progressState == ProgressState.Error) return
}
mainView.isClickable = currentStateByView == ProgressState.Done || currentStateByView == ProgressState.Error
animateState(progressState)
viewSwitcher.showNext()
}
@Suppress("MagicNumber")
private fun animateState(state: ProgressState) {
when (state) {
ProgressState.Done, ProgressState.Error -> {
progressViewAnimation?.cancel()
progressViewAnimation = null
}
ProgressState.Loading -> {
progressViewAnimation = RotateAnimation(
/* fromDegrees = */
0f,
/* toDegrees = */
360f,
/* pivotXType = */
Animation.RELATIVE_TO_SELF,
/* pivotXValue = */
0.5f,
/* pivotYType = */
Animation.RELATIVE_TO_SELF,
/* pivotYValue = */
0.5f,
)
progressViewAnimation?.duration = 700
progressViewAnimation?.interpolator = AccelerateInterpolator()
progressViewAnimation?.repeatCount = -1
progressViewAnimation?.let { arrowView.startAnimation(it) }
}
else -> {}
}
}
fun show(show: Boolean) {
if (show == isShowing) return
isShowing = show
if (show) {
mainView.startAnimation(ShowAnimation())
} else {
mainView.startAnimation(AlphaAnimation(1f, 0f).apply { fillAfter = true })
}
}
private fun isArrowRefreshActive(): Boolean = viewSwitcher.currentView.id == R.id.fl_arrow_refresh_container
private fun getState(): ProgressState = if (isArrowRefreshActive()) ProgressState.Done else ProgressState.Loading
}
@Suppress("MagicNumber")
class ShowAnimation : AnimationSet(true) {
init {
addAnimation(
ScaleAnimation(
0f,
1f,
0f,
1f,
Animation.RELATIVE_TO_SELF,
0.5f,
Animation.RELATIVE_TO_SELF,
0.5f,
),
)
addAnimation(AlphaAnimation(0f, 1f))
duration = 300
interpolator = AnticipateOvershootInterpolator()
}
}

View file

@ -1,75 +0,0 @@
package com.tangem.tap.common.ui
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.widget.Toast
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.domain.model.WalletAddressData
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import com.tangem.wallet.R
import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding
/**
[REDACTED_AUTHOR]
*/
internal class AddressInfoBottomSheetDialog(
private val stateDialog: AppDialog.AddressInfoDialog,
context: Context,
) : BottomSheetDialog(context) {
var binding: DialogOnboardingAddressInfoBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DialogOnboardingAddressInfoBinding
.inflate(LayoutInflater.from(context))
setContentView(binding!!.root)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
setOnCancelListener {
store.dispatchDialogHide()
binding = null
}
}
override fun show() {
super.show()
Analytics.send(Token.Receive.ScreenOpened(stateDialog.currency.currencySymbol))
showData(data = stateDialog.addressData)
}
private fun showData(data: WalletAddressData) = with(binding!!) {
pseudoToolbar.imvClose.setOnClickListener {
dismissWithAnimation = true
cancel()
}
imvQrCode.setImageBitmap(data.shareUrl.toQrCode())
tvAddress.text = data.address
btnFlCopyAddress.setOnClickListener {
Analytics.send(Token.Receive.ButtonCopyAddress())
Toast
.makeText(context, R.string.wallet_notification_address_copied, Toast.LENGTH_SHORT)
.show()
store.inject(DaggerGraphState::clipboardManager).setText(text = data.address, isSensitive = true)
}
btnFlShare.setOnClickListener {
Analytics.send(Token.Receive.ButtonShareAddress())
store.dispatchShare(data.shareUrl)
}
val blockchain = stateDialog.currency.blockchain
tvReceiveMessage.text = tvReceiveMessage.getString(
id = R.string.address_qr_code_message_format,
blockchain.getCoinName(),
blockchain.currency,
blockchain.fullName,
)
}
}