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.repositories.AssetRepository
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceViewModel
|
||||
import cy.agorise.bitsybitshareswallet.viewmodels.ConnectedActivityViewModel
|
||||
import cy.agorise.bitsybitshareswallet.viewmodels.TransferViewModel
|
||||
|
@ -121,7 +122,9 @@ abstract class ConnectedActivity : AppCompatActivity() {
|
|||
mConnectedActivityViewModel = ViewModelProviders.of(this).get(ConnectedActivityViewModel::class.java)
|
||||
|
||||
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
|
||||
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.models.FilterOptions
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
||||
import cy.agorise.bitsybitshareswallet.views.DatePickerFragment
|
||||
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 }
|
||||
cbEquivalentValue.isChecked = mFilterOptions.equivalentValueAll
|
||||
|
||||
// TODO obtain user selected currency
|
||||
val currencySymbol = "usd"
|
||||
mCurrency = Currency.getInstance(currencySymbol)
|
||||
val currency = Currency.getInstance(Locale.getDefault())
|
||||
val currencyCode = Helper.getCoingeckoSupportedCurrency(currency.currencyCode)
|
||||
mCurrency = Currency.getInstance(currencyCode)
|
||||
|
||||
val fromEquivalentValue = mFilterOptions.fromEquivalentValue /
|
||||
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()
|
||||
etToEquivalentValue.setText("$toEquivalentValue", TextView.BufferType.EDITABLE)
|
||||
|
||||
tvEquivalentValueSymbol.text = currencySymbol.toUpperCase()
|
||||
tvEquivalentValueSymbol.text = currencyCode.toUpperCase(Locale.getDefault())
|
||||
|
||||
// Initialize transaction network fees
|
||||
switchAgoriseFees.isChecked = mFilterOptions.agoriseFees
|
||||
|
|
|
@ -13,8 +13,4 @@ interface CoingeckoService {
|
|||
fun getHistoricalValueSync(@Query("id") id: String,
|
||||
@Query("date") date: String,
|
||||
@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.ServiceGenerator
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import java.text.SimpleDateFormat
|
||||
|
@ -164,41 +163,4 @@ class TransferRepository internal constructor(context: Context) {
|
|||
if(!compositeDisposable.isDisposed)
|
||||
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,51 +10,64 @@ import androidx.core.content.FileProvider
|
|||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 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
|
||||
* if it is a simple view or a ViewGroup like a ConstraintLayout or a LinearLayout.
|
||||
*
|
||||
* @param view The view that is gonna be pictured.
|
||||
* @return The generated image from the given view.
|
||||
*/
|
||||
fun loadBitmapFromView(view: View): Bitmap {
|
||||
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
view.draw(canvas)
|
||||
|
||||
/**
|
||||
* Creates and returns a Bitmap from the contents of a View, does not matter
|
||||
* if it is a simple view or a ViewGroup like a ConstraintLayout or a LinearLayout.
|
||||
*
|
||||
* @param view The view that is gonna be pictured.
|
||||
* @return The generated image from the given view.
|
||||
*/
|
||||
fun loadBitmapFromView(view: View): Bitmap {
|
||||
val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
view.draw(canvas)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
return bitmap
|
||||
fun saveTemporalBitmap(context: Context, bitmap: Bitmap): Uri {
|
||||
// save bitmap to cache directory
|
||||
try {
|
||||
val cachePath = File(context.cacheDir, "images")
|
||||
if (!cachePath.mkdirs())
|
||||
// don't forget to make the directory
|
||||
Log.d(TAG, "shareBitmapImage creating cache images folder")
|
||||
|
||||
val stream = FileOutputStream("$cachePath/image.png") // overwrites this image every time
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
|
||||
stream.close()
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "shareBitmapImage error: " + e.message)
|
||||
}
|
||||
|
||||
fun saveTemporalBitmap(context: Context, bitmap: Bitmap): Uri {
|
||||
// save bitmap to cache directory
|
||||
try {
|
||||
val cachePath = File(context.cacheDir, "images")
|
||||
if (!cachePath.mkdirs())
|
||||
// don't forget to make the directory
|
||||
Log.d(TAG, "shareBitmapImage creating cache images folder")
|
||||
// Send intent to share image+text
|
||||
val imagePath = File(context.cacheDir, "images")
|
||||
val newFile = File(imagePath, "image.png")
|
||||
|
||||
val stream = FileOutputStream(cachePath.toString() + "/image.png") // overwrites this image every time
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
|
||||
stream.close()
|
||||
} catch (e: IOException) {
|
||||
Log.d(TAG, "shareBitmapImage error: " + e.message)
|
||||
}
|
||||
// Create and return image uri
|
||||
return FileProvider.getUriForFile(context, "cy.agorise.bitsybitshareswallet.FileProvider", newFile)
|
||||
}
|
||||
|
||||
// Send intent to share image+text
|
||||
val imagePath = File(context.cacheDir, "images")
|
||||
val newFile = File(imagePath, "image.png")
|
||||
/**
|
||||
* 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")
|
||||
|
||||
// Create and return image uri
|
||||
return FileProvider.getUriForFile(context, "cy.agorise.bitsybitshareswallet.FileProvider", newFile)
|
||||
}
|
||||
return if (currencyCode.toLowerCase(Locale.ROOT) in supportedCurrencies)
|
||||
currencyCode
|
||||
else
|
||||
"usd"
|
||||
}
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
package cy.agorise.bitsybitshareswallet.viewmodels
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
|
||||
import cy.agorise.bitsybitshareswallet.repositories.EquivalentValuesRepository
|
||||
import cy.agorise.bitsybitshareswallet.repositories.NodeRepository
|
||||
import cy.agorise.bitsybitshareswallet.repositories.TransferRepository
|
||||
import cy.agorise.graphenej.network.FullNode
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
class ConnectedActivityViewModel(application: Application) : AndroidViewModel(application) {
|
||||
companion object {
|
||||
|
@ -24,17 +22,7 @@ class ConnectedActivityViewModel(application: Application) : AndroidViewModel(ap
|
|||
}
|
||||
|
||||
fun observeMissingEquivalentValuesIn(symbol: String) {
|
||||
mTransfersRepository.getSupportedCurrency(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}")
|
||||
}
|
||||
})
|
||||
mTransfersRepository.observeMissingEquivalentValuesIn(symbol)
|
||||
}
|
||||
|
||||
fun updateNodeLatencies(nodes: List<FullNode>) {
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package cy.agorise.bitsybitshareswallet.viewmodels
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.*
|
||||
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
|
||||
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
||||
import cy.agorise.bitsybitshareswallet.repositories.TransferDetailRepository
|
||||
import cy.agorise.bitsybitshareswallet.repositories.TransferRepository
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -16,10 +13,9 @@ import java.util.*
|
|||
|
||||
class TransactionsViewModel(application: Application) : AndroidViewModel(application) {
|
||||
companion object {
|
||||
val TAG = "TransactionsViewModel"
|
||||
const val TAG = "TransactionsViewModel"
|
||||
}
|
||||
private var mRepository = TransferDetailRepository(application)
|
||||
private var mTransfersRepository = TransferRepository(application)
|
||||
|
||||
/**
|
||||
* [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>> {
|
||||
val currency = Currency.getInstance(Locale.getDefault())
|
||||
mTransfersRepository.getSupportedCurrency(currency.currencyCode)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({
|
||||
Log.d(TAG,"Looking for currency with code: ${it}")
|
||||
transactions = mRepository.getAll(userId, it)
|
||||
val currencyCode = Helper.getCoingeckoSupportedCurrency(currency.currencyCode)
|
||||
transactions = mRepository.getAll(userId, currencyCode)
|
||||
|
||||
filteredTransactions.addSource(transactions) { transactions ->
|
||||
viewModelScope.launch {
|
||||
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}")
|
||||
}
|
||||
})
|
||||
filteredTransactions.addSource(transactions) { transactions ->
|
||||
viewModelScope.launch {
|
||||
filteredTransactions.value = filter(transactions, mFilterOptions)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredTransactions
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue