174 lines
6.6 KiB
Kotlin
174 lines
6.6 KiB
Kotlin
package cy.agorise.bitsybitshareswallet.ui.transactions
|
|
|
|
import android.app.Application
|
|
import android.net.Uri
|
|
import android.util.Log
|
|
import androidx.documentfile.provider.DocumentFile
|
|
import androidx.lifecycle.*
|
|
import com.google.android.material.datepicker.MaterialDatePicker
|
|
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
|
|
import cy.agorise.bitsybitshareswallet.domain.usecase.ExportTransactionsToCsvUseCase
|
|
import cy.agorise.bitsybitshareswallet.domain.usecase.ExportTransactionsToPdfUseCase
|
|
import cy.agorise.bitsybitshareswallet.repositories.TransferDetailRepository
|
|
import cy.agorise.bitsybitshareswallet.utils.Helper
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.withContext
|
|
import java.util.*
|
|
|
|
|
|
class TransactionsViewModel(application: Application) : AndroidViewModel(application) {
|
|
|
|
// TODO Inject below dependencies
|
|
private val exportTransactionsToPdfUseCase = ExportTransactionsToPdfUseCase(application)
|
|
private val exportTransactionsToCsvUseCase = ExportTransactionsToCsvUseCase(application)
|
|
private val mRepository = TransferDetailRepository(application)
|
|
|
|
/**
|
|
* [FilterOptions] used to filter the list of [TransferDetail] taken from the database
|
|
*/
|
|
private var mFilterOptions = FilterOptions()
|
|
|
|
private lateinit var transactions: LiveData<List<TransferDetail>>
|
|
|
|
/**
|
|
* This [MediatorLiveData] is used to combine two sources of information into one, keeping the
|
|
* client of this [ViewModel] receiving only one stream of data (a list of filtered [TransferDetail])
|
|
*/
|
|
private val filteredTransactions = MediatorLiveData<List<TransferDetail>>()
|
|
|
|
init {
|
|
// Initialize the start and end dates for the FilterOptions
|
|
val calendar = getClearedUtc()
|
|
calendar.timeInMillis = MaterialDatePicker.todayInUtcMilliseconds()
|
|
mFilterOptions.endDate = calendar.timeInMillis
|
|
calendar.roll(Calendar.MONTH, -2)
|
|
mFilterOptions.startDate = calendar.timeInMillis
|
|
}
|
|
|
|
private fun getClearedUtc(): Calendar {
|
|
val utc = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
|
utc.clear()
|
|
return utc
|
|
}
|
|
|
|
internal fun getFilteredTransactions(userId: String): LiveData<List<TransferDetail>> {
|
|
val currencyCode = Helper.getCoingeckoSupportedCurrency(Locale.getDefault())
|
|
transactions = mRepository.getAll(userId, currencyCode)
|
|
|
|
filteredTransactions.addSource(transactions) { transactions ->
|
|
viewModelScope.launch {
|
|
filteredTransactions.value = filter(transactions, mFilterOptions)
|
|
}
|
|
}
|
|
|
|
return filteredTransactions
|
|
}
|
|
|
|
internal fun getFilterOptions(): FilterOptions {
|
|
return mFilterOptions
|
|
}
|
|
|
|
internal fun applyFilterOptions(filterOptions: FilterOptions) =
|
|
transactions.value?.let { transactions ->
|
|
viewModelScope.launch {
|
|
filteredTransactions.value = filter(transactions, filterOptions)
|
|
}
|
|
}.also { mFilterOptions = filterOptions }
|
|
|
|
internal fun setFilterQuery(query: String) = transactions.value?.let { transactions ->
|
|
mFilterOptions.query = query
|
|
viewModelScope.launch {
|
|
filteredTransactions.value = filter(transactions, mFilterOptions)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Filters the given list of [TransferDetail] given the [FilterOptions] and returns a filtered list
|
|
* of [TransferDetail], doing all the work in a background thread using kotlin coroutines
|
|
*/
|
|
private suspend fun filter(
|
|
transactions: List<TransferDetail>,
|
|
filterOptions: FilterOptions
|
|
): List<TransferDetail> = withContext(Dispatchers.Default) {
|
|
|
|
// Create a list to store the filtered transactions
|
|
val filteredTransactions = ArrayList<TransferDetail>()
|
|
|
|
// Make sure the filter dates use the same format as the transactions' dates
|
|
val startDate = filterOptions.startDate / 1000
|
|
val endDate = filterOptions.endDate / 1000
|
|
|
|
for (transaction in transactions) {
|
|
// Filter by transfer direction
|
|
if (transaction.direction) { // Transfer sent
|
|
if (filterOptions.transactionsDirection == 1)
|
|
// Looking for received transfers only
|
|
continue
|
|
} else { // Transfer received
|
|
if (filterOptions.transactionsDirection == 2)
|
|
// Looking for sent transactions only
|
|
continue
|
|
}
|
|
|
|
// Filter by date range
|
|
if (!filterOptions.dateRangeAll && (transaction.date < startDate ||
|
|
transaction.date > endDate)
|
|
)
|
|
continue
|
|
|
|
// Filter by asset
|
|
if (!filterOptions.assetAll && transaction.assetSymbol != filterOptions.asset)
|
|
continue
|
|
|
|
// Filter by equivalent value
|
|
if (!filterOptions.equivalentValueAll && ((transaction.fiatAmount
|
|
?: -1) < filterOptions.fromEquivalentValue
|
|
|| (transaction.fiatAmount ?: -1) > filterOptions.toEquivalentValue)
|
|
)
|
|
continue
|
|
|
|
// Filter transactions sent to agorise
|
|
if (filterOptions.agoriseFees && transaction.to.equals("agorise"))
|
|
continue
|
|
|
|
// Filter by search query
|
|
val text = "${transaction.from ?: ""} ${transaction.to ?: ""} ${transaction.memo}"
|
|
if (text.contains(filterOptions.query, ignoreCase = true)) {
|
|
filteredTransactions.add(transaction)
|
|
}
|
|
}
|
|
|
|
filteredTransactions
|
|
}
|
|
|
|
/** Creates the export procedures for PDF and CSV, depending on the user selection. */
|
|
fun exportFilteredTransactions(folderUri: Uri, exportPdf: Boolean, exportCsv: Boolean) {
|
|
Log.d(TAG, "Export options selected. PDF: $exportPdf, CSV: $exportCsv")
|
|
|
|
if (!exportPdf && !exportCsv) return
|
|
|
|
// TODO Use injected context
|
|
val folderDocumentFile = DocumentFile.fromTreeUri(getApplication(), folderUri) ?: return
|
|
val transactions = filteredTransactions.value ?: return
|
|
|
|
if (exportPdf) {
|
|
viewModelScope.launch {
|
|
// TODO Show success/failure message
|
|
exportTransactionsToPdfUseCase(transactions, folderDocumentFile)
|
|
}
|
|
}
|
|
|
|
if (exportCsv) {
|
|
viewModelScope.launch {
|
|
// TODO Show success/failure message
|
|
exportTransactionsToCsvUseCase(transactions, folderDocumentFile)
|
|
}
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
private const val TAG = "TransactionsViewModel"
|
|
}
|
|
}
|