Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Commit

Permalink
Merge pull request #131 from ILIYANGERMANOV/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
ILIYANGERMANOV authored Nov 15, 2021
2 parents f769aef + 9eeac43 commit ce021a8
Show file tree
Hide file tree
Showing 23 changed files with 724 additions and 560 deletions.
2 changes: 1 addition & 1 deletion app/src/main/java/com/ivy/wallet/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ object Constants {
const val URL_IVY_CONTRIBUTORS =
"https://github.com/ILIYANGERMANOV/ivy-wallet#contributors-see-graph"

const val USER_INACTIVE_TIME_LIMIT = 300 //Time in seconds
const val USER_INACTIVITY_TIME_LIMIT = 60 //Time in seconds
}
183 changes: 183 additions & 0 deletions app/src/main/java/com/ivy/wallet/base/AmountFormatting.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package com.ivy.wallet.base

import com.ivy.wallet.model.IvyCurrency
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import kotlin.math.abs
import kotlin.math.log10
import kotlin.math.truncate

const val MILLION = 1000000
const val N_100K = 100000
const val THOUSAND = 1000

fun String.amountToDoubleOrNull(): Double? {
return this.normalizeAmount().toDoubleOrNull()
}

fun String.amountToDouble(): Double {
return this.normalizeAmount().toDouble()
}

fun String.normalizeAmount(): String {
return this.removeGroupingSeparator()
.normalizeDecimalSeparator()
}

fun String.normalizeExpression(): String {
return this.removeGroupingSeparator()
.normalizeDecimalSeparator()
}

fun String.removeGroupingSeparator(): String {
return replace(localGroupingSeparator(), "")
}

fun String.normalizeDecimalSeparator(): String {
return replace(localDecimalSeparator(), ".")
}

fun localDecimalSeparator(): String {
return DecimalFormatSymbols.getInstance().decimalSeparator.toString()
}

fun localGroupingSeparator(): String {
return DecimalFormatSymbols.getInstance().groupingSeparator.toString()
}

//Display Formatting
fun Double.format(digits: Int) = "%.${digits}f".format(this)

fun Double.format(currencyCode: String): String {
return this.format(IvyCurrency.fromCode(currencyCode))
}

fun Double.format(currency: IvyCurrency?): String {
return if (currency?.isCrypto == true) {
val result = this.formatCrypto()
return when {
result.lastOrNull() == localDecimalSeparator().firstOrNull() -> {
val newResult = result.dropLast(1)
if (newResult.isEmpty()) "0" else newResult
}
result.isEmpty() -> {
"0"
}
else -> result
}
} else {
formatFIAT()
}
}

fun Double.formatCrypto(): String {
val pattern = "###,###,##0.${"0".repeat(9)}"
val format = DecimalFormat(pattern)
val numberStringWithZeros = format.format(this)

var lastTrailingZeroIndex: Int? = null
for (i in numberStringWithZeros.lastIndex.downTo(0)) {
if (numberStringWithZeros[i] == '0') {
lastTrailingZeroIndex = i
} else {
break
}
}

return if (lastTrailingZeroIndex != null)
numberStringWithZeros.substring(0, lastTrailingZeroIndex) else numberStringWithZeros
}

private fun Double.formatFIAT(): String = DecimalFormat("#,##0.00").format(this)

fun shortenAmount(amount: Double): String {
return when {
abs(amount) >= MILLION -> {
formatShortenedNumber(amount / MILLION, "m")
}
abs(amount) >= THOUSAND -> {
formatShortenedNumber(amount / THOUSAND, "k")
}
else -> amount.toString()
}
}

private fun formatShortenedNumber(
number: Double,
extension: String
): String {
return if (hasSignificantDecimalPart(number)) {
"${number.format(2)}$extension"
} else {
"${number.toInt()}$extension"
}
}

fun hasSignificantDecimalPart(number: Double): Boolean {
//TODO: Review, might cause trouble when integrating crypto
val intPart = number.toInt()
return abs(number - intPart) >= 0.009
}

fun shouldShortAmount(amount: Double): Boolean {
return abs(amount) >= N_100K
}

fun formatInt(number: Int): String {
return DecimalFormat("#,###,###,###").format(number)
}

