bitsy-wallet/app/src/main/java/cy/agorise/bitsybitshareswallet/domain/usecase/ExportTransactionsToPdfUseC...

167 lines
6.0 KiB
Kotlin

package cy.agorise.bitsybitshareswallet.domain.usecase
import android.content.Context
import android.util.Log
import androidx.core.os.ConfigurationCompat
import androidx.documentfile.provider.DocumentFile
import com.pdfjet.*
import cy.agorise.bitsybitshareswallet.R
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.BufferedOutputStream
import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.pow
class ExportTransactionsToPdfUseCase(private val applicationContext: Context) {
suspend operator fun invoke(
transactions: List<TransferDetail>,
folderDocumentFile: DocumentFile
) = withContext(Dispatchers.IO) { // TODO Inject Dispatcher
// Create the PDF file name
val fileName = applicationContext.resources.let {
"${it.getString(R.string.app_name)}-${it.getString(R.string.title_transactions)}.pdf"
}
// Obtains the path to store the PDF
val pdfDocUri = folderDocumentFile.createFile("application/pdf", fileName)?.uri
val contentResolver = applicationContext.contentResolver
val pdfOutputStream = contentResolver.openOutputStream(pdfDocUri!!, "w")
// Creates a new PDF object
val pdf = PDF(BufferedOutputStream(pdfOutputStream), Compliance.PDF_A_1B)
// Font used for the table headers
val f1 = Font(pdf, CoreFont.HELVETICA_BOLD).apply { size = 7f }
// Font used for the table contents
val f2 = Font(pdf, CoreFont.HELVETICA).apply { size = 7f }
// Creates a new PDF table
val table = Table()
// 2D array of cells used to populate the PDF table
val tableData = mutableListOf<List<Cell>>()
// Add column names/headers
val columnNames = intArrayOf(
R.string.title_from, R.string.title_to, R.string.title_memo, R.string.title_date,
R.string.title_time, R.string.title_amount, R.string.title_equivalent_value
)
val header = columnNames.map { columnName ->
Cell(f1, applicationContext.getString(columnName)).apply { setPadding(2f) }
}
// Add the table headers
tableData.add(header)
// Add the table contents
val locale = ConfigurationCompat.getLocales(applicationContext.resources.configuration)[0]
tableData.addAll(getData(transactions, f2, locale))
// Configure the PDF table
table.setData(tableData, Table.DATA_HAS_1_HEADER_ROWS)
table.setCellBordersWidth(0.2f)
// The A4 size has 595 points of width, with the below we are trying to assign the same
// width to all cells and also keep them centered.
for (i in 0..6) {
table.setColumnWidth(i, 65f)
}
table.wrapAroundCellText()
table.mergeOverlaidBorders()
// Populate the PDF table
while (table.hasMoreData()) {
// Configures the PDF page
val page = Page(pdf, Letter.PORTRAIT)
table.setLocation(45f, 30f)
table.drawOn(page)
}
pdf.close()
Log.d("ExportTransactionsToPdf", "PDF generated and saved")
}
private suspend fun getData(
transferDetails: List<TransferDetail>,
font: Font,
locale: Locale
): List<List<Cell>> = withContext(Dispatchers.IO) { // TODO Inject Dispatcher
val tableData = mutableListOf<List<Cell>>()
// Configure date and time formats to reuse in all the transfers
val dateFormat = SimpleDateFormat("MM-dd-yyyy", locale)
val timeFormat = SimpleDateFormat("HH:mm:ss", locale)
var date: Date
// Save all the transfers information
for ((index, transferDetail) in transferDetails.withIndex()) {
val cols = mutableListOf<String>()
date = Date(transferDetail.date * 1000)
cols.add(transferDetail.from ?: "") // From
cols.add(transferDetail.to ?: "") // To
cols.add(transferDetail.memo) // Memo
cols.add(dateFormat.format(date)) // Date
cols.add(timeFormat.format(date)) // Time
// Asset Amount
val assetPrecision = transferDetail.assetPrecision
val assetAmount = transferDetail.assetAmount / 10.0.pow(assetPrecision)
cols.add(
String.format(
"%.${assetPrecision}f %s",
assetAmount,
transferDetail.assetSymbol
)
)
// Fiat Equivalent
if (transferDetail.fiatAmount != null && transferDetail.fiatSymbol != null) {
val currency = Currency.getInstance(transferDetail.fiatSymbol)
val fiatAmount =
transferDetail.fiatAmount / 10.0.pow(currency.defaultFractionDigits)
cols.add(
String.format(
"%.${currency.defaultFractionDigits}f %s",
fiatAmount, currency.currencyCode
)
)
}
val row = cols.map { col ->
Cell(font, col).apply { setPadding(2f) }
}
tableData.add(row)
appendMissingCells(tableData, font)
// TODO update progress
}
tableData
}
private fun appendMissingCells(tableData: List<List<Cell>>, font: Font) {
val firstRow = tableData[0]
val numOfColumns = firstRow.size
for (i in tableData.indices) {
val dataRow = tableData[i] as ArrayList<Cell>
val dataRowColumns = dataRow.size
if (dataRowColumns < numOfColumns) {
for (j in 0 until numOfColumns - dataRowColumns) {
dataRow.add(Cell(font))
}
dataRow[dataRowColumns - 1].colSpan = numOfColumns - dataRowColumns + 1
}
}
}
}