Updated on 2026-08-14
This commit is contained in:
parent
9b3f6994a9
commit
98ea9b0ead
25 changed files with 648 additions and 88 deletions
23
app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt
Normal file
23
app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
61
app/src/main/java/com/tangem/tap/common/KeyboardObserver.kt
Normal file
61
app/src/main/java/com/tangem/tap/common/KeyboardObserver.kt
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.tap.common.entities
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class TapCurrency {
|
||||
companion object{
|
||||
val main = "USD"
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue