Updated on 2026-08-14
This commit is contained in:
parent
459db3d0df
commit
f43f6299d1
62 changed files with 2 additions and 5914 deletions
|
|
@ -1,36 +0,0 @@
|
|||
package com.tangem.tap.common
|
||||
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.tap.common.extensions.scaleToFiat
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class CurrencyConverter(
|
||||
private val rateValue: BigDecimal,
|
||||
private val decimals: Int,
|
||||
) {
|
||||
private val roundingMode = RoundingMode.HALF_UP
|
||||
|
||||
fun toFiat(crypto: BigDecimal, fiatDecimals: Int = 2): BigDecimal {
|
||||
return toFiatUnscaled(crypto).setScale(fiatDecimals, roundingMode)
|
||||
}
|
||||
|
||||
fun toFiatUnscaled(crypto: BigDecimal): BigDecimal {
|
||||
return rateValue.multiply(crypto).setScale(decimals, roundingMode)
|
||||
}
|
||||
|
||||
fun toFiatWithPrecision(crypto: BigDecimal): BigDecimal {
|
||||
return toFiatUnscaled(crypto).scaleToFiat(true)
|
||||
}
|
||||
|
||||
fun toCrypto(fiat: BigDecimal): BigDecimal {
|
||||
if (fiat.isZero()) return fiat
|
||||
|
||||
val fiatValue = fiat.setScale(rateValue.scale(), RoundingMode.UP)
|
||||
val cryptoValue = fiatValue.divide(rateValue, RoundingMode.UP)
|
||||
return cryptoValue.setScale(decimals, roundingMode)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.tangem.tap.common
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class GlobalLayoutStateHandler<T : View>(
|
||||
private val view: T,
|
||||
attachImmediately: Boolean = true,
|
||||
) : ViewTreeObserver.OnGlobalLayoutListener {
|
||||
|
||||
var onStateChanged: ((T) -> Unit)? = null
|
||||
|
||||
private var isAttached: Boolean = false
|
||||
|
||||
init {
|
||||
if (attachImmediately) attach()
|
||||
}
|
||||
|
||||
private fun attach() {
|
||||
if (isAttached) {
|
||||
Timber.d("Already attached")
|
||||
return
|
||||
}
|
||||
|
||||
isAttached = true
|
||||
view.viewTreeObserver.addOnGlobalLayoutListener(this)
|
||||
}
|
||||
|
||||
fun detach() {
|
||||
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
isAttached = false
|
||||
}
|
||||
|
||||
override fun onGlobalLayout() {
|
||||
onStateChanged?.invoke(view)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package com.tangem.tap.common
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Rect
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
class KeyboardObserver(activity: Activity) {
|
||||
|
||||
private val decorView = activity.window.decorView
|
||||
private val windowManager = activity.windowManager
|
||||
private val originalWindowHeight: Int = getWindowHeight()
|
||||
private val onGlobalLayoutListener: OnGlobalLayoutListener = OnGlobalLayoutListener { onGlobalLayout() }
|
||||
|
||||
private var onKeyboardListener: ((Boolean) -> Unit)? = null
|
||||
private var lastIsShow = false
|
||||
private var lastWindowHeight = getWindowHeight()
|
||||
|
||||
fun registerListener(listener: (Boolean) -> Unit) {
|
||||
decorView.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)
|
||||
onKeyboardListener = listener
|
||||
}
|
||||
|
||||
fun unregisterListener() {
|
||||
decorView.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalLayoutListener)
|
||||
onKeyboardListener = null
|
||||
}
|
||||
|
||||
private fun getWindowHeight() = Rect().apply { decorView.getWindowVisibleDisplayFrame(this) }.bottom
|
||||
|
||||
private fun onGlobalLayout() {
|
||||
val currentWindowHeight = getWindowHeight()
|
||||
if (isSoftKeyChanged()) {
|
||||
lastWindowHeight = currentWindowHeight
|
||||
return
|
||||
}
|
||||
|
||||
lastWindowHeight = currentWindowHeight
|
||||
val isShow = originalWindowHeight != currentWindowHeight
|
||||
if (lastIsShow == isShow) return
|
||||
|
||||
lastIsShow = isShow
|
||||
onKeyboardListener?.invoke(isShow)
|
||||
}
|
||||
|
||||
private fun isSoftKeyChanged() = (lastWindowHeight - getWindowHeight()).absoluteValue == getSoftKeyButtonHeight()
|
||||
|
||||
private fun getSoftKeyButtonHeight(): Int {
|
||||
val applicationDisplayHeight = DisplayMetrics().apply {
|
||||
windowManager.defaultDisplay.getMetrics(this)
|
||||
}.heightPixels
|
||||
|
||||
val realDisplayHeight = DisplayMetrics().apply {
|
||||
windowManager.defaultDisplay.getRealMetrics(this)
|
||||
}.heightPixels
|
||||
|
||||
return realDisplayHeight - applicationDisplayHeight
|
||||
}
|
||||
}
|
||||
|
|
@ -22,44 +22,6 @@ sealed class Token(
|
|||
class ButtonShareAddress : Receive("Button - Share Address")
|
||||
}
|
||||
|
||||
sealed class Send(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : Token("Token / Send", event, params, error) {
|
||||
|
||||
class ScreenOpened : Send(event = "Send Screen Opened")
|
||||
class ButtonPaste : Send(event = "Button - Paste")
|
||||
class ButtonQRCode : Send(event = "Button - QR Code")
|
||||
class ButtonSwapCurrency : Send(event = "Button - Swap Currency")
|
||||
|
||||
class AddressEntered(sourceType: SourceType, validationResult: ValidationResult) : Send(
|
||||
event = "Address Entered",
|
||||
params = mapOf(
|
||||
"Source" to sourceType.name,
|
||||
"Validation" to validationResult.name,
|
||||
),
|
||||
) {
|
||||
enum class SourceType {
|
||||
QRCode, PasteButton, PastePopup
|
||||
}
|
||||
|
||||
enum class ValidationResult {
|
||||
Success, Fail
|
||||
}
|
||||
}
|
||||
|
||||
class SelectedCurrency(currency: CurrencyType) : Send(
|
||||
event = "Selected Currency",
|
||||
params = mapOf("Type" to currency.value),
|
||||
) {
|
||||
|
||||
enum class CurrencyType(val value: String) {
|
||||
Token(value = "Token"), AppCurrency(value = "App Currency")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Topup(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
package com.tangem.tap.common.entities
|
||||
|
||||
import com.tangem.tap.features.send.redux.states.ButtonState
|
||||
|
||||
open class Button(val enabled: Boolean)
|
||||
|
||||
open class IndeterminateProgressButton(
|
||||
val state: ButtonState,
|
||||
) : Button(state != ButtonState.DISABLED) {
|
||||
|
||||
val progressState: ProgressState
|
||||
get() = when (state) {
|
||||
ButtonState.PROGRESS -> ProgressState.Loading
|
||||
else -> ProgressState.Done
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun Bitmap.toByteArray(): ByteArray {
|
||||
val stream = ByteArrayOutputStream()
|
||||
this.compress(Bitmap.CompressFormat.JPEG, 20, stream)
|
||||
return stream.toByteArray()
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import com.tangem.common.extensions.isZero
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
|
|
@ -25,20 +24,4 @@ fun BigDecimal.toFormattedString(
|
|||
|
||||
fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString()
|
||||
|
||||
// 0.00 -> 0.00
|
||||
// 0.00002345 -> 0.00002
|
||||
// 1.00002345 -> 1.00
|
||||
// 1.45002345 -> 1.45
|
||||
fun BigDecimal.scaleToFiat(applyPrecision: Boolean = false): BigDecimal {
|
||||
if (this.isZero()) return this
|
||||
|
||||
val scaledFiat = this.setScale(2, RoundingMode.DOWN)
|
||||
return if (scaledFiat.isZero() && applyPrecision) this.setPrecision(1) else scaledFiat
|
||||
}
|
||||
|
||||
fun BigDecimal.setPrecision(precision: Int, roundingMode: RoundingMode = RoundingMode.DOWN): BigDecimal {
|
||||
if (precision == precision() || scale() <= precision) return this
|
||||
return this.setScale(scale() - precision() + precision, roundingMode)
|
||||
}
|
||||
|
||||
fun BigDecimal.isPositive(): Boolean = this.signum() == 1
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.webkit.WebView
|
||||
|
||||
fun WebView.stop() {
|
||||
stopLoading()
|
||||
pauseTimers()
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
package com.tangem.tap.common.leapfrogWidget
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidget
|
||||
import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidgetState
|
||||
import com.tangem.tap.domain.twins.TwinsCardWidget
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.TestLeapfrogFragmentBinding
|
||||
|
||||
class TestLeapfrogFragment : Fragment(R.layout.test_leapfrog_fragment) {
|
||||
|
||||
private lateinit var twinsCardWidget: TwinsCardWidget
|
||||
private val binding: TestLeapfrogFragmentBinding by viewBinding(TestLeapfrogFragmentBinding::bind)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val inflater = TransitionInflater.from(requireContext())
|
||||
exitTransition = inflater.inflateTransition(R.transition.fade)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val leapfrogContainer: FrameLayout = view.findViewById(R.id.leapfrog_views_container)
|
||||
val leapfrog = LeapfrogWidget(leapfrogContainer)
|
||||
twinsCardWidget = TwinsCardWidget(leapfrog) { 200f }
|
||||
|
||||
binding.btnTwinWelcome.setOnClickListener {
|
||||
twinsCardWidget.toWelcome()
|
||||
}
|
||||
binding.btnTwinToLeapfrog.setOnClickListener {
|
||||
twinsCardWidget.toLeapfrog()
|
||||
}
|
||||
binding.btnTwinActivate.setOnClickListener {
|
||||
twinsCardWidget.toActivate()
|
||||
}
|
||||
|
||||
binding.btnLpInit.setOnClickListener {
|
||||
leapfrog.initViews()
|
||||
}
|
||||
binding.btnLpUnfold.setOnClickListener {
|
||||
leapfrog.unfold()
|
||||
}
|
||||
binding.btnLpFold.setOnClickListener {
|
||||
leapfrog.fold()
|
||||
}
|
||||
binding.btnLpLeap.setOnClickListener {
|
||||
leapfrog.leap()
|
||||
}
|
||||
binding.btnLpLeapBack.setOnClickListener {
|
||||
leapfrog.leapBack()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
leapfrogWidgetState?.let { twinsCardWidget.leapfrogWidget.applyState(it) }
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
leapfrogWidgetState = twinsCardWidget.leapfrogWidget.getState()
|
||||
}
|
||||
}
|
||||
|
||||
private var leapfrogWidgetState: LeapfrogWidgetState? = null
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
package com.tangem.tap.common.recyclerView
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.tangem.sdk.extensions.dpToPx
|
||||
|
||||
class SpaceItemDecoration(
|
||||
private val horizontalSpaceDp: Float,
|
||||
private val verticalSpaceDp: Float,
|
||||
) : RecyclerView.ItemDecoration() {
|
||||
|
||||
private lateinit var space: Space
|
||||
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
if (state.itemCount == 0) return
|
||||
if (!::space.isInitialized) {
|
||||
space = Space(
|
||||
view.dpToPx(horizontalSpaceDp).toInt(),
|
||||
view.dpToPx(verticalSpaceDp).toInt(),
|
||||
)
|
||||
}
|
||||
|
||||
outRect.left = space.horizontal
|
||||
outRect.right = space.horizontal
|
||||
|
||||
when (state.itemCount) {
|
||||
1 -> {
|
||||
outRect.top = space.vertical
|
||||
outRect.bottom = space.vertical
|
||||
}
|
||||
else -> {
|
||||
val adapterPosition = parent.getChildAdapterPosition(view)
|
||||
if (adapterPosition == -1) return
|
||||
|
||||
when (adapterPosition) {
|
||||
0 -> {
|
||||
// first
|
||||
outRect.top = space.vertical
|
||||
outRect.bottom = space.vertical / 2
|
||||
}
|
||||
state.itemCount - 1 -> {
|
||||
// last
|
||||
outRect.top = space.vertical / 2
|
||||
outRect.bottom = space.vertical
|
||||
}
|
||||
else -> {
|
||||
// middle
|
||||
outRect.top = space.vertical / 2
|
||||
outRect.bottom = space.vertical / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class Space(
|
||||
val horizontal: Int,
|
||||
val vertical: Int,
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun all(dp: Float): SpaceItemDecoration = SpaceItemDecoration(dp, dp)
|
||||
fun vertical(dp: Float): SpaceItemDecoration = SpaceItemDecoration(0f, dp)
|
||||
fun horizontal(dp: Float): SpaceItemDecoration = SpaceItemDecoration(dp, 0f)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@ import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOt
|
|||
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.send.redux.reducers.SendScreenReducer
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokensReducer
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeReducer
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
|
|
@ -28,7 +27,6 @@ fun appReducer(action: Action, state: AppState?, appStateHolder: AppStateHolder)
|
|||
onboardingWalletState = OnboardingWalletReducer.reduce(action, state),
|
||||
onboardingOtherCardsState = OnboardingOtherCardsReducer.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),
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWallet
|
|||
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.send.redux.middlewares.SendMiddleware
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
import com.tangem.tap.features.tokens.legacy.redux.TokensState
|
||||
import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeMiddleware
|
||||
|
|
@ -44,7 +42,6 @@ data class AppState(
|
|||
val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(),
|
||||
val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(),
|
||||
val twinCardsState: TwinCardsState = TwinCardsState(),
|
||||
val sendState: SendState = SendState(),
|
||||
val detailsState: DetailsState = DetailsState(),
|
||||
val disclaimerState: DisclaimerState = DisclaimerState(),
|
||||
val tokensState: TokensState = TokensState(),
|
||||
|
|
@ -77,7 +74,6 @@ data class AppState(
|
|||
OnboardingWalletMiddleware.handler,
|
||||
OnboardingOtherCardsMiddleware.handler,
|
||||
TwinCardsMiddleware.handler,
|
||||
SendMiddleware().sendMiddleware,
|
||||
DetailsMiddleware().detailsMiddleware,
|
||||
DisclaimerMiddleware().disclaimerMiddleware,
|
||||
WalletConnectMiddleware().walletConnectMiddleware,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.tangem.tap.common.redux.DebugErrorAction
|
|||
import com.tangem.tap.common.redux.ErrorAction
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import org.rekotlin.Action
|
||||
|
||||
|
|
@ -71,8 +70,6 @@ sealed class GlobalAction : Action {
|
|||
val walletPublicKey: ByteArray,
|
||||
) : GlobalAction()
|
||||
|
||||
data class HideWarningMessage(val warning: WarningMessage) : GlobalAction()
|
||||
|
||||
data class SetConfigManager(val configManager: ConfigManager) : GlobalAction()
|
||||
data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction()
|
||||
data class SetFeedbackManager(val feedbackManager: LegacyFeedbackManager) : GlobalAction()
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.network.exchangeServices.BuyExchangeService
|
||||
import com.tangem.tap.network.exchangeServices.CardExchangeRules
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
|
|
@ -56,18 +55,6 @@ private fun handleAction(action: Action, appState: () -> AppState?) {
|
|||
is GlobalAction.RestoreAppCurrency -> {
|
||||
restoreAppCurrency()
|
||||
}
|
||||
is GlobalAction.HideWarningMessage -> {
|
||||
store.state.globalState.warningManager?.let {
|
||||
if (it.hideWarning(action.warning)) {
|
||||
// if (WarningMessagesManager.isAlreadySignedHashesWarning()) {
|
||||
// // TODO: No appropriate warningMessage identification. Make it better later
|
||||
// store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId)
|
||||
// }
|
||||
|
||||
store.dispatch(SendAction.Warnings.Update)
|
||||
}
|
||||
}
|
||||
}
|
||||
is GlobalAction.SendEmail -> {
|
||||
store.state.globalState.feedbackManager?.sendEmail(
|
||||
feedbackData = action.feedbackData,
|
||||
|
|
|
|||
|
|
@ -1,97 +0,0 @@
|
|||
package com.tangem.tap.common.snackBar
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.ContentViewCallback
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.sdk.extensions.dpToPx
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class MaxAmountSnackbar(
|
||||
parent: ViewGroup,
|
||||
content: MaxAmountSnackbarView,
|
||||
) : BaseTransientBottomBar<MaxAmountSnackbar>(parent, content, content) {
|
||||
|
||||
companion object {
|
||||
|
||||
fun make(view: View, onClick: () -> Unit): MaxAmountSnackbar {
|
||||
val parent = view.findSuitableParent() ?: throw IllegalArgumentException(
|
||||
"No suitable parent found from the given view. Please provide a valid view.",
|
||||
)
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val customView = inflater.inflate(R.layout.view_snackbar_max_amount, parent, false) as MaxAmountSnackbarView
|
||||
customView.setOnClickListener { onClick() }
|
||||
|
||||
return MaxAmountSnackbar(parent, customView).apply {
|
||||
updateBottomMargin()
|
||||
duration = Snackbar.LENGTH_INDEFINITE
|
||||
}
|
||||
}
|
||||
|
||||
private fun MaxAmountSnackbar.updateBottomMargin() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(this.view) { _, insets ->
|
||||
val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
|
||||
val bottomInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
|
||||
|
||||
this.view.updateLayoutParams<CoordinatorLayout.LayoutParams> {
|
||||
bottomMargin = imeInsets - bottomInsets + context.dpToPx(dp = 8f).toInt()
|
||||
}
|
||||
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
private fun View?.findSuitableParent(): ViewGroup? {
|
||||
var view = this
|
||||
var fallback: ViewGroup? = null
|
||||
do {
|
||||
if (view is CoordinatorLayout) {
|
||||
return view
|
||||
} else if (view is FrameLayout) {
|
||||
if (view.id == android.R.id.content) {
|
||||
return view
|
||||
} else {
|
||||
fallback = view
|
||||
}
|
||||
}
|
||||
|
||||
if (view != null) {
|
||||
val parent = view.parent
|
||||
view = if (parent is View) parent else null
|
||||
}
|
||||
} while (view != null)
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MaxAmountSnackbarView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr), ContentViewCallback {
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.view_snackbar_max_amount_content, this)
|
||||
clipToPadding = false
|
||||
}
|
||||
|
||||
override fun animateContentIn(delay: Int, duration: Int) {
|
||||
}
|
||||
|
||||
override fun animateContentOut(delay: Int, duration: Int) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package com.tangem.tap.common.text
|
||||
|
||||
import android.text.InputFilter
|
||||
import android.text.Spanned
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class DecimalDigitsInputFilter(
|
||||
digitsBeforeDecimal: Int,
|
||||
digitsAfterDecimal: Int,
|
||||
private val decimalSeparator: String,
|
||||
) : InputFilter {
|
||||
private val pattern: Pattern = Pattern.compile(
|
||||
"(([1-9]{1}[0-9]{0,${digitsBeforeDecimal - 1}})?||[0]{1})" +
|
||||
"((\\$decimalSeparator[0-9]{0,$digitsAfterDecimal})?)||(\\$decimalSeparator)?",
|
||||
)
|
||||
|
||||
override fun filter(
|
||||
source: CharSequence,
|
||||
sourceStart: Int,
|
||||
sourceEnd: Int,
|
||||
destination: Spanned,
|
||||
destinationStart: Int,
|
||||
destinationEnd: Int,
|
||||
): CharSequence? {
|
||||
val destString = destination.toString()
|
||||
val prefix = destString.substring(0, destinationStart)
|
||||
val suffix = destString.substring(destinationEnd, destString.length)
|
||||
val newDestination = prefix + suffix
|
||||
|
||||
val resultPrefix = newDestination.substring(0, destinationStart)
|
||||
val resultSuffix = newDestination.substring(destinationStart, newDestination.length)
|
||||
val result = resultPrefix + source.toString() + resultSuffix
|
||||
|
||||
return if (pattern.matcher(result).matches()) {
|
||||
null
|
||||
} else {
|
||||
val replacedWithAppropriateDecimalSeparator = setDecimalSeparator(result, decimalSeparator)
|
||||
if (pattern.matcher(replacedWithAppropriateDecimalSeparator).matches()) {
|
||||
decimalSeparator
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun setDecimalSeparator(value: String, decimalSeparator: String): String {
|
||||
if (value.contains(decimalSeparator)) return value
|
||||
|
||||
return if (decimalSeparator == ".") {
|
||||
value.replace(",", decimalSeparator)
|
||||
} else {
|
||||
value.replace(".", decimalSeparator)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
package com.tangem.tap.common.text
|
||||
|
||||
import android.widget.TextView
|
||||
import com.tangem.tap.common.extensions.isEven
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
enum class TruncateType {
|
||||
START, MIDDLE, END
|
||||
}
|
||||
|
||||
interface Truncate {
|
||||
fun apply(tv: TextView, text: String, with: String): String
|
||||
|
||||
companion object {
|
||||
fun create(type: TruncateType): Truncate {
|
||||
return when (type) {
|
||||
TruncateType.START -> TruncateStart()
|
||||
TruncateType.MIDDLE -> TruncateMiddle()
|
||||
TruncateType.END -> TruncateEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BaseTruncate : Truncate {
|
||||
protected var hasBeenTruncated = false
|
||||
|
||||
override fun apply(tv: TextView, text: String, with: String): String {
|
||||
val roughLength = getRoughFitLength(tv, text)
|
||||
val fittedText = preciseFitting(tv, roughTruncate(text, roughLength), with)
|
||||
return if (hasBeenTruncated) attachWith(fittedText, with) else fittedText
|
||||
}
|
||||
|
||||
private fun getRoughFitLength(tv: TextView, text: String): Int {
|
||||
val existingSpace = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd)
|
||||
val textWillTakeSpace = tv.paint.measureText(text)
|
||||
val overSizeRatio: Float = textWillTakeSpace / existingSpace
|
||||
|
||||
val maxLengthOfText = text.length / overSizeRatio
|
||||
if (text.length <= maxLengthOfText) return text.length
|
||||
|
||||
return maxLengthOfText.toInt()
|
||||
}
|
||||
|
||||
private fun preciseFitting(tv: TextView, text: String, with: String): String {
|
||||
if (!hasBeenTruncated) return text
|
||||
|
||||
val spaceForText = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd)
|
||||
|
||||
var fittedText = text
|
||||
while (tv.paint.measureText(fittedText + with) > spaceForText) {
|
||||
fittedText = preciseTruncate(fittedText)
|
||||
}
|
||||
return fittedText
|
||||
}
|
||||
|
||||
protected abstract fun roughTruncate(text: String, residualLength: Int): String
|
||||
protected abstract fun preciseTruncate(text: String): String
|
||||
protected abstract fun attachWith(text: String, with: String): String
|
||||
}
|
||||
|
||||
class TruncateStart : BaseTruncate() {
|
||||
override fun roughTruncate(text: String, residualLength: Int): String {
|
||||
if (text.length <= residualLength) return text
|
||||
|
||||
hasBeenTruncated = true
|
||||
return text.substring(residualLength, text.length)
|
||||
}
|
||||
|
||||
override fun preciseTruncate(text: String): String = text.substring(1, text.length)
|
||||
|
||||
override fun attachWith(text: String, with: String): String = with + text
|
||||
}
|
||||
|
||||
class TruncateMiddle : BaseTruncate() {
|
||||
|
||||
override fun roughTruncate(text: String, residualLength: Int): String {
|
||||
if (text.length <= residualLength || residualLength < 0) return text
|
||||
|
||||
hasBeenTruncated = true
|
||||
val halfOfResidualLength = residualLength / 2
|
||||
val leftSide = text.substring(0, halfOfResidualLength)
|
||||
val rightSide = text.substring(text.length - halfOfResidualLength, text.length)
|
||||
|
||||
return leftSide + rightSide
|
||||
}
|
||||
|
||||
override fun preciseTruncate(text: String): String {
|
||||
val middlePosition = text.length / 2
|
||||
return if (text.length.isEven()) {
|
||||
val leftSide = text.substring(0, middlePosition - 1)
|
||||
val rightSide = text.substring(middlePosition, text.length)
|
||||
leftSide + rightSide
|
||||
} else {
|
||||
val leftSide = text.substring(0, middlePosition)
|
||||
val rightSide = text.substring(middlePosition + 1, text.length)
|
||||
leftSide + rightSide
|
||||
}
|
||||
}
|
||||
|
||||
override fun attachWith(text: String, with: String): String {
|
||||
val cuttingPosition = text.length / 2
|
||||
val leftSide = text.substring(0, cuttingPosition)
|
||||
val rightSide = text.substring(cuttingPosition, text.length)
|
||||
return leftSide + with + rightSide
|
||||
}
|
||||
}
|
||||
|
||||
class TruncateEnd : BaseTruncate() {
|
||||
override fun roughTruncate(text: String, residualLength: Int): String {
|
||||
if (text.length <= residualLength) return text
|
||||
|
||||
hasBeenTruncated = true
|
||||
return text.substring(0, residualLength)
|
||||
}
|
||||
|
||||
override fun preciseTruncate(text: String): String = text.substring(0, text.length - 1)
|
||||
|
||||
override fun attachWith(text: String, with: String): String = text + with
|
||||
}
|
||||
|
||||
fun TextView.truncateWith(text: String, type: TruncateType, with: String = "..."): String {
|
||||
val truncate = Truncate.create(type)
|
||||
return truncate.apply(this, text, with)
|
||||
}
|
||||
|
||||
fun TextView.truncateMiddleWith(text: String, with: String = "..."): String =
|
||||
this.truncateWith(text, TruncateType.MIDDLE, with)
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
package com.tangem.tap.common.toggleWidget
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.tangem.tap.common.entities.ProgressState
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
open class IndeterminateProgressButtonWidget(
|
||||
private val button: MaterialButton,
|
||||
private val progress: View,
|
||||
initialState: ProgressState = ProgressState.Done,
|
||||
) : ViewStateWidget {
|
||||
|
||||
private var text: CharSequence = button.text
|
||||
private var icon: Drawable? = button.icon
|
||||
private var iconGravity: Int? = button.iconGravity
|
||||
|
||||
init {
|
||||
if (initialState != ProgressState.Done) changeState(initialState)
|
||||
}
|
||||
|
||||
var isEnabled: Boolean
|
||||
get() = button.isEnabled
|
||||
set(value) {
|
||||
button.isEnabled = value
|
||||
}
|
||||
|
||||
override val mainView: View = button
|
||||
|
||||
override fun changeState(state: WidgetState) {
|
||||
val progressState = state as? ProgressState ?: return
|
||||
|
||||
when (progressState) {
|
||||
ProgressState.Done, ProgressState.Error -> switchToNone()
|
||||
ProgressState.Loading -> switchToProgress()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun switchToNone() {
|
||||
button.isClickable = true
|
||||
button.text = text
|
||||
button.icon = icon
|
||||
iconGravity?.let { button.iconGravity = it }
|
||||
|
||||
progress.hide()
|
||||
}
|
||||
|
||||
protected open fun switchToProgress() {
|
||||
button.isClickable = false
|
||||
button.text = ""
|
||||
button.icon = null
|
||||
|
||||
progress.show()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue