mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
193 lines
8.0 KiB
Kotlin
193 lines
8.0 KiB
Kotlin
package com.kunzisoft.keepass.tasks
|
|
|
|
import android.content.ContentResolver
|
|
import android.graphics.Bitmap
|
|
import android.graphics.BitmapFactory
|
|
import android.net.Uri
|
|
import android.util.Log
|
|
import com.kunzisoft.keepass.database.element.Database
|
|
import com.kunzisoft.keepass.database.element.database.BinaryData
|
|
import com.kunzisoft.keepass.database.element.database.BinaryCache
|
|
import com.kunzisoft.keepass.stream.readAllBytes
|
|
import com.kunzisoft.keepass.utils.UriUtil
|
|
import kotlinx.coroutines.*
|
|
import java.io.ByteArrayInputStream
|
|
import java.io.ByteArrayOutputStream
|
|
import java.io.InputStream
|
|
import java.io.OutputStream
|
|
import kotlin.math.ceil
|
|
import kotlin.math.ln
|
|
import kotlin.math.max
|
|
import kotlin.math.pow
|
|
|
|
object BinaryDatabaseManager {
|
|
|
|
fun downloadFromDatabase(database: Database,
|
|
attachmentToUploadUri: Uri,
|
|
binaryData: BinaryData,
|
|
contentResolver: ContentResolver,
|
|
update: ((percent: Int)->Unit)? = null,
|
|
canceled: ()-> Boolean = { false },
|
|
bufferSize: Int = DEFAULT_BUFFER_SIZE) {
|
|
UriUtil.getUriOutputStream(contentResolver, attachmentToUploadUri)?.use { outputStream ->
|
|
downloadFromDatabase(database.binaryCache, outputStream, binaryData, update, canceled, bufferSize)
|
|
}
|
|
}
|
|
|
|
private fun downloadFromDatabase(binaryCache: BinaryCache,
|
|
outputStream: OutputStream,
|
|
binaryData: BinaryData,
|
|
update: ((percent: Int)->Unit)? = null,
|
|
canceled: ()-> Boolean = { false },
|
|
bufferSize: Int = DEFAULT_BUFFER_SIZE) {
|
|
val fileSize = binaryData.getSize()
|
|
var dataDownloaded = 0L
|
|
binaryData.getUnGzipInputDataStream(binaryCache).use { inputStream ->
|
|
inputStream.readAllBytes(bufferSize, canceled) { buffer ->
|
|
outputStream.write(buffer)
|
|
dataDownloaded += buffer.size
|
|
try {
|
|
val percentDownload = (100 * dataDownloaded / fileSize).toInt()
|
|
update?.invoke(percentDownload)
|
|
} catch (e: Exception) {
|
|
Log.w(TAG, "Unable to call update callback during download", e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun uploadToDatabase(database: Database,
|
|
attachmentFromDownloadUri: Uri,
|
|
binaryData: BinaryData,
|
|
contentResolver: ContentResolver,
|
|
update: ((percent: Int)->Unit)? = null,
|
|
canceled: ()-> Boolean = { false },
|
|
bufferSize: Int = DEFAULT_BUFFER_SIZE) {
|
|
val fileSize = contentResolver.openFileDescriptor(attachmentFromDownloadUri, "r")?.statSize ?: 0
|
|
UriUtil.getUriInputStream(contentResolver, attachmentFromDownloadUri)?.use { inputStream ->
|
|
uploadToDatabase(database.binaryCache, inputStream, fileSize, binaryData, update, canceled, bufferSize)
|
|
}
|
|
}
|
|
|
|
private fun uploadToDatabase(binaryCache: BinaryCache,
|
|
inputStream: InputStream,
|
|
fileSize: Long,
|
|
binaryData: BinaryData,
|
|
update: ((percent: Int)->Unit)? = null,
|
|
canceled: ()-> Boolean = { false },
|
|
bufferSize: Int = DEFAULT_BUFFER_SIZE) {
|
|
var dataUploaded = 0L
|
|
binaryData.getGzipOutputDataStream(binaryCache).use { outputStream ->
|
|
inputStream.readAllBytes(bufferSize, canceled) { buffer ->
|
|
outputStream.write(buffer)
|
|
dataUploaded += buffer.size
|
|
try {
|
|
val percentDownload = (100 * dataUploaded / fileSize).toInt()
|
|
update?.invoke(percentDownload)
|
|
} catch (e: Exception) {
|
|
Log.w(TAG, "Unable to call update callback during upload", e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun resizeBitmapAndStoreDataInBinaryFile(contentResolver: ContentResolver,
|
|
database: Database,
|
|
bitmapUri: Uri?,
|
|
binaryData: BinaryData?) {
|
|
try {
|
|
binaryData?.let {
|
|
UriUtil.getUriInputStream(contentResolver, bitmapUri)?.use { inputStream ->
|
|
BitmapFactory.decodeStream(inputStream)?.let { bitmap ->
|
|
val bitmapResized = bitmap.resize(DEFAULT_ICON_WIDTH)
|
|
val byteArrayOutputStream = ByteArrayOutputStream()
|
|
bitmapResized?.compress(Bitmap.CompressFormat.PNG, 0, byteArrayOutputStream)
|
|
val bitmapData: ByteArray = byteArrayOutputStream.toByteArray()
|
|
val byteArrayInputStream = ByteArrayInputStream(bitmapData)
|
|
uploadToDatabase(
|
|
database.binaryCache,
|
|
byteArrayInputStream,
|
|
bitmapData.size.toLong(),
|
|
binaryData
|
|
)
|
|
}
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
Log.e(TAG, "Unable to resize bitmap to store it in binary", e)
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* reduces the size of the image
|
|
* @param maxSize
|
|
* @return
|
|
*/
|
|
private fun Bitmap.resize(maxSize: Int): Bitmap? {
|
|
var width = this.width
|
|
var height = this.height
|
|
val bitmapRatio = width.toFloat() / height.toFloat()
|
|
if (bitmapRatio > 1) {
|
|
width = maxSize
|
|
height = (width / bitmapRatio).toInt()
|
|
} else {
|
|
height = maxSize
|
|
width = (height * bitmapRatio).toInt()
|
|
}
|
|
return Bitmap.createScaledBitmap(this, width, height, true)
|
|
}
|
|
|
|
fun loadBitmap(database: Database,
|
|
binaryData: BinaryData,
|
|
maxWidth: Int,
|
|
actionOnFinish: (Bitmap?) -> Unit) {
|
|
CoroutineScope(Dispatchers.Main).launch {
|
|
withContext(Dispatchers.IO) {
|
|
val asyncResult: Deferred<Bitmap?> = async {
|
|
runCatching {
|
|
val bitmap: Bitmap? = decodeSampledBitmap(binaryData,
|
|
database.binaryCache,
|
|
maxWidth)
|
|
bitmap
|
|
}.getOrNull()
|
|
}
|
|
withContext(Dispatchers.Main) {
|
|
actionOnFinish(asyncResult.await())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun decodeSampledBitmap(binaryData: BinaryData,
|
|
binaryCache: BinaryCache,
|
|
maxWidth: Int): Bitmap? {
|
|
// First decode with inJustDecodeBounds=true to check dimensions
|
|
return BitmapFactory.Options().run {
|
|
try {
|
|
inJustDecodeBounds = true
|
|
binaryData.getUnGzipInputDataStream(binaryCache).use {
|
|
BitmapFactory.decodeStream(it, null, this)
|
|
}
|
|
// Calculate inSampleSize
|
|
var scale = 1
|
|
if (outHeight > maxWidth || outWidth > maxWidth) {
|
|
scale = 2.0.pow(ceil(ln(maxWidth / max(outHeight, outWidth).toDouble()) / ln(0.5))).toInt()
|
|
}
|
|
inSampleSize = scale
|
|
|
|
// Decode bitmap with inSampleSize set
|
|
inJustDecodeBounds = false
|
|
binaryData.getUnGzipInputDataStream(binaryCache).use {
|
|
BitmapFactory.decodeStream(it, null, this)
|
|
}
|
|
} catch (e: Exception) {
|
|
null
|
|
}
|
|
}
|
|
}
|
|
|
|
private const val DEFAULT_ICON_WIDTH = 64
|
|
|
|
private val TAG = BinaryDatabaseManager::class.java.name
|
|
} |