204 lines
8.3 KiB
Kotlin
204 lines
8.3 KiB
Kotlin
package cy.agorise.bitsybitshareswallet.repositories
|
|
|
|
import android.content.Context
|
|
import android.content.SharedPreferences
|
|
import android.os.AsyncTask
|
|
import android.preference.PreferenceManager
|
|
import android.util.Log
|
|
import androidx.lifecycle.LiveData
|
|
import cy.agorise.bitsybitshareswallet.database.BitsyDatabase
|
|
import cy.agorise.bitsybitshareswallet.database.daos.EquivalentValueDao
|
|
import cy.agorise.bitsybitshareswallet.database.daos.TransferDao
|
|
import cy.agorise.bitsybitshareswallet.database.entities.EquivalentValue
|
|
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
|
|
import java.util.*
|
|
|
|
class TransferRepository internal constructor(context: Context) {
|
|
companion object {
|
|
private const val TAG = "TransferRepository"
|
|
}
|
|
|
|
private val mTransferDao: TransferDao
|
|
private val mEquivalentValuesDao: EquivalentValueDao
|
|
private val compositeDisposable = CompositeDisposable()
|
|
private val mPreferences: SharedPreferences
|
|
|
|
init {
|
|
val db = BitsyDatabase.getDatabase(context)
|
|
mTransferDao = db!!.transferDao()
|
|
mEquivalentValuesDao = db.equivalentValueDao()
|
|
mPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
|
}
|
|
|
|
fun insertAll(transfers: List<Transfer>) {
|
|
insertAllAsyncTask(mTransferDao).execute(transfers)
|
|
}
|
|
|
|
fun update(transfer: Transfer){
|
|
mTransferDao.insert(transfer)
|
|
}
|
|
|
|
fun setBlockTime(blockNumber: Long, timestamp: Long) {
|
|
setBlockTimeAsyncTask(mTransferDao).execute(Pair(blockNumber, timestamp))
|
|
}
|
|
|
|
fun getAll(): LiveData<List<Transfer>> {
|
|
return mTransferDao.getAll()
|
|
}
|
|
|
|
fun getCount(): Single<Int> {
|
|
return mTransferDao.getCount()
|
|
}
|
|
|
|
fun getTransferBlockNumberWithMissingTime(): LiveData<Long> {
|
|
return mTransferDao.getTransferBlockNumberWithMissingTime()
|
|
}
|
|
|
|
fun getTransfersWithMissingBtsValue(): LiveData<Transfer> {
|
|
return mTransferDao.getTransfersWithMissingBtsValue()
|
|
}
|
|
|
|
fun deleteAll() {
|
|
deleteAllAsyncTask(mTransferDao).execute()
|
|
}
|
|
|
|
/**
|
|
* Creates a subscription to the transfers table which will listen & process equivalent values.
|
|
*
|
|
* This function will create a subscription that will listen for missing equivalent values. This will
|
|
* automatically trigger a procedure designed to calculate the fiat equivalent value of any entry
|
|
* of the 'transactions' table that stil doesn't have a corresponding entry in the 'equivalent_values'
|
|
* table for that specific fiat currency.
|
|
*
|
|
* @param symbol The 3 letters symbol of the desired fiat currency.
|
|
*/
|
|
fun observeMissingEquivalentValuesIn(symbol: String) {
|
|
compositeDisposable.add(mTransferDao.getTransfersWithMissingValueIn(symbol)
|
|
.map { transfer -> obtainFiatValue(transfer, symbol) }
|
|
.subscribe({
|
|
if(it.value >= 0) mEquivalentValuesDao.insert(it)
|
|
},{
|
|
Log.e(TAG,"Error while trying to create a new equivalent value. Msg: ${it.message}")
|
|
for(element in it.stackTrace){
|
|
Log.e(TAG,"${element.className}#${element.methodName}:${element.lineNumber}")
|
|
}
|
|
})
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Creates an equivalent value for a given transaction.
|
|
*
|
|
* Function used to perform a request to the Coingecko's price API trying to obtain the
|
|
* equivalent value of a specific [Transfer].
|
|
*
|
|
* @param transfer The transfer whose equivalent value we want to obtain
|
|
* @param symbol The symbol of the fiat that the equivalent value should be calculated in
|
|
* @return An instance of the [EquivalentValue] class, ready to be inserted into the database.
|
|
*/
|
|
private fun obtainFiatValue(transfer: Transfer, symbol: String): EquivalentValue {
|
|
val sg = ServiceGenerator(Constants.COINGECKO_URL)
|
|
val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.ROOT)
|
|
val date = Date(transfer.timestamp * 1000)
|
|
val response = sg.getService(CoingeckoService::class.java)
|
|
?.getHistoricalValueSync("bitshares", dateFormat.format(date), false)
|
|
?.execute()
|
|
var equivalentFiatValue = -1L
|
|
if(response?.isSuccessful == true){
|
|
val price: Double = response.body()?.market_data?.current_price?.get(symbol.toLowerCase()) ?: -1.0
|
|
if(price > 0){
|
|
// The equivalent value is obtained by:
|
|
// 1- Dividing the base value by 100000 (BTS native precision)
|
|
// 2- Multiplying that BTS value by the unit price in the chosen fiat
|
|
// 3- Multiplying the resulting value by 100 in order to express it in cents
|
|
equivalentFiatValue = Math.round(transfer.btsValue?.toFloat()?.div(1e5)?.times(price)?.times(100) ?: -1.0)
|
|
}
|
|
}else{
|
|
Log.w(TAG,"Request was not successful. code: ${response?.code()}")
|
|
}
|
|
return EquivalentValue(transfer.id, equivalentFiatValue, symbol)
|
|
}
|
|
|
|
private class insertAllAsyncTask internal constructor(private val mAsyncTaskDao: TransferDao) :
|
|
AsyncTask<List<Transfer>, Void, Void>() {
|
|
|
|
override fun doInBackground(vararg transfers: List<Transfer>): Void? {
|
|
mAsyncTaskDao.insertAll(transfers[0])
|
|
return null
|
|
}
|
|
}
|
|
|
|
private class setBlockTimeAsyncTask internal constructor(private val mAsyncTaskDao: TransferDao) :
|
|
AsyncTask<Pair<Long, Long>, Void, Void>() {
|
|
|
|
override fun doInBackground(vararg pair: Pair<Long, Long>): Void? {
|
|
mAsyncTaskDao.setBlockTime(pair[0].first, pair[0].second)
|
|
return null
|
|
}
|
|
}
|
|
|
|
private class deleteAllAsyncTask internal constructor(private val mAsyncTaskDao: TransferDao) :
|
|
AsyncTask<Void, Void, Void>() {
|
|
|
|
override fun doInBackground(vararg params: Void?): Void? {
|
|
mAsyncTaskDao.deleteAll()
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called whenever the disposables have to be cleared.
|
|
*
|
|
* Since this repository manages a subscription it is necessary to clear the disposable after we're done with it.
|
|
* The parent ViewModel will let us know when that subscription is no longer necessary and the resources can
|
|
* be cleared.
|
|
*/
|
|
fun onCleared() {
|
|
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
|
|
}
|
|
}
|
|
} |