Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-07 19:02:08 +05:00
parent 459db3d0df
commit f43f6299d1
62 changed files with 2 additions and 5914 deletions

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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(),

View file

@ -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
}
}

View file

@ -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()
}

View file

@ -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

View file

@ -1,8 +0,0 @@
package com.tangem.tap.common.extensions
import android.webkit.WebView
fun WebView.stop() {
stopLoading()
pauseTimers()
}

View file

@ -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

View file

@ -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)
}
}

View file

@ -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),

View file

@ -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,

View file

@ -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()

View file

@ -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,

View file

@ -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) {
}
}

View file

@ -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)
}
}
}
}

View file

@ -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)

View file

@ -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()
}
}