fun decimalPartFormatted(currency: String, value: Double): String {
return if (IvyCurrency.fromCode(currency)?.isCrypto == true) {
val decimalPartFormatted = value.formatCrypto()
.split(localDecimalSeparator())
.getOrNull(1) ?: "null"
if (decimalPartFormatted.isNotBlank())
"${localDecimalSeparator()}$decimalPartFormatted" else ""
} else {
"${localDecimalSeparator()}${decimalPartFormattedFIAT(value)}"
}
}

private fun decimalPartFormattedFIAT(value: Double): String {
return DecimalFormat(".00").format(value)
.split(localDecimalSeparator())
.getOrNull(1)
?: value.toString()
.split(localDecimalSeparator())
.getOrNull(1)
?: "null"
}

fun Long.length() = when (this) {
0L -> 1
else -> log10(abs(toDouble())).toInt() + 1
}

fun formatInputAmount(
currency: String,
amount: String,
newSymbol: String
): String? {
val newlyEnteredNumberString = amount + newSymbol

val decimalPartString = newlyEnteredNumberString
.split(localDecimalSeparator())
.getOrNull(1)
val decimalCount = decimalPartString?.length ?: 0

val amountDouble = newlyEnteredNumberString.amountToDoubleOrNull()

val decimalCountOkay = IvyCurrency.fromCode(currency)?.isCrypto == true
|| decimalCount <= 2
if (amountDouble != null && decimalCountOkay) {
val intPart = truncate(amountDouble).toInt()
val decimalPartFormatted = if (decimalPartString != null) {
"${localDecimalSeparator()}${decimalPartString}"
} else ""

return formatInt(intPart) + decimalPartFormatted
}

return null
}
107 changes: 107 additions & 0 deletions app/src/main/java/com/ivy/wallet/base/GesturesExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.ivy.wallet.base

import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.gestures.detectVerticalDragGestures
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.input.pointer.pointerInput

/**
* sensitivity - the lower the number, the higher the sensitivity
*/
fun Modifier.verticalSwipeListener(
sensitivity: Int,
onSwipeUp: () -> Unit = {},
onSwipeDown: () -> Unit = {}
): Modifier = composed {
var swipeOffset by remember {
mutableStateOf(0f)
}
var gestureConsumed by remember {
mutableStateOf(false)
}

this.pointerInput(Unit) {
detectVerticalDragGestures(
onDragEnd = {
swipeOffset = 0f
gestureConsumed = false
},
onVerticalDrag = { _, dragAmount ->
//dragAmount: positive when scrolling down; negative when scrolling up
swipeOffset += dragAmount

when {
swipeOffset > sensitivity -> {
//offset > 0 when swipe down
if (!gestureConsumed) {
onSwipeDown()
gestureConsumed = true
}
}

swipeOffset < -sensitivity -> {
//offset < 0 when swipe up
if (!gestureConsumed) {
onSwipeUp()
gestureConsumed = true
}
}
}

}
)
}
}

/**
* sensitivity - the lower the number, the higher the sensitivity
*/
fun Modifier.horizontalSwipeListener(
sensitivity: Int,
onSwipeLeft: () -> Unit = {},
onSwipeRight: () -> Unit = {}
): Modifier = composed {
var swipeOffset by remember {
mutableStateOf(0f)
}
var gestureConsumed by remember {
mutableStateOf(false)
}

this.pointerInput(Unit) {
detectHorizontalDragGestures(
onDragEnd = {
swipeOffset = 0f
gestureConsumed = false
},
onHorizontalDrag = { _, dragAmount ->
//dragAmount: positive when scrolling down; negative when scrolling up
swipeOffset += dragAmount

when {
swipeOffset > sensitivity -> {
//offset > 0 when swipe right
if (!gestureConsumed) {
onSwipeRight()
gestureConsumed = true
}
}

swipeOffset < -sensitivity -> {
//offset < 0 when swipe left
if (!gestureConsumed) {
onSwipeLeft()
gestureConsumed = true
}
}
}

}
)
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/com/ivy/wallet/base/MVVMExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext

fun <T> MutableLiveData<T>.asLiveData(): LiveData<T> {
return this
}

fun <T> MutableStateFlow<T>.asFlow(): StateFlow<T> {
return this
}

fun Fragment.args(putArgs: Bundle.() -> Unit): Fragment {
arguments = Bundle().apply { putArgs() }
return this
Expand Down
Loading

0 comments on commit ce021a8

Please sign in to comment.