Avoid crash due to unsupported currency locale.
- Avoided a crash in ConnectedActivity when trying to obtain the Locale's associated currency, when a Locale does not have a currency. - Standardized the process to obtain the coingecko supported currency, which will first try to use the current locale's currency and fallback to USD in case the first is not supported.
This commit is contained in:
parent
766d42386a
commit
9cabc0565a
7 changed files with 66 additions and 117 deletions
|
@ -19,6 +19,7 @@ import cy.agorise.bitsybitshareswallet.database.entities.Transfer
|
||||||
import cy.agorise.bitsybitshareswallet.processors.TransfersLoader
|
import cy.agorise.bitsybitshareswallet.processors.TransfersLoader
|
||||||
import cy.agorise.bitsybitshareswallet.repositories.AssetRepository
|
import cy.agorise.bitsybitshareswallet.repositories.AssetRepository
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
|
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceViewModel
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.ConnectedActivityViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.ConnectedActivityViewModel
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.TransferViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.TransferViewModel
|
||||||
|
@ -121,7 +122,9 @@ abstract class ConnectedActivity : AppCompatActivity() {
|
||||||
mConnectedActivityViewModel = ViewModelProviders.of(this).get(ConnectedActivityViewModel::class.java)
|
mConnectedActivityViewModel = ViewModelProviders.of(this).get(ConnectedActivityViewModel::class.java)
|
||||||
|
|
||||||
val currency = Currency.getInstance(Locale.getDefault())
|
val currency = Currency.getInstance(Locale.getDefault())
|
||||||
mConnectedActivityViewModel.observeMissingEquivalentValuesIn(currency.currencyCode) //TODO: Obtain this from shared preferences?
|
val currencyCode = Helper.getCoingeckoSupportedCurrency(currency.currencyCode)
|
||||||
|
Log.d(TAG, "Using currency: ${currencyCode.toUpperCase(Locale.ROOT)}")
|
||||||
|
mConnectedActivityViewModel.observeMissingEquivalentValuesIn(currencyCode)
|
||||||
|
|
||||||
// Configure UserAccountViewModel to obtain the missing account ids
|
// Configure UserAccountViewModel to obtain the missing account ids
|
||||||
mUserAccountViewModel = ViewModelProviders.of(this).get(UserAccountViewModel::class.java)
|
mUserAccountViewModel = ViewModelProviders.of(this).get(UserAccountViewModel::class.java)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import cy.agorise.bitsybitshareswallet.adapters.BalancesDetailsAdapter
|
||||||
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
||||||
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
|
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
||||||
import cy.agorise.bitsybitshareswallet.views.DatePickerFragment
|
import cy.agorise.bitsybitshareswallet.views.DatePickerFragment
|
||||||
import kotlinx.android.synthetic.main.dialog_filter_options.*
|
import kotlinx.android.synthetic.main.dialog_filter_options.*
|
||||||
|
@ -158,9 +159,9 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
||||||
llEquivalentValue.visibility = if(isChecked) View.GONE else View.VISIBLE }
|
llEquivalentValue.visibility = if(isChecked) View.GONE else View.VISIBLE }
|
||||||
cbEquivalentValue.isChecked = mFilterOptions.equivalentValueAll
|
cbEquivalentValue.isChecked = mFilterOptions.equivalentValueAll
|
||||||
|
|
||||||
// TODO obtain user selected currency
|
val currency = Currency.getInstance(Locale.getDefault())
|
||||||
val currencySymbol = "usd"
|
val currencyCode = Helper.getCoingeckoSupportedCurrency(currency.currencyCode)
|
||||||
mCurrency = Currency.getInstance(currencySymbol)
|
mCurrency = Currency.getInstance(currencyCode)
|
||||||
|
|
||||||
val fromEquivalentValue = mFilterOptions.fromEquivalentValue /
|
val fromEquivalentValue = mFilterOptions.fromEquivalentValue /
|
||||||
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
||||||
|
@ -170,7 +171,7 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
||||||
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
||||||
etToEquivalentValue.setText("$toEquivalentValue", TextView.BufferType.EDITABLE)
|
etToEquivalentValue.setText("$toEquivalentValue", TextView.BufferType.EDITABLE)
|
||||||
|
|
||||||
tvEquivalentValueSymbol.text = currencySymbol.toUpperCase()
|
tvEquivalentValueSymbol.text = currencyCode.toUpperCase(Locale.getDefault())
|
||||||
|
|
||||||
// Initialize transaction network fees
|
// Initialize transaction network fees
|
||||||
switchAgoriseFees.isChecked = mFilterOptions.agoriseFees
|
switchAgoriseFees.isChecked = mFilterOptions.agoriseFees
|
||||||
|
|
|
@ -13,8 +13,4 @@ interface CoingeckoService {
|
||||||
fun getHistoricalValueSync(@Query("id") id: String,
|
fun getHistoricalValueSync(@Query("id") id: String,
|
||||||
@Query("date") date: String,
|
@Query("date") date: String,
|
||||||
@Query("localization") localization: Boolean): Call<HistoricalPrice>
|
@Query("localization") localization: Boolean): Call<HistoricalPrice>
|
||||||
|
|
||||||
@Headers("Content-Type: application/json")
|
|
||||||
@GET("/api/v3/simple/supported_vs_currencies")
|
|
||||||
fun getSupportedCurrencies(): Call<Array<String>>
|
|
||||||
}
|
}
|
|
@ -14,7 +14,6 @@ import cy.agorise.bitsybitshareswallet.database.entities.Transfer
|
||||||
import cy.agorise.bitsybitshareswallet.network.CoingeckoService
|
import cy.agorise.bitsybitshareswallet.network.CoingeckoService
|
||||||
import cy.agorise.bitsybitshareswallet.network.ServiceGenerator
|
import cy.agorise.bitsybitshareswallet.network.ServiceGenerator
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import io.reactivex.Observable
|
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
@ -164,41 +163,4 @@ class TransferRepository internal constructor(context: Context) {
|
||||||
if(!compositeDisposable.isDisposed)
|
if(!compositeDisposable.isDisposed)
|
||||||
compositeDisposable.clear()
|
compositeDisposable.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Method used to override a given currency if it turns out not to be supported by the API.
|
|
||||||
* <p>
|
|
||||||
* The CoinGecko API supports 20+ fiat currencies. So we can very easily calculate historical
|
|
||||||
* equivalent values for those currencies. If the currency is not supported though,
|
|
||||||
* we must fall back to USD.
|
|
||||||
*
|
|
||||||
* @param symbol The 3 letters symbol of the currency
|
|
||||||
*/
|
|
||||||
fun getSupportedCurrency(symbol: String): Observable<String> {
|
|
||||||
return Observable.just(symbol)
|
|
||||||
.map {
|
|
||||||
val sg = ServiceGenerator(Constants.COINGECKO_URL)
|
|
||||||
val response = sg.getService(CoingeckoService::class.java)
|
|
||||||
?.getSupportedCurrencies()
|
|
||||||
?.execute()
|
|
||||||
// Updating the supported currencies cache
|
|
||||||
mPreferences.edit()
|
|
||||||
.putStringSet(Constants.KEY_COINGECKO_CURRENCIES_CACHE, response?.body()?.toMutableSet() ?: setOf())
|
|
||||||
.apply()
|
|
||||||
if(response?.body()?.indexOf(symbol.toLowerCase()) == -1)
|
|
||||||
"usd"
|
|
||||||
else
|
|
||||||
it
|
|
||||||
}
|
|
||||||
.onErrorReturn {
|
|
||||||
// Error caused potentially by the lack of connectivity. If this happens we just
|
|
||||||
// retrieve the value from the cache
|
|
||||||
val currencies = mPreferences.getStringSet(Constants.KEY_COINGECKO_CURRENCIES_CACHE, setOf())
|
|
||||||
var selectedCurrency = "usd"
|
|
||||||
if(currencies.contains(symbol)) {
|
|
||||||
selectedCurrency = symbol
|
|
||||||
}
|
|
||||||
selectedCurrency
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -10,14 +10,13 @@ import androidx.core.content.FileProvider
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains methods that are helpful in different parts of the app
|
* Contains methods that are helpful in different parts of the app
|
||||||
*/
|
*/
|
||||||
class Helper {
|
object Helper {
|
||||||
|
private const val TAG = "Helper"
|
||||||
companion object {
|
|
||||||
private val TAG = "Helper"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and returns a Bitmap from the contents of a View, does not matter
|
* Creates and returns a Bitmap from the contents of a View, does not matter
|
||||||
|
@ -42,7 +41,7 @@ class Helper {
|
||||||
// don't forget to make the directory
|
// don't forget to make the directory
|
||||||
Log.d(TAG, "shareBitmapImage creating cache images folder")
|
Log.d(TAG, "shareBitmapImage creating cache images folder")
|
||||||
|
|
||||||
val stream = FileOutputStream(cachePath.toString() + "/image.png") // overwrites this image every time
|
val stream = FileOutputStream("$cachePath/image.png") // overwrites this image every time
|
||||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
|
||||||
stream.close()
|
stream.close()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
|
@ -56,5 +55,19 @@ class Helper {
|
||||||
// Create and return image uri
|
// Create and return image uri
|
||||||
return FileProvider.getUriForFile(context, "cy.agorise.bitsybitshareswallet.FileProvider", newFile)
|
return FileProvider.getUriForFile(context, "cy.agorise.bitsybitshareswallet.FileProvider", newFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the given currency code is supported, returns it, else returns the default one.
|
||||||
|
*/
|
||||||
|
fun getCoingeckoSupportedCurrency(currencyCode: String): String {
|
||||||
|
val supportedCurrencies = setOf("usd", "aed", "ars", "aud", "bdt", "bhd", "bmd", "brl", "cad",
|
||||||
|
"chf", "clp", "cny", "czk", "dkk", "eur", "gbp", "hkd", "huf", "idr", "ils", "inr", "jpy",
|
||||||
|
"krw", "kwd", "lkr", "mmk", "mxn", "myr", "nok", "nzd", "php", "pkr", "pln", "rub", "sar",
|
||||||
|
"sek", "sgd", "thb", "try", "twd", "uah", "vef", "vnd", "zar", "xdr", "xag", "xau")
|
||||||
|
|
||||||
|
return if (currencyCode.toLowerCase(Locale.ROOT) in supportedCurrencies)
|
||||||
|
currencyCode
|
||||||
|
else
|
||||||
|
"usd"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,12 @@
|
||||||
package cy.agorise.bitsybitshareswallet.viewmodels
|
package cy.agorise.bitsybitshareswallet.viewmodels
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.util.Log
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
|
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
|
||||||
import cy.agorise.bitsybitshareswallet.repositories.EquivalentValuesRepository
|
import cy.agorise.bitsybitshareswallet.repositories.EquivalentValuesRepository
|
||||||
import cy.agorise.bitsybitshareswallet.repositories.NodeRepository
|
import cy.agorise.bitsybitshareswallet.repositories.NodeRepository
|
||||||
import cy.agorise.bitsybitshareswallet.repositories.TransferRepository
|
import cy.agorise.bitsybitshareswallet.repositories.TransferRepository
|
||||||
import cy.agorise.graphenej.network.FullNode
|
import cy.agorise.graphenej.network.FullNode
|
||||||
import io.reactivex.schedulers.Schedulers
|
|
||||||
|
|
||||||
class ConnectedActivityViewModel(application: Application) : AndroidViewModel(application) {
|
class ConnectedActivityViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -24,17 +22,7 @@ class ConnectedActivityViewModel(application: Application) : AndroidViewModel(ap
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observeMissingEquivalentValuesIn(symbol: String) {
|
fun observeMissingEquivalentValuesIn(symbol: String) {
|
||||||
mTransfersRepository.getSupportedCurrency(symbol)
|
mTransfersRepository.observeMissingEquivalentValuesIn(symbol)
|
||||||
.observeOn(Schedulers.io())
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.subscribe({
|
|
||||||
currency -> mTransfersRepository.observeMissingEquivalentValuesIn(currency)
|
|
||||||
},{
|
|
||||||
Log.e(TAG,"Error while trying to subscribe to missing equivalent values observer. Msg: ${it.message}")
|
|
||||||
for(element in it.stackTrace){
|
|
||||||
Log.e(TAG,"${element.className}#${element.methodName}:${element.lineNumber}")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateNodeLatencies(nodes: List<FullNode>) {
|
fun updateNodeLatencies(nodes: List<FullNode>) {
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
package cy.agorise.bitsybitshareswallet.viewmodels
|
package cy.agorise.bitsybitshareswallet.viewmodels
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.util.Log
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.*
|
||||||
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
|
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
|
||||||
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
||||||
import cy.agorise.bitsybitshareswallet.repositories.TransferDetailRepository
|
import cy.agorise.bitsybitshareswallet.repositories.TransferDetailRepository
|
||||||
import cy.agorise.bitsybitshareswallet.repositories.TransferRepository
|
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
|
||||||
import io.reactivex.schedulers.Schedulers
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -16,10 +13,9 @@ import java.util.*
|
||||||
|
|
||||||
class TransactionsViewModel(application: Application) : AndroidViewModel(application) {
|
class TransactionsViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
companion object {
|
companion object {
|
||||||
val TAG = "TransactionsViewModel"
|
const val TAG = "TransactionsViewModel"
|
||||||
}
|
}
|
||||||
private var mRepository = TransferDetailRepository(application)
|
private var mRepository = TransferDetailRepository(application)
|
||||||
private var mTransfersRepository = TransferRepository(application)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [FilterOptions] used to filter the list of [TransferDetail] taken from the database
|
* [FilterOptions] used to filter the list of [TransferDetail] taken from the database
|
||||||
|
@ -44,24 +40,14 @@ class TransactionsViewModel(application: Application) : AndroidViewModel(applica
|
||||||
|
|
||||||
internal fun getFilteredTransactions(userId: String): LiveData<List<TransferDetail>> {
|
internal fun getFilteredTransactions(userId: String): LiveData<List<TransferDetail>> {
|
||||||
val currency = Currency.getInstance(Locale.getDefault())
|
val currency = Currency.getInstance(Locale.getDefault())
|
||||||
mTransfersRepository.getSupportedCurrency(currency.currencyCode)
|
val currencyCode = Helper.getCoingeckoSupportedCurrency(currency.currencyCode)
|
||||||
.subscribeOn(Schedulers.io())
|
transactions = mRepository.getAll(userId, currencyCode)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe({
|
|
||||||
Log.d(TAG,"Looking for currency with code: ${it}")
|
|
||||||
transactions = mRepository.getAll(userId, it)
|
|
||||||
|
|
||||||
filteredTransactions.addSource(transactions) { transactions ->
|
filteredTransactions.addSource(transactions) { transactions ->
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
filteredTransactions.value = filter(transactions, mFilterOptions)
|
filteredTransactions.value = filter(transactions, mFilterOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},{
|
|
||||||
Log.e(TAG,"Error while trying to obtain a filtered list of transactions. Msg: ${it.message}")
|
|
||||||
for(element in it.stackTrace){
|
|
||||||
Log.e(ConnectedActivityViewModel.TAG,"${element.className}#${element.methodName}:${element.lineNumber}")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return filteredTransactions
|
return filteredTransactions
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue