Updated on 2026-08-14

This commit is contained in:
Tangem 2020-09-08 15:50:55 +03:00
parent 9b3f6994a9
commit 98ea9b0ead
25 changed files with 648 additions and 88 deletions

View file

@ -0,0 +1,23 @@
package com.tangem.tap.common
import com.tangem.common.extensions.isZero
import java.math.BigDecimal
import java.math.RoundingMode
/**
[REDACTED_AUTHOR]
*/
class CurrencyConverter(var rateValue: BigDecimal) {
fun toFiat(crypto: BigDecimal, decimals: Int = 2): BigDecimal {
return rateValue.multiply(crypto).setScale(decimals, RoundingMode.DOWN)
}
fun toCrypto(fiat: BigDecimal, decimals: Int): BigDecimal {
if (fiat.isZero() || rateValue.isZero()) return fiat
val scaledRateValue = rateValue.setScale(decimals, RoundingMode.DOWN)
val scaledFiat = fiat.setScale(decimals, RoundingMode.DOWN)
return scaledFiat.divide(scaledRateValue, RoundingMode.UP)
}
}

View file

@ -0,0 +1,61 @@
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

@ -0,0 +1,10 @@
package com.tangem.tap.common.entities
/**
[REDACTED_AUTHOR]
*/
class TapCurrency {
companion object{
val main = "USD"
}
}

View file

@ -42,7 +42,7 @@ fun BigDecimal.toFormattedString(decimals: Int): String {
df.minimumFractionDigits = 0
df.isGroupingUsed = false
val bd = BigDecimal(unscaledValue(), scale())
bd.setScale(decimals, BigDecimal.ROUND_DOWN)
bd.setScale(decimals, RoundingMode.DOWN)
return df.format(bd)
}
@ -50,4 +50,6 @@ fun BigDecimal.toFiatString(rateValue: BigDecimal): String? {
var fiatValue = rateValue.multiply(this)
fiatValue = fiatValue.setScale(2, RoundingMode.DOWN)
return "USD $fiatValue"
}
}
fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString()

View file

@ -0,0 +1,49 @@
package com.tangem.tap.common.extensions
import android.view.View
import android.widget.EditText
import android.widget.TextSwitcher
import android.widget.TextView
import com.google.android.material.textfield.TextInputLayout
/**
[REDACTED_AUTHOR]
*/
fun TextView.update(text: String?) {
if (this.text?.toString() != text) this.text = text
}
fun EditText.update(text: String?) {
if (this.text?.toString() == text) return
val textLength = text?.length ?: 0
//prevent cursor jumping while editing a text
val cursorPosition = if (selectionEnd > textLength) textLength else selectionEnd
this.setText(text)
if (!isFocused || textLength == 0) return
if (cursorPosition == 0) setSelection(textLength)
else setSelection(cursorPosition)
}
fun TextSwitcher.update(text: String?) {
val textView = this.currentView as? TextView ?: return
if (textView.text?.toString() != text) this.setText(text)
}
// By default the TextInputLayout didn't activates the error state if the message is empty or null
fun TextInputLayout.enableError(enable: Boolean, errorMessage: String? = null) {
if (enable) {
if (errorMessage == null || errorMessage.isEmpty()) {
error = "Any message"
if (childCount == 2) getChildAt(1).visibility = View.GONE
} else {
error = errorMessage
}
} else {
error = null
isErrorEnabled = false
}
}

View file

@ -10,7 +10,6 @@ import android.os.Build
import android.text.Spannable
import android.text.style.ForegroundColorSpan
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
@ -132,11 +131,4 @@ fun Context.shareText(text: String) {
fun Fragment.shareText(text: String) {
requireContext().shareText(text)
}
fun ViewGroup.inflate(viewToInflate: Int, rootView: ViewGroup?, parent: ViewGroup) {
if (rootView == null) {
val inflatedView = LayoutInflater.from(context).inflate(viewToInflate, rootView)
parent.addView(inflatedView)
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.tap.common.extensions
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.transition.AutoTransition
import androidx.transition.Transition
import androidx.transition.TransitionManager
/**
[REDACTED_AUTHOR]
*/
fun ViewGroup.inflate(viewToInflate: Int, rootView: ViewGroup?, parent: ViewGroup) {
if (rootView == null) {
val inflatedView = LayoutInflater.from(context).inflate(viewToInflate, rootView)
parent.addView(inflatedView)
}
}
fun ViewGroup.beginDelayedTransition(transition: Transition = AutoTransition()) {
TransitionManager.beginDelayedTransition(this, transition)
}

View file

@ -0,0 +1,81 @@
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 com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.ContentViewCallback
import com.google.android.material.snackbar.Snackbar
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(view.context)
val customView = inflater.inflate(R.layout.view_snackbar_max_amount, parent, false) as MaxAmountSnackbarView
customView.setOnClickListener { onClick() }
return MaxAmountSnackbar(parent, customView).apply {
duration = Snackbar.LENGTH_INDEFINITE
}
}
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) {
}
}