mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Upload custom attachment and fix warning
This commit is contained in:
@@ -3,7 +3,7 @@ package com.kunzisoft.keepass.tests.stream
|
||||
import android.content.Context
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryAttachment
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryFile
|
||||
import com.kunzisoft.keepass.stream.readAllBytes
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import junit.framework.TestCase.assertEquals
|
||||
@@ -12,7 +12,7 @@ import java.io.DataInputStream
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
class BinaryAttachmentTest {
|
||||
class BinaryFileTest {
|
||||
|
||||
private val context: Context by lazy {
|
||||
InstrumentationRegistry.getInstrumentation().context
|
||||
@@ -25,9 +25,9 @@ class BinaryAttachmentTest {
|
||||
|
||||
private val loadedKey = Database.LoadedKey.generateNewCipherKey()
|
||||
|
||||
private fun saveBinary(asset: String, binaryAttachment: BinaryAttachment) {
|
||||
private fun saveBinary(asset: String, binaryFile: BinaryFile) {
|
||||
context.assets.open(asset).use { assetInputStream ->
|
||||
binaryAttachment.getOutputDataStream(loadedKey).use { binaryOutputStream ->
|
||||
binaryFile.getOutputDataStream(loadedKey).use { binaryOutputStream ->
|
||||
assetInputStream.readAllBytes(DEFAULT_BUFFER_SIZE) { buffer ->
|
||||
binaryOutputStream.write(buffer)
|
||||
}
|
||||
@@ -37,8 +37,8 @@ class BinaryAttachmentTest {
|
||||
|
||||
@Test
|
||||
fun testSaveTextInCache() {
|
||||
val binaryA = BinaryAttachment(fileA)
|
||||
val binaryB = BinaryAttachment(fileB)
|
||||
val binaryA = BinaryFile(fileA)
|
||||
val binaryB = BinaryFile(fileB)
|
||||
saveBinary(TEST_TEXT_ASSET, binaryA)
|
||||
saveBinary(TEST_TEXT_ASSET, binaryB)
|
||||
assertEquals("Save text binary length failed.", binaryA.length, binaryB.length)
|
||||
@@ -47,8 +47,8 @@ class BinaryAttachmentTest {
|
||||
|
||||
@Test
|
||||
fun testSaveImageInCache() {
|
||||
val binaryA = BinaryAttachment(fileA)
|
||||
val binaryB = BinaryAttachment(fileB)
|
||||
val binaryA = BinaryFile(fileA)
|
||||
val binaryB = BinaryFile(fileB)
|
||||
saveBinary(TEST_IMAGE_ASSET, binaryA)
|
||||
saveBinary(TEST_IMAGE_ASSET, binaryB)
|
||||
assertEquals("Save image binary length failed.", binaryA.length, binaryB.length)
|
||||
@@ -57,9 +57,9 @@ class BinaryAttachmentTest {
|
||||
|
||||
@Test
|
||||
fun testCompressText() {
|
||||
val binaryA = BinaryAttachment(fileA)
|
||||
val binaryB = BinaryAttachment(fileB)
|
||||
val binaryC = BinaryAttachment(fileC)
|
||||
val binaryA = BinaryFile(fileA)
|
||||
val binaryB = BinaryFile(fileB)
|
||||
val binaryC = BinaryFile(fileC)
|
||||
saveBinary(TEST_TEXT_ASSET, binaryA)
|
||||
saveBinary(TEST_TEXT_ASSET, binaryB)
|
||||
saveBinary(TEST_TEXT_ASSET, binaryC)
|
||||
@@ -74,9 +74,9 @@ class BinaryAttachmentTest {
|
||||
|
||||
@Test
|
||||
fun testCompressImage() {
|
||||
val binaryA = BinaryAttachment(fileA)
|
||||
var binaryB = BinaryAttachment(fileB)
|
||||
val binaryC = BinaryAttachment(fileC)
|
||||
val binaryA = BinaryFile(fileA)
|
||||
var binaryB = BinaryFile(fileB)
|
||||
val binaryC = BinaryFile(fileC)
|
||||
saveBinary(TEST_IMAGE_ASSET, binaryA)
|
||||
saveBinary(TEST_IMAGE_ASSET, binaryB)
|
||||
saveBinary(TEST_IMAGE_ASSET, binaryC)
|
||||
@@ -84,7 +84,7 @@ class BinaryAttachmentTest {
|
||||
binaryB.compress(loadedKey)
|
||||
assertEquals("Compress image length failed.", binaryA.length, binaryA.length)
|
||||
assertEquals("Compress image failed.", binaryA.md5(), binaryA.md5())
|
||||
binaryB = BinaryAttachment(fileB, true)
|
||||
binaryB = BinaryFile(fileB, true)
|
||||
binaryB.decompress(loadedKey)
|
||||
assertEquals("Decompress image length failed.", binaryB.length, binaryC.length)
|
||||
assertEquals("Decompress image failed.", binaryB.md5(), binaryC.md5())
|
||||
@@ -92,7 +92,7 @@ class BinaryAttachmentTest {
|
||||
|
||||
@Test
|
||||
fun testReadText() {
|
||||
val binaryA = BinaryAttachment(fileA)
|
||||
val binaryA = BinaryFile(fileA)
|
||||
saveBinary(TEST_TEXT_ASSET, binaryA)
|
||||
assert(streamAreEquals(context.assets.open(TEST_TEXT_ASSET),
|
||||
binaryA.getInputDataStream(loadedKey)))
|
||||
@@ -100,7 +100,7 @@ class BinaryAttachmentTest {
|
||||
|
||||
@Test
|
||||
fun testReadImage() {
|
||||
val binaryA = BinaryAttachment(fileA)
|
||||
val binaryA = BinaryFile(fileA)
|
||||
saveBinary(TEST_IMAGE_ASSET, binaryA)
|
||||
assert(streamAreEquals(context.assets.open(TEST_IMAGE_ASSET),
|
||||
binaryA.getInputDataStream(loadedKey)))
|
||||
@@ -484,7 +484,7 @@ class EntryEditActivity : LockingActivity(),
|
||||
|
||||
private fun buildNewAttachment(attachmentToUploadUri: Uri, fileName: String) {
|
||||
val compression = mDatabase?.compressionForNewEntry() ?: false
|
||||
mDatabase?.buildNewAttachment(UriUtil.getBinaryDir(this), compression)?.let { binaryAttachment ->
|
||||
mDatabase?.buildNewBinaryAttachment(UriUtil.getBinaryDir(this), compression)?.let { binaryAttachment ->
|
||||
val entryAttachment = Attachment(fileName, binaryAttachment)
|
||||
// Ask to replace the current attachment
|
||||
if ((mDatabase?.allowMultipleAttachments != true && entryEditFragment?.containsAttachment() == true) ||
|
||||
@@ -506,7 +506,6 @@ class EntryEditActivity : LockingActivity(),
|
||||
|
||||
mSelectFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
|
||||
uri?.let { attachmentToUploadUri ->
|
||||
// TODO Async to get the name
|
||||
UriUtil.getFileData(this, attachmentToUploadUri)?.also { documentFile ->
|
||||
documentFile.name?.let { fileName ->
|
||||
if (documentFile.length() > MAX_WARNING_BINARY_FILE) {
|
||||
@@ -572,7 +571,7 @@ class EntryEditActivity : LockingActivity(),
|
||||
// Delete temp attachment if not used
|
||||
mTempAttachments.forEach { tempAttachmentState ->
|
||||
val tempAttachment = tempAttachmentState.attachment
|
||||
mDatabase?.binaryPool?.let { binaryPool ->
|
||||
mDatabase?.attachmentPool?.let { binaryPool ->
|
||||
if (!newEntry.getAttachments(binaryPool).contains(tempAttachment)) {
|
||||
mDatabase?.removeAttachmentIfNotUsed(tempAttachment)
|
||||
}
|
||||
|
||||
@@ -20,8 +20,11 @@
|
||||
package com.kunzisoft.keepass.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ContentResolver
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -34,8 +37,10 @@ import com.kunzisoft.keepass.activities.helpers.SelectFileHelper
|
||||
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryFile
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImage
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.stream.readAllBytes
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import com.kunzisoft.keepass.view.updateLockPaddingLeft
|
||||
import com.kunzisoft.keepass.viewmodels.IconPickerViewModel
|
||||
@@ -132,16 +137,54 @@ class IconPickerActivity : LockingActivity() {
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun buildNewIcon(iconToUploadUri: Uri) {
|
||||
mDatabase?.buildNewCustomIcon(UriUtil.getBinaryDir(this))?.let { customIcon ->
|
||||
uploadIconToDatabase(iconToUploadUri, customIcon.binaryFile, contentResolver,
|
||||
{
|
||||
|
||||
}
|
||||
)
|
||||
iconPickerViewModel.addCustomIcon(customIcon)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Encapsulate
|
||||
fun uploadIconToDatabase(iconToUploadUri: Uri,
|
||||
binaryFile: BinaryFile,
|
||||
contentResolver: ContentResolver,
|
||||
update: ((percent: Int)->Unit)? = null,
|
||||
canceled: ()-> Boolean = { false },
|
||||
bufferSize: Int = DEFAULT_BUFFER_SIZE,) {
|
||||
var dataUploaded = 0L
|
||||
val fileSize = contentResolver.openFileDescriptor(iconToUploadUri, "r")?.statSize ?: 0
|
||||
UriUtil.getUriInputStream(contentResolver, iconToUploadUri)?.use { inputStream ->
|
||||
Database.getInstance().loadedCipherKey?.let { binaryCipherKey ->
|
||||
binaryFile.getGzipOutputDataStream(binaryCipherKey).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.e("", "", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
mSelectFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
|
||||
uri?.let { iconToUploadUri ->
|
||||
UriUtil.getFileData(this, iconToUploadUri)?.also { documentFile ->
|
||||
if (documentFile.length() <= MAX_ICON_SIZE) {
|
||||
mDatabase?.buildNewCustomIcon(UriUtil.getBinaryDir(this))
|
||||
} else {
|
||||
if (documentFile.length() > MAX_ICON_SIZE) {
|
||||
// TODO Error Icon size too big
|
||||
} else {
|
||||
buildNewIcon(iconToUploadUri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class ImageViewerActivity : LockingActivity() {
|
||||
intent.getParcelableExtra<Attachment>(IMAGE_ATTACHMENT_TAG)?.let { attachment ->
|
||||
|
||||
supportActionBar?.title = attachment.name
|
||||
supportActionBar?.subtitle = Formatter.formatFileSize(this, attachment.binaryAttachment.length)
|
||||
supportActionBar?.subtitle = Formatter.formatFileSize(this, attachment.binaryFile.length)
|
||||
|
||||
Attachment.loadBitmap(attachment, Database.getInstance().loadedCipherKey) { bitmapLoaded ->
|
||||
if (bitmapLoaded == null) {
|
||||
|
||||
@@ -18,12 +18,17 @@ class IconCustomFragment : IconFragment() {
|
||||
}
|
||||
|
||||
override fun defineIconList(database: Database): List<IconImage> {
|
||||
return database.iconPool.getCustomIconList()
|
||||
return database.iconsManager.getCustomIconList()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
iconPickerViewModel.iconCustomAdded.observe(viewLifecycleOwner) { _ ->
|
||||
iconAdapter.setList(defineIconList(database))
|
||||
iconAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
iconAdapter.iconPickerListener = object : IconAdapter.IconPickerListener {
|
||||
override fun iconPicked(icon: IconImage) {
|
||||
iconPickerViewModel.selectIconCustom(icon.custom)
|
||||
|
||||
@@ -19,6 +19,8 @@ abstract class IconFragment : Fragment() {
|
||||
private lateinit var iconsGridView: RecyclerView
|
||||
protected lateinit var iconAdapter: IconAdapter
|
||||
|
||||
protected val database = Database.getInstance()
|
||||
|
||||
protected val iconPickerViewModel: IconPickerViewModel by activityViewModels()
|
||||
|
||||
abstract fun retrieveMainLayoutId(): Int
|
||||
@@ -28,8 +30,6 @@ abstract class IconFragment : Fragment() {
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
|
||||
val database = Database.getInstance()
|
||||
|
||||
iconAdapter = IconAdapter(requireActivity()).apply {
|
||||
iconDrawableFactory = database.iconDrawableFactory
|
||||
setList(defineIconList(database))
|
||||
|
||||
@@ -18,7 +18,7 @@ class IconStandardFragment : IconFragment() {
|
||||
}
|
||||
|
||||
override fun defineIconList(database: Database): List<IconImage> {
|
||||
return database.iconPool.getStandardIconList()
|
||||
return database.iconsManager.getStandardIconList()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
||||
@@ -101,22 +101,22 @@ class EntryAttachmentsItemsAdapter(context: Context)
|
||||
}
|
||||
holder.binaryFileBroken.apply {
|
||||
setColorFilter(Color.RED)
|
||||
visibility = if (entryAttachmentState.attachment.binaryAttachment.isCorrupted) {
|
||||
visibility = if (entryAttachmentState.attachment.binaryFile.isCorrupted) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
}
|
||||
holder.binaryFileTitle.text = entryAttachmentState.attachment.name
|
||||
if (entryAttachmentState.attachment.binaryAttachment.isCorrupted) {
|
||||
if (entryAttachmentState.attachment.binaryFile.isCorrupted) {
|
||||
holder.binaryFileTitle.setTextColor(Color.RED)
|
||||
} else {
|
||||
holder.binaryFileTitle.setTextColor(mTitleColor)
|
||||
}
|
||||
holder.binaryFileSize.text = Formatter.formatFileSize(context,
|
||||
entryAttachmentState.attachment.binaryAttachment.length)
|
||||
entryAttachmentState.attachment.binaryFile.length)
|
||||
holder.binaryFileCompression.apply {
|
||||
if (entryAttachmentState.attachment.binaryAttachment.isCompressed) {
|
||||
if (entryAttachmentState.attachment.binaryFile.isCompressed) {
|
||||
text = CompressionAlgorithm.GZip.getName(context.resources)
|
||||
visibility = View.VISIBLE
|
||||
} else {
|
||||
|
||||
@@ -110,10 +110,10 @@ class SearchEntryCursorAdapter(private val context: Context,
|
||||
return database.createEntry()?.apply {
|
||||
database.startManageEntry(this)
|
||||
entryKDB?.let { entryKDB ->
|
||||
(cursor as EntryCursorKDB).populateEntry(entryKDB, database.iconPool)
|
||||
(cursor as EntryCursorKDB).populateEntry(entryKDB, database.iconsManager)
|
||||
}
|
||||
entryKDBX?.let { entryKDBX ->
|
||||
(cursor as EntryCursorKDBX).populateEntry(entryKDBX, database.iconPool)
|
||||
(cursor as EntryCursorKDBX).populateEntry(entryKDBX, database.iconsManager)
|
||||
}
|
||||
database.stopManageEntry(this)
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class DeleteNodesRunnable(context: Context,
|
||||
database.deleteEntry(currentNode)
|
||||
}
|
||||
// Remove the oldest attachments
|
||||
currentNode.getAttachments(database.binaryPool).forEach {
|
||||
currentNode.getAttachments(database.attachmentPool).forEach {
|
||||
database.removeAttachmentIfNotUsed(it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,14 @@ class UpdateEntryRunnable constructor(
|
||||
mNewEntry.addParentFrom(mOldEntry)
|
||||
|
||||
// Build oldest attachments
|
||||
val oldEntryAttachments = mOldEntry.getAttachments(database.binaryPool, true)
|
||||
val newEntryAttachments = mNewEntry.getAttachments(database.binaryPool, true)
|
||||
val oldEntryAttachments = mOldEntry.getAttachments(database.attachmentPool, true)
|
||||
val newEntryAttachments = mNewEntry.getAttachments(database.attachmentPool, true)
|
||||
val attachmentsToRemove = ArrayList<Attachment>(oldEntryAttachments)
|
||||
// Not use equals because only check name
|
||||
newEntryAttachments.forEach { newAttachment ->
|
||||
oldEntryAttachments.forEach { oldAttachment ->
|
||||
if (oldAttachment.name == newAttachment.name
|
||||
&& oldAttachment.binaryAttachment == newAttachment.binaryAttachment)
|
||||
&& oldAttachment.binaryFile == newAttachment.binaryFile)
|
||||
attachmentsToRemove.remove(oldAttachment)
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ class UpdateEntryRunnable constructor(
|
||||
|
||||
// Create an entry history (an entry history don't have history)
|
||||
mOldEntry.addEntryToHistory(Entry(mBackupEntryHistory, copyHistory = false))
|
||||
database.removeOldestEntryHistory(mOldEntry, database.binaryPool)
|
||||
database.removeOldestEntryHistory(mOldEntry, database.attachmentPool)
|
||||
|
||||
// Only change data in index
|
||||
database.updateEntry(mOldEntry)
|
||||
|
||||
@@ -24,7 +24,7 @@ import android.provider.BaseColumns
|
||||
import com.kunzisoft.keepass.database.element.DateInstant
|
||||
import com.kunzisoft.keepass.database.element.entry.EntryVersioned
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImage
|
||||
import com.kunzisoft.keepass.database.element.icon.IconPool
|
||||
import com.kunzisoft.keepass.database.element.icon.IconsManager
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
import java.util.*
|
||||
|
||||
@@ -50,12 +50,12 @@ abstract class EntryCursor<EntryId, PwEntryV : EntryVersioned<*, EntryId, *, *>>
|
||||
|
||||
abstract fun getPwNodeId(): NodeId<EntryId>
|
||||
|
||||
open fun populateEntry(pwEntry: PwEntryV, iconPool: IconPool) {
|
||||
open fun populateEntry(pwEntry: PwEntryV, iconsManager: IconsManager) {
|
||||
pwEntry.nodeId = getPwNodeId()
|
||||
pwEntry.title = getString(getColumnIndex(COLUMN_INDEX_TITLE))
|
||||
|
||||
val iconStandard = iconPool.getIcon(getInt(getColumnIndex(COLUMN_INDEX_ICON_STANDARD)))
|
||||
val iconCustom = iconPool.getIcon(UUID(getLong(getColumnIndex(COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)),
|
||||
val iconStandard = iconsManager.getIcon(getInt(getColumnIndex(COLUMN_INDEX_ICON_STANDARD)))
|
||||
val iconCustom = iconsManager.getIcon(UUID(getLong(getColumnIndex(COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)),
|
||||
getLong(getColumnIndex(COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS))))
|
||||
pwEntry.icon = IconImage(iconStandard, iconCustom)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
package com.kunzisoft.keepass.database.cursor
|
||||
|
||||
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
|
||||
import com.kunzisoft.keepass.database.element.icon.IconPool
|
||||
import com.kunzisoft.keepass.database.element.icon.IconsManager
|
||||
|
||||
class EntryCursorKDBX : EntryCursorUUID<EntryKDBX>() {
|
||||
|
||||
@@ -50,8 +50,8 @@ class EntryCursorKDBX : EntryCursorUUID<EntryKDBX>() {
|
||||
entryId++
|
||||
}
|
||||
|
||||
override fun populateEntry(pwEntry: EntryKDBX, iconPool: IconPool) {
|
||||
super.populateEntry(pwEntry, iconPool)
|
||||
override fun populateEntry(pwEntry: EntryKDBX, iconsManager: IconsManager) {
|
||||
super.populateEntry(pwEntry, iconsManager)
|
||||
|
||||
// Retrieve extra fields
|
||||
if (extraFieldCursor.moveToFirst()) {
|
||||
|
||||
@@ -23,20 +23,20 @@ import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryAttachment
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryFile
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
data class Attachment(var name: String,
|
||||
var binaryAttachment: BinaryAttachment) : Parcelable {
|
||||
var binaryFile: BinaryFile) : Parcelable {
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readString() ?: "",
|
||||
parcel.readParcelable(BinaryAttachment::class.java.classLoader) ?: BinaryAttachment()
|
||||
parcel.readParcelable(BinaryFile::class.java.classLoader) ?: BinaryFile()
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(name)
|
||||
parcel.writeParcelable(binaryAttachment, flags)
|
||||
parcel.writeParcelable(binaryFile, flags)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
@@ -44,7 +44,7 @@ data class Attachment(var name: String,
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "$name at $binaryAttachment"
|
||||
return "$name at $binaryFile"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@@ -78,7 +78,7 @@ data class Attachment(var name: String,
|
||||
runCatching {
|
||||
binaryCipherKey?.let { binaryKey ->
|
||||
var bitmap: Bitmap?
|
||||
attachment.binaryAttachment.getUnGzipInputDataStream(binaryKey).use { bitmapInputStream ->
|
||||
attachment.binaryFile.getUnGzipInputDataStream(binaryKey).use { bitmapInputStream ->
|
||||
bitmap = BitmapFactory.decodeStream(bitmapInputStream)
|
||||
}
|
||||
bitmap
|
||||
|
||||
@@ -27,7 +27,7 @@ import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||
import com.kunzisoft.keepass.database.action.node.NodeHandler
|
||||
import com.kunzisoft.keepass.database.element.database.*
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
import com.kunzisoft.keepass.database.element.icon.IconPool
|
||||
import com.kunzisoft.keepass.database.element.icon.IconsManager
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
import com.kunzisoft.keepass.database.element.node.NodeIdInt
|
||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||
@@ -68,7 +68,7 @@ class Database {
|
||||
|
||||
var isReadOnly = false
|
||||
|
||||
val iconDrawableFactory = IconDrawableFactory()
|
||||
val iconDrawableFactory = IconDrawableFactory { loadedCipherKey }
|
||||
|
||||
var loaded = false
|
||||
set(value) {
|
||||
@@ -92,9 +92,9 @@ class Database {
|
||||
return mDatabaseKDB?.loadedCipherKey ?: mDatabaseKDBX?.loadedCipherKey
|
||||
}
|
||||
|
||||
val iconPool: IconPool
|
||||
val iconsManager: IconsManager
|
||||
get() {
|
||||
return mDatabaseKDB?.iconPool ?: mDatabaseKDBX?.iconPool ?: IconPool()
|
||||
return mDatabaseKDB?.iconsManager ?: mDatabaseKDBX?.iconsManager ?: IconsManager()
|
||||
}
|
||||
|
||||
fun buildNewCustomIcon(cacheDirectory: File): IconImageCustom? {
|
||||
@@ -537,9 +537,9 @@ class Database {
|
||||
}, omitBackup, max)
|
||||
}
|
||||
|
||||
val binaryPool: BinaryPool
|
||||
val attachmentPool: AttachmentPool
|
||||
get() {
|
||||
return mDatabaseKDBX?.binaryPool ?: BinaryPool()
|
||||
return mDatabaseKDBX?.binaryPool ?: AttachmentPool()
|
||||
}
|
||||
|
||||
val allowMultipleAttachments: Boolean
|
||||
@@ -551,9 +551,9 @@ class Database {
|
||||
return false
|
||||
}
|
||||
|
||||
fun buildNewAttachment(cacheDirectory: File,
|
||||
compressed: Boolean = false,
|
||||
protected: Boolean = false): BinaryAttachment? {
|
||||
fun buildNewBinaryAttachment(cacheDirectory: File,
|
||||
compressed: Boolean = false,
|
||||
protected: Boolean = false): BinaryFile? {
|
||||
return mDatabaseKDB?.buildNewAttachment(cacheDirectory)
|
||||
?: mDatabaseKDBX?.buildNewAttachment(cacheDirectory, compressed, protected)
|
||||
}
|
||||
@@ -561,7 +561,7 @@ class Database {
|
||||
fun removeAttachmentIfNotUsed(attachment: Attachment) {
|
||||
// No need in KDB database because unique attachment by entry
|
||||
// Don't clear to fix upload multiple times
|
||||
mDatabaseKDBX?.removeUnlinkedAttachment(attachment.binaryAttachment, false)
|
||||
mDatabaseKDBX?.removeUnlinkedAttachment(attachment.binaryFile, false)
|
||||
}
|
||||
|
||||
fun removeUnlinkedAttachments() {
|
||||
@@ -630,7 +630,7 @@ class Database {
|
||||
}
|
||||
|
||||
fun clear(filesDirectory: File? = null) {
|
||||
iconPool.clearCache()
|
||||
iconsManager.clearCache()
|
||||
iconDrawableFactory.clearCache()
|
||||
// Delete the cache of the database if present
|
||||
mDatabaseKDB?.clearCache()
|
||||
@@ -797,7 +797,7 @@ class Database {
|
||||
* @param entryToCopy
|
||||
* @param newParent
|
||||
*/
|
||||
fun copyEntryTo(entryToCopy: Entry, newParent: Group): Entry? {
|
||||
fun copyEntryTo(entryToCopy: Entry, newParent: Group): Entry {
|
||||
val entryCopied = Entry(entryToCopy, false)
|
||||
entryCopied.nodeId = mDatabaseKDB?.newEntryId() ?: mDatabaseKDBX?.newEntryId() ?: NodeIdUUID()
|
||||
entryCopied.parent = newParent
|
||||
@@ -954,7 +954,7 @@ class Database {
|
||||
rootGroup?.doForEachChildAndForIt(
|
||||
object : NodeHandler<Entry>() {
|
||||
override fun operate(node: Entry): Boolean {
|
||||
removeOldestEntryHistory(node, binaryPool)
|
||||
removeOldestEntryHistory(node, attachmentPool)
|
||||
return true
|
||||
}
|
||||
},
|
||||
@@ -969,7 +969,7 @@ class Database {
|
||||
/**
|
||||
* Remove oldest history if more than max items or max memory
|
||||
*/
|
||||
fun removeOldestEntryHistory(entry: Entry, binaryPool: BinaryPool) {
|
||||
fun removeOldestEntryHistory(entry: Entry, attachmentPool: AttachmentPool) {
|
||||
mDatabaseKDBX?.let {
|
||||
val maxItems = historyMaxItems
|
||||
if (maxItems >= 0) {
|
||||
@@ -983,7 +983,7 @@ class Database {
|
||||
while (true) {
|
||||
var historySize: Long = 0
|
||||
for (entryHistory in entry.getHistory()) {
|
||||
historySize += entryHistory.getSize(binaryPool)
|
||||
historySize += entryHistory.getSize(attachmentPool)
|
||||
}
|
||||
if (historySize > maxSize) {
|
||||
removeOldestEntryHistory(entry)
|
||||
@@ -997,7 +997,7 @@ class Database {
|
||||
|
||||
private fun removeOldestEntryHistory(entry: Entry) {
|
||||
entry.removeOldestEntryFromHistory()?.let {
|
||||
it.getAttachments(binaryPool, false).forEach { attachmentToRemove ->
|
||||
it.getAttachments(attachmentPool, false).forEach { attachmentToRemove ->
|
||||
removeAttachmentIfNotUsed(attachmentToRemove)
|
||||
}
|
||||
}
|
||||
@@ -1005,7 +1005,7 @@ class Database {
|
||||
|
||||
fun removeEntryHistory(entry: Entry, entryHistoryPosition: Int) {
|
||||
entry.removeEntryFromHistory(entryHistoryPosition)?.let {
|
||||
it.getAttachments(binaryPool, false).forEach { attachmentToRemove ->
|
||||
it.getAttachments(attachmentPool, false).forEach { attachmentToRemove ->
|
||||
removeAttachmentIfNotUsed(attachmentToRemove)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,12 @@ package com.kunzisoft.keepass.database.element
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryPool
|
||||
import com.kunzisoft.keepass.database.element.database.AttachmentPool
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
||||
import com.kunzisoft.keepass.database.element.entry.EntryKDB
|
||||
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
|
||||
import com.kunzisoft.keepass.database.element.entry.EntryVersionedInterface
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImage
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
||||
import com.kunzisoft.keepass.database.element.node.Node
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||
@@ -311,12 +309,12 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
||||
entryKDBX?.stopToManageFieldReferences()
|
||||
}
|
||||
|
||||
fun getAttachments(binaryPool: BinaryPool, inHistory: Boolean = false): List<Attachment> {
|
||||
fun getAttachments(attachmentPool: AttachmentPool, inHistory: Boolean = false): List<Attachment> {
|
||||
val attachments = ArrayList<Attachment>()
|
||||
entryKDB?.getAttachment()?.let {
|
||||
attachments.add(it)
|
||||
}
|
||||
entryKDBX?.getAttachments(binaryPool, inHistory)?.let {
|
||||
entryKDBX?.getAttachments(attachmentPool, inHistory)?.let {
|
||||
attachments.addAll(it)
|
||||
}
|
||||
return attachments
|
||||
@@ -337,9 +335,9 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
||||
entryKDBX?.removeAttachments()
|
||||
}
|
||||
|
||||
private fun putAttachment(attachment: Attachment, binaryPool: BinaryPool) {
|
||||
private fun putAttachment(attachment: Attachment, attachmentPool: AttachmentPool) {
|
||||
entryKDB?.putAttachment(attachment)
|
||||
entryKDBX?.putAttachment(attachment, binaryPool)
|
||||
entryKDBX?.putAttachment(attachment, attachmentPool)
|
||||
}
|
||||
|
||||
fun getHistory(): ArrayList<Entry> {
|
||||
@@ -371,8 +369,8 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
||||
return null
|
||||
}
|
||||
|
||||
fun getSize(binaryPool: BinaryPool): Long {
|
||||
return entryKDBX?.getSize(binaryPool) ?: 0L
|
||||
fun getSize(attachmentPool: AttachmentPool): Long {
|
||||
return entryKDBX?.getSize(attachmentPool) ?: 0L
|
||||
}
|
||||
|
||||
fun containsCustomData(): Boolean {
|
||||
@@ -414,7 +412,7 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
||||
// Replace parameter fields by generated OTP fields
|
||||
entryInfo.customFields = OtpEntryFields.generateAutoFields(entryInfo.customFields)
|
||||
}
|
||||
database?.binaryPool?.let { binaryPool ->
|
||||
database?.attachmentPool?.let { binaryPool ->
|
||||
entryInfo.attachments = getAttachments(binaryPool)
|
||||
}
|
||||
|
||||
@@ -441,7 +439,7 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
||||
url = newEntryInfo.url
|
||||
notes = newEntryInfo.notes
|
||||
addExtraFields(newEntryInfo.customFields)
|
||||
database?.binaryPool?.let { binaryPool ->
|
||||
database?.attachmentPool?.let { binaryPool ->
|
||||
newEntryInfo.attachments.forEach { attachment ->
|
||||
putAttachment(attachment, binaryPool)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2021 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.element.database
|
||||
|
||||
class AttachmentPool : BinaryPool<Int>() {
|
||||
|
||||
/**
|
||||
* Utility method to find an unused key in the pool
|
||||
*/
|
||||
override fun findUnusedKey(): Int {
|
||||
var unusedKey = 0
|
||||
while (pool[unusedKey] != null)
|
||||
unusedKey++
|
||||
return unusedKey
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to order binaries and solve index problem in database v4
|
||||
*/
|
||||
private fun orderedBinaries(): List<KeyBinary<Int>> {
|
||||
val keyBinaryList = ArrayList<KeyBinary<Int>>()
|
||||
for ((key, binary) in pool) {
|
||||
// Don't deduplicate
|
||||
val existentBinary = keyBinaryList.find { it.binary.md5() == binary.md5() }
|
||||
if (existentBinary == null) {
|
||||
keyBinaryList.add(KeyBinary(binary, key))
|
||||
} else {
|
||||
existentBinary.addKey(key)
|
||||
}
|
||||
}
|
||||
return keyBinaryList
|
||||
}
|
||||
|
||||
/**
|
||||
* To register a binary with a ref corresponding to an ordered index
|
||||
*/
|
||||
fun getBinaryIndexFromKey(key: Int): Int? {
|
||||
val index = orderedBinaries().indexOfFirst { it.keys.contains(key) }
|
||||
return if (index < 0)
|
||||
null
|
||||
else
|
||||
index
|
||||
}
|
||||
|
||||
/**
|
||||
* Different from doForEach, provide an ordered index to each binary
|
||||
*/
|
||||
fun doForEachOrderedBinary(action: (index: Int, binary: BinaryFile) -> Unit) {
|
||||
orderedBinaries().forEachIndexed { index, keyBinary ->
|
||||
action.invoke(index, keyBinary.binary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ import javax.crypto.CipherInputStream
|
||||
import javax.crypto.CipherOutputStream
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
|
||||
class BinaryAttachment : Parcelable {
|
||||
class BinaryFile : Parcelable {
|
||||
|
||||
private var dataFile: File? = null
|
||||
var length: Long = 0
|
||||
@@ -74,12 +74,12 @@ class BinaryAttachment : Parcelable {
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getInputDataStream(cipherKey: Database.LoadedKey): InputStream {
|
||||
return buildInputStream(dataFile!!, cipherKey)
|
||||
return buildInputStream(dataFile, cipherKey)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun getOutputDataStream(cipherKey: Database.LoadedKey): OutputStream {
|
||||
return buildOutputStream(dataFile!!, cipherKey)
|
||||
return buildOutputStream(dataFile, cipherKey)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
@@ -201,7 +201,7 @@ class BinaryAttachment : Parcelable {
|
||||
return true
|
||||
if (other == null || javaClass != other.javaClass)
|
||||
return false
|
||||
if (other !is BinaryAttachment)
|
||||
if (other !is BinaryFile)
|
||||
return false
|
||||
|
||||
var sameData = false
|
||||
@@ -262,15 +262,15 @@ class BinaryAttachment : Parcelable {
|
||||
|
||||
companion object {
|
||||
|
||||
private val TAG = BinaryAttachment::class.java.name
|
||||
private val TAG = BinaryFile::class.java.name
|
||||
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.Creator<BinaryAttachment> = object : Parcelable.Creator<BinaryAttachment> {
|
||||
override fun createFromParcel(parcel: Parcel): BinaryAttachment {
|
||||
return BinaryAttachment(parcel)
|
||||
val CREATOR: Parcelable.Creator<BinaryFile> = object : Parcelable.Creator<BinaryFile> {
|
||||
override fun createFromParcel(parcel: Parcel): BinaryFile {
|
||||
return BinaryFile(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<BinaryAttachment?> {
|
||||
override fun newArray(size: Int): Array<BinaryFile?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
@@ -19,39 +19,58 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.element.database
|
||||
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
class BinaryPool {
|
||||
private val pool = LinkedHashMap<Int, BinaryAttachment>()
|
||||
abstract class BinaryPool<T> {
|
||||
|
||||
protected val pool = LinkedHashMap<T, BinaryFile>()
|
||||
|
||||
private var binaryFileIncrement = 0L // Unique file id (don't use current time because CPU too fast)
|
||||
|
||||
/**
|
||||
* To get a binary by the pool key (ref attribute in entry)
|
||||
*/
|
||||
operator fun get(key: Int): BinaryAttachment? {
|
||||
operator fun get(key: T): BinaryFile? {
|
||||
return pool[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a new binary file not yet linked to a binary
|
||||
*/
|
||||
fun put(cacheDirectory: File,
|
||||
key: T? = null,
|
||||
compression: Boolean = false,
|
||||
protection: Boolean = false): KeyBinary<T> {
|
||||
val fileInCache = File(cacheDirectory, binaryFileIncrement.toString())
|
||||
binaryFileIncrement++
|
||||
val newBinaryFile = BinaryFile(fileInCache, compression, protection)
|
||||
val newKey = put(key, newBinaryFile)
|
||||
return KeyBinary(newBinaryFile, newKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* To linked a binary with a pool key, if the pool key doesn't exists, create an unused one
|
||||
*/
|
||||
fun put(key: Int?, value: BinaryAttachment) {
|
||||
fun put(key: T?, value: BinaryFile): T {
|
||||
if (key == null)
|
||||
put(value)
|
||||
return put(value)
|
||||
else
|
||||
pool[key] = value
|
||||
return key
|
||||
}
|
||||
|
||||
/**
|
||||
* To put a [binaryAttachment] in the pool,
|
||||
* To put a [binaryFile] in the pool,
|
||||
* if already exists, replace the current one,
|
||||
* else add it with a new key
|
||||
*/
|
||||
fun put(binaryAttachment: BinaryAttachment): Int {
|
||||
var key = findKey(binaryAttachment)
|
||||
fun put(binaryFile: BinaryFile): T {
|
||||
var key: T? = findKey(binaryFile)
|
||||
if (key == null) {
|
||||
key = findUnusedKey()
|
||||
}
|
||||
pool[key] = binaryAttachment
|
||||
pool[key!!] = binaryFile
|
||||
return key
|
||||
}
|
||||
|
||||
@@ -59,8 +78,8 @@ class BinaryPool {
|
||||
* Remove a binary from the pool, the file is not deleted
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun remove(binaryAttachment: BinaryAttachment) {
|
||||
findKey(binaryAttachment)?.let {
|
||||
fun remove(binaryFile: BinaryFile) {
|
||||
findKey(binaryFile)?.let {
|
||||
pool.remove(it)
|
||||
}
|
||||
// Don't clear attachment here because a file can be used in many BinaryAttachment
|
||||
@@ -69,23 +88,18 @@ class BinaryPool {
|
||||
/**
|
||||
* Utility method to find an unused key in the pool
|
||||
*/
|
||||
private fun findUnusedKey(): Int {
|
||||
var unusedKey = 0
|
||||
while (pool[unusedKey] != null)
|
||||
unusedKey++
|
||||
return unusedKey
|
||||
}
|
||||
abstract fun findUnusedKey(): T
|
||||
|
||||
/**
|
||||
* Return key of [binaryAttachmentToRetrieve] or null if not found
|
||||
* Return key of [binaryFileToRetrieve] or null if not found
|
||||
*/
|
||||
private fun findKey(binaryAttachmentToRetrieve: BinaryAttachment): Int? {
|
||||
val contains = pool.containsValue(binaryAttachmentToRetrieve)
|
||||
private fun findKey(binaryFileToRetrieve: BinaryFile): T? {
|
||||
val contains = pool.containsValue(binaryFileToRetrieve)
|
||||
return if (!contains)
|
||||
null
|
||||
else {
|
||||
for ((key, binary) in pool) {
|
||||
if (binary == binaryAttachmentToRetrieve) {
|
||||
if (binary == binaryFileToRetrieve) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
@@ -93,54 +107,23 @@ class BinaryPool {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to order binaries and solve index problem in database v4
|
||||
*/
|
||||
private fun orderedBinaries(): List<KeyBinary> {
|
||||
val keyBinaryList = ArrayList<KeyBinary>()
|
||||
for ((key, binary) in pool) {
|
||||
// Don't deduplicate
|
||||
val existentBinary = keyBinaryList.find { it.binary.md5() == binary.md5() }
|
||||
if (existentBinary == null) {
|
||||
keyBinaryList.add(KeyBinary(binary, key))
|
||||
} else {
|
||||
existentBinary.addKey(key)
|
||||
}
|
||||
}
|
||||
return keyBinaryList
|
||||
}
|
||||
|
||||
/**
|
||||
* To register a binary with a ref corresponding to an ordered index
|
||||
*/
|
||||
fun getBinaryIndexFromKey(key: Int): Int? {
|
||||
val index = orderedBinaries().indexOfFirst { it.keys.contains(key) }
|
||||
return if (index < 0)
|
||||
null
|
||||
else
|
||||
index
|
||||
}
|
||||
|
||||
/**
|
||||
* Different from doForEach, provide an ordered index to each binary
|
||||
*/
|
||||
fun doForEachOrderedBinary(action: (index: Int, binary: BinaryAttachment) -> Unit) {
|
||||
orderedBinaries().forEachIndexed { index, keyBinary ->
|
||||
action.invoke(index, keyBinary.binary)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To do an action on each binary in the pool
|
||||
*/
|
||||
fun doForEachBinary(action: (binary: BinaryAttachment) -> Unit) {
|
||||
pool.values.forEach { action.invoke(it) }
|
||||
fun doForEachBinary(action: (key: T, binary: BinaryFile) -> Unit) {
|
||||
for ((key, value) in pool) {
|
||||
action.invoke(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
fun isEmpty(): Boolean {
|
||||
return pool.isEmpty()
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun clear() {
|
||||
doForEachBinary {
|
||||
it.clear()
|
||||
doForEachBinary { _, binary ->
|
||||
binary.clear()
|
||||
}
|
||||
pool.clear()
|
||||
}
|
||||
@@ -159,13 +142,13 @@ class BinaryPool {
|
||||
/**
|
||||
* Utility class to order binaries
|
||||
*/
|
||||
private class KeyBinary(val binary: BinaryAttachment, key: Int) {
|
||||
val keys = HashSet<Int>()
|
||||
class KeyBinary<T>(val binary: BinaryFile, key: T) {
|
||||
val keys = HashSet<T>()
|
||||
init {
|
||||
addKey(key)
|
||||
}
|
||||
|
||||
fun addKey(key: Int) {
|
||||
fun addKey(key: T) {
|
||||
keys.add(key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
getGroupById(backupGroupId)
|
||||
}
|
||||
|
||||
override val kdfEngine: KdfEngine?
|
||||
override val kdfEngine: KdfEngine
|
||||
get() = kdfListV3[0]
|
||||
|
||||
override val kdfAvailableList: List<KdfEngine>
|
||||
@@ -177,7 +177,7 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
}
|
||||
|
||||
override fun getStandardIcon(iconId: Int): IconImageStandard {
|
||||
return this.iconPool.getIcon(iconId)
|
||||
return this.iconsManager.getIcon(iconId)
|
||||
}
|
||||
|
||||
override fun containsCustomData(): Boolean {
|
||||
@@ -274,11 +274,11 @@ class DatabaseKDB : DatabaseVersioned<Int, UUID, GroupKDB, EntryKDB>() {
|
||||
addEntryTo(entry, origParent)
|
||||
}
|
||||
|
||||
fun buildNewAttachment(cacheDirectory: File): BinaryAttachment {
|
||||
fun buildNewAttachment(cacheDirectory: File): BinaryFile {
|
||||
// Generate an unique new file
|
||||
val fileInCache = File(cacheDirectory, binaryIncrement.toString())
|
||||
binaryIncrement++
|
||||
return BinaryAttachment(fileInCache)
|
||||
return BinaryFile(fileInCache)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -108,8 +108,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
val deletedObjects = ArrayList<DeletedObject>()
|
||||
val customData = HashMap<String, String>()
|
||||
|
||||
var binaryPool = BinaryPool()
|
||||
private var binaryIncrement = 0 // Unique id (don't use current time because CPU too fast)
|
||||
var binaryPool = AttachmentPool()
|
||||
|
||||
var localizedAppName = "KeePassDX"
|
||||
|
||||
@@ -211,7 +210,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
}
|
||||
|
||||
private fun compressAllBinaries() {
|
||||
binaryPool.doForEachBinary { binary ->
|
||||
binaryPool.doForEachBinary { _, binary ->
|
||||
try {
|
||||
val cipherKey = loadedCipherKey
|
||||
?: throw IOException("Unable to retrieve cipher key to compress binaries")
|
||||
@@ -224,7 +223,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
}
|
||||
|
||||
private fun decompressAllBinaries() {
|
||||
binaryPool.doForEachBinary { binary ->
|
||||
binaryPool.doForEachBinary { _, binary ->
|
||||
try {
|
||||
val cipherKey = loadedCipherKey
|
||||
?: throw IOException("Unable to retrieve cipher key to decompress binaries")
|
||||
@@ -308,19 +307,19 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
}
|
||||
|
||||
override fun getStandardIcon(iconId: Int): IconImageStandard {
|
||||
return this.iconPool.getIcon(iconId)
|
||||
return this.iconsManager.getIcon(iconId)
|
||||
}
|
||||
|
||||
fun getCustomIcon(iconUuid: UUID): IconImageCustom {
|
||||
return this.iconPool.getIcon(iconUuid)
|
||||
return this.iconsManager.getIcon(iconUuid)
|
||||
}
|
||||
|
||||
fun putCustomIcon(customIcon: IconImageCustom) {
|
||||
this.iconPool.putIcon(customIcon)
|
||||
this.iconsManager.putIcon(customIcon)
|
||||
}
|
||||
|
||||
fun containsCustomIcons(): Boolean {
|
||||
return this.iconPool.containsCustomIcons()
|
||||
return this.iconsManager.containsCustomIcons()
|
||||
}
|
||||
|
||||
fun putCustomData(label: String, value: String) {
|
||||
@@ -636,18 +635,12 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
fun buildNewAttachment(cacheDirectory: File,
|
||||
compression: Boolean,
|
||||
protection: Boolean,
|
||||
binaryPoolId: Int? = null): BinaryAttachment {
|
||||
// New file with current time
|
||||
val fileInCache = File(cacheDirectory, binaryIncrement.toString())
|
||||
binaryIncrement++
|
||||
val binaryAttachment = BinaryAttachment(fileInCache, compression, protection)
|
||||
// add attachment to pool
|
||||
binaryPool.put(binaryPoolId, binaryAttachment)
|
||||
return binaryAttachment
|
||||
binaryPoolId: Int? = null): BinaryFile {
|
||||
return binaryPool.put(cacheDirectory, binaryPoolId, compression, protection).binary
|
||||
}
|
||||
|
||||
fun removeUnlinkedAttachment(binary: BinaryAttachment, clear: Boolean) {
|
||||
val listBinaries = ArrayList<BinaryAttachment>()
|
||||
fun removeUnlinkedAttachment(binary: BinaryFile, clear: Boolean) {
|
||||
val listBinaries = ArrayList<BinaryFile>()
|
||||
listBinaries.add(binary)
|
||||
removeUnlinkedAttachments(listBinaries, clear)
|
||||
}
|
||||
@@ -656,11 +649,11 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
removeUnlinkedAttachments(emptyList(), clear)
|
||||
}
|
||||
|
||||
private fun removeUnlinkedAttachments(binaries: List<BinaryAttachment>, clear: Boolean) {
|
||||
private fun removeUnlinkedAttachments(binaries: List<BinaryFile>, clear: Boolean) {
|
||||
// Build binaries to remove with all binaries known
|
||||
val binariesToRemove = ArrayList<BinaryAttachment>()
|
||||
val binariesToRemove = ArrayList<BinaryFile>()
|
||||
if (binaries.isEmpty()) {
|
||||
binaryPool.doForEachBinary { binary ->
|
||||
binaryPool.doForEachBinary { _, binary ->
|
||||
binariesToRemove.add(binary)
|
||||
}
|
||||
} else {
|
||||
@@ -670,7 +663,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
rootGroup?.doForEachChild(object : NodeHandler<EntryKDBX>() {
|
||||
override fun operate(node: EntryKDBX): Boolean {
|
||||
node.getAttachments(binaryPool, true).forEach {
|
||||
binariesToRemove.remove(it.binaryAttachment)
|
||||
binariesToRemove.remove(it.binaryFile)
|
||||
}
|
||||
return binariesToRemove.isNotEmpty()
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.kunzisoft.keepass.database.element.entry.EntryVersioned
|
||||
import com.kunzisoft.keepass.database.element.group.GroupVersioned
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
||||
import com.kunzisoft.keepass.database.element.icon.IconPool
|
||||
import com.kunzisoft.keepass.database.element.icon.IconsManager
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
import com.kunzisoft.keepass.database.element.node.Type
|
||||
import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm
|
||||
@@ -54,7 +54,7 @@ abstract class DatabaseVersioned<
|
||||
var finalKey: ByteArray? = null
|
||||
protected set
|
||||
|
||||
val iconPool = IconPool()
|
||||
val iconsManager = IconsManager()
|
||||
|
||||
var changeDuplicateId = false
|
||||
|
||||
@@ -329,13 +329,8 @@ abstract class DatabaseVersioned<
|
||||
|
||||
abstract fun getStandardIcon(iconId: Int): IconImageStandard
|
||||
|
||||
fun buildNewCustomIcon(cacheDirectory: File): IconImageCustom {
|
||||
// New file with current time
|
||||
val fileInCache = File(cacheDirectory, System.currentTimeMillis().toString())
|
||||
val newCustomIcon = IconImageCustom(UUID.randomUUID())
|
||||
// add icon to pool
|
||||
iconPool.putIcon(newCustomIcon)
|
||||
return newCustomIcon
|
||||
fun buildNewCustomIcon(cacheDirectory: File, customIconId: UUID? = null): IconImageCustom {
|
||||
return iconsManager.buildNewCustomIcon(cacheDirectory, customIconId)
|
||||
}
|
||||
|
||||
abstract fun containsCustomData(): Boolean
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element.entry
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryAttachment
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryFile
|
||||
import com.kunzisoft.keepass.database.element.group.GroupKDB
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.KEY_ID
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
@@ -56,7 +56,7 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
|
||||
|
||||
/** A string describing what is in binaryData */
|
||||
var binaryDescription = ""
|
||||
var binaryData: BinaryAttachment? = null
|
||||
var binaryData: BinaryFile? = null
|
||||
|
||||
// Determine if this is a MetaStream entry
|
||||
val isMetaStream: Boolean
|
||||
@@ -89,7 +89,7 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
|
||||
url = parcel.readString() ?: url
|
||||
notes = parcel.readString() ?: notes
|
||||
binaryDescription = parcel.readString() ?: binaryDescription
|
||||
binaryData = parcel.readParcelable(BinaryAttachment::class.java.classLoader)
|
||||
binaryData = parcel.readParcelable(BinaryFile::class.java.classLoader)
|
||||
}
|
||||
|
||||
override fun readParentParcelable(parcel: Parcel): GroupKDB? {
|
||||
@@ -151,7 +151,7 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
|
||||
|
||||
fun putAttachment(attachment: Attachment) {
|
||||
this.binaryDescription = attachment.name
|
||||
this.binaryData = attachment.binaryAttachment
|
||||
this.binaryData = attachment.binaryFile
|
||||
}
|
||||
|
||||
fun removeAttachment(attachment: Attachment? = null) {
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.DateInstant
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryPool
|
||||
import com.kunzisoft.keepass.database.element.database.AttachmentPool
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
||||
import com.kunzisoft.keepass.database.element.group.GroupKDBX
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
@@ -56,7 +56,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
var additional = ""
|
||||
var tags = ""
|
||||
|
||||
fun getSize(binaryPool: BinaryPool): Long {
|
||||
fun getSize(attachmentPool: AttachmentPool): Long {
|
||||
var size = FIXED_LENGTH_SIZE
|
||||
|
||||
for (entry in fields.entries) {
|
||||
@@ -64,7 +64,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
size += entry.value.length().toLong()
|
||||
}
|
||||
|
||||
size += getAttachmentsSize(binaryPool)
|
||||
size += getAttachmentsSize(attachmentPool)
|
||||
|
||||
size += autoType.defaultSequence.length.toLong()
|
||||
for ((key, value) in autoType.entrySet()) {
|
||||
@@ -73,7 +73,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
}
|
||||
|
||||
for (entry in history) {
|
||||
size += entry.getSize(binaryPool)
|
||||
size += entry.getSize(attachmentPool)
|
||||
}
|
||||
|
||||
size += overrideURL.length.toLong()
|
||||
@@ -262,16 +262,16 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
/**
|
||||
* It's a list because history labels can be defined multiple times
|
||||
*/
|
||||
fun getAttachments(binaryPool: BinaryPool, inHistory: Boolean = false): List<Attachment> {
|
||||
fun getAttachments(attachmentPool: AttachmentPool, inHistory: Boolean = false): List<Attachment> {
|
||||
val entryAttachmentList = ArrayList<Attachment>()
|
||||
for ((label, poolId) in binaries) {
|
||||
binaryPool[poolId]?.let { binary ->
|
||||
attachmentPool[poolId]?.let { binary ->
|
||||
entryAttachmentList.add(Attachment(label, binary))
|
||||
}
|
||||
}
|
||||
if (inHistory) {
|
||||
history.forEach {
|
||||
entryAttachmentList.addAll(it.getAttachments(binaryPool, false))
|
||||
entryAttachmentList.addAll(it.getAttachments(attachmentPool, false))
|
||||
}
|
||||
}
|
||||
return entryAttachmentList
|
||||
@@ -281,8 +281,8 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
return binaries.isNotEmpty()
|
||||
}
|
||||
|
||||
fun putAttachment(attachment: Attachment, binaryPool: BinaryPool) {
|
||||
binaries[attachment.name] = binaryPool.put(attachment.binaryAttachment)
|
||||
fun putAttachment(attachment: Attachment, attachmentPool: AttachmentPool) {
|
||||
binaries[attachment.name] = attachmentPool.put(attachment.binaryFile)
|
||||
}
|
||||
|
||||
fun removeAttachment(attachment: Attachment) {
|
||||
@@ -293,11 +293,11 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
binaries.clear()
|
||||
}
|
||||
|
||||
private fun getAttachmentsSize(binaryPool: BinaryPool): Long {
|
||||
private fun getAttachmentsSize(attachmentPool: AttachmentPool): Long {
|
||||
var size = 0L
|
||||
for ((label, poolId) in binaries) {
|
||||
size += label.length.toLong()
|
||||
size += binaryPool[poolId]?.length ?: 0
|
||||
size += attachmentPool[poolId]?.length ?: 0
|
||||
}
|
||||
return size
|
||||
}
|
||||
@@ -314,7 +314,7 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
history.add(entry)
|
||||
}
|
||||
|
||||
fun removeEntryFromHistory(position: Int): EntryKDBX? {
|
||||
fun removeEntryFromHistory(position: Int): EntryKDBX {
|
||||
return history.removeAt(position)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,29 +21,34 @@ package com.kunzisoft.keepass.database.element.icon
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryFile
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||
import java.util.*
|
||||
|
||||
class IconImageCustom() : Parcelable {
|
||||
class IconImageCustom : Parcelable {
|
||||
|
||||
var uuid: UUID = DatabaseVersioned.UUID_ZERO
|
||||
@Transient
|
||||
var imageData: ByteArray = ByteArray(0)
|
||||
var uuid: UUID
|
||||
var binaryFile: BinaryFile
|
||||
|
||||
constructor(uuid: UUID, data: ByteArray) : this() {
|
||||
this.uuid = uuid
|
||||
this.imageData = data
|
||||
constructor() {
|
||||
uuid = DatabaseVersioned.UUID_ZERO
|
||||
binaryFile = BinaryFile()
|
||||
}
|
||||
|
||||
constructor(uuid: UUID) : this() {
|
||||
constructor(uuid: UUID,
|
||||
binaryFile: BinaryFile) {
|
||||
this.uuid = uuid
|
||||
this.imageData = ByteArray(0)
|
||||
this.binaryFile = binaryFile
|
||||
}
|
||||
|
||||
constructor(parcel: Parcel) : this() {
|
||||
constructor(uuid: UUID) {
|
||||
this.uuid = uuid
|
||||
binaryFile = BinaryFile()
|
||||
}
|
||||
|
||||
constructor(parcel: Parcel) {
|
||||
uuid = parcel.readSerializable() as UUID
|
||||
// TODO Take too much memories
|
||||
// parcel.readByteArray(imageData);
|
||||
binaryFile = parcel.readParcelable(BinaryFile::class.java.classLoader) ?: BinaryFile()
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
@@ -52,7 +57,7 @@ class IconImageCustom() : Parcelable {
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeSerializable(uuid)
|
||||
// Too big for a parcelable dest.writeByteArray(imageData);
|
||||
dest.writeParcelable(binaryFile, flags)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
||||
@@ -19,16 +19,17 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.element.icon
|
||||
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryPool
|
||||
import com.kunzisoft.keepass.icons.IconPack.Companion.NB_ICONS
|
||||
import java.util.ArrayList
|
||||
import java.util.UUID
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class IconPool {
|
||||
class IconsManager {
|
||||
|
||||
private val standardCache = List(NB_ICONS) {
|
||||
IconImageStandard(it)
|
||||
}
|
||||
private val customCache = HashMap<UUID, IconImageCustom?>()
|
||||
private val customCache = CustomIconPool()
|
||||
|
||||
fun getIcon(iconId: Int): IconImageStandard {
|
||||
return standardCache[iconId]
|
||||
@@ -42,33 +43,44 @@ class IconPool {
|
||||
* Custom
|
||||
*/
|
||||
|
||||
fun buildNewCustomIcon(cacheDirectory: File, key: UUID? = null): IconImageCustom {
|
||||
val keyBinary = customCache.put(cacheDirectory, key)
|
||||
return IconImageCustom(keyBinary.keys.first(), keyBinary.binary)
|
||||
}
|
||||
|
||||
fun putIcon(icon: IconImageCustom) {
|
||||
customCache[icon.uuid] = icon
|
||||
customCache.put(icon.uuid, icon.binaryFile)
|
||||
}
|
||||
|
||||
fun getIcon(iconUuid: UUID): IconImageCustom {
|
||||
var icon: IconImageCustom? = customCache[iconUuid]
|
||||
|
||||
if (icon == null) {
|
||||
icon = IconImageCustom(iconUuid)
|
||||
customCache[iconUuid] = icon
|
||||
customCache[iconUuid]?.let {
|
||||
return IconImageCustom(iconUuid, it)
|
||||
}
|
||||
|
||||
return icon
|
||||
return IconImageCustom(iconUuid)
|
||||
}
|
||||
|
||||
fun containsCustomIcons(): Boolean {
|
||||
return customCache.isNotEmpty()
|
||||
return !customCache.isEmpty()
|
||||
}
|
||||
|
||||
fun getCustomIconList(): List<IconImage> {
|
||||
val list = ArrayList<IconImage>(customCache.size)
|
||||
for ((_, customIcon) in customCache) {
|
||||
list.add(IconImage(customIcon!!))
|
||||
val list = ArrayList<IconImage>()
|
||||
customCache.doForEachBinary { key, binary ->
|
||||
list.add(IconImage(IconImageCustom(key, binary)))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
class CustomIconPool : BinaryPool<UUID>() {
|
||||
override fun findUnusedKey(): UUID {
|
||||
var newUUID = UUID.randomUUID()
|
||||
while (pool.containsKey(newUUID)) {
|
||||
newUUID = UUID.randomUUID()
|
||||
}
|
||||
return newUUID
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache of icons
|
||||
*/
|
||||
@@ -29,14 +29,13 @@ import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.DateInstant
|
||||
import com.kunzisoft.keepass.database.element.DeletedObject
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryAttachment
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryFile
|
||||
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX.Companion.BASE_64_FLAG
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
|
||||
import com.kunzisoft.keepass.database.element.group.GroupKDBX
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||
import com.kunzisoft.keepass.database.element.node.NodeKDBXInterface
|
||||
import com.kunzisoft.keepass.database.element.security.ProtectedString
|
||||
@@ -79,7 +78,7 @@ class DatabaseInputKDBX(cacheDirectory: File)
|
||||
private var ctxStringName: String? = null
|
||||
private var ctxStringValue: ProtectedString? = null
|
||||
private var ctxBinaryName: String? = null
|
||||
private var ctxBinaryValue: BinaryAttachment? = null
|
||||
private var ctxBinaryValue: BinaryFile? = null
|
||||
private var ctxATName: String? = null
|
||||
private var ctxATSeq: String? = null
|
||||
private var entryInHistory = false
|
||||
@@ -705,8 +704,12 @@ class DatabaseInputKDBX(cacheDirectory: File)
|
||||
return KdbContext.Meta
|
||||
} else if (ctx == KdbContext.CustomIcon && name.equals(DatabaseKDBXXML.ElemCustomIconItem, ignoreCase = true)) {
|
||||
if (customIconID != DatabaseVersioned.UUID_ZERO && customIconData != null) {
|
||||
val icon = IconImageCustom(customIconID, customIconData!!)
|
||||
mDatabase.putCustomIcon(icon)
|
||||
val customIcon = mDatabase.buildNewCustomIcon(cacheDirectory, customIconID)
|
||||
mDatabase.loadedCipherKey?.let { cipherKey ->
|
||||
customIcon.binaryFile.getOutputDataStream(cipherKey).use { outputStream ->
|
||||
outputStream.write(customIconData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customIconID = DatabaseVersioned.UUID_ZERO
|
||||
@@ -962,7 +965,7 @@ class DatabaseInputKDBX(cacheDirectory: File)
|
||||
}
|
||||
|
||||
@Throws(XmlPullParserException::class, IOException::class)
|
||||
private fun readBinary(xpp: XmlPullParser): BinaryAttachment? {
|
||||
private fun readBinary(xpp: XmlPullParser): BinaryFile? {
|
||||
|
||||
// Reference Id to a binary already present in binary pool
|
||||
val ref = xpp.getAttributeValue(null, DatabaseKDBXXML.AttrRef)
|
||||
@@ -993,7 +996,7 @@ class DatabaseInputKDBX(cacheDirectory: File)
|
||||
}
|
||||
|
||||
@Throws(IOException::class, XmlPullParserException::class)
|
||||
private fun createBinary(binaryId: Int?, xpp: XmlPullParser): BinaryAttachment? {
|
||||
private fun createBinary(binaryId: Int?, xpp: XmlPullParser): BinaryFile? {
|
||||
var compressed = false
|
||||
var protected = true
|
||||
|
||||
|
||||
@@ -701,11 +701,17 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
||||
|
||||
xml.startTag(null, DatabaseKDBXXML.ElemCustomIcons)
|
||||
|
||||
mDatabaseKDBX.iconPool.getCustomIconList().forEach { icon ->
|
||||
mDatabaseKDBX.iconsManager.getCustomIconList().forEach { icon ->
|
||||
xml.startTag(null, DatabaseKDBXXML.ElemCustomIconItem)
|
||||
|
||||
writeUuid(DatabaseKDBXXML.ElemCustomIconItemID, icon.custom.uuid)
|
||||
writeObject(DatabaseKDBXXML.ElemCustomIconItemData, String(Base64.encode(icon.custom.imageData, BASE_64_FLAG)))
|
||||
var customImageData = ByteArray(0)
|
||||
mDatabaseKDBX.loadedCipherKey?.let { cipherKey ->
|
||||
icon.custom.binaryFile.getInputDataStream(cipherKey).use { inputStream ->
|
||||
customImageData = inputStream.readBytes()
|
||||
}
|
||||
}
|
||||
writeObject(DatabaseKDBXXML.ElemCustomIconItemData, String(Base64.encode(customImageData, BASE_64_FLAG)))
|
||||
|
||||
xml.endTag(null, DatabaseKDBXXML.ElemCustomIconItem)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.widget.ImageViewCompat
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImage
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
import org.apache.commons.collections.map.AbstractReferenceMap
|
||||
@@ -44,7 +45,7 @@ import org.apache.commons.collections.map.ReferenceMap
|
||||
/**
|
||||
* Factory class who build database icons dynamically, can assign an icon of IconPack, or a custom icon to an ImageView with a tint
|
||||
*/
|
||||
class IconDrawableFactory {
|
||||
class IconDrawableFactory(private val retrieveCipherKey : () -> Database.LoadedKey?) {
|
||||
|
||||
/** customIconMap
|
||||
* Cache for icon drawable.
|
||||
@@ -155,19 +156,21 @@ class IconDrawableFactory {
|
||||
*/
|
||||
private fun getIconDrawable(resources: Resources, icon: IconImageCustom): Drawable {
|
||||
val patternIcon = PatternIcon(resources)
|
||||
|
||||
var draw: Drawable? = customIconMap[icon.uuid] as Drawable?
|
||||
if (draw == null) {
|
||||
var bitmap: Bitmap? = BitmapFactory.decodeByteArray(icon.imageData, 0, icon.imageData.size)
|
||||
// Could not understand custom icon
|
||||
bitmap?.let { bitmapIcon ->
|
||||
bitmap = resize(bitmapIcon, patternIcon)
|
||||
draw = BitmapDrawable(resources, bitmap)
|
||||
customIconMap[icon.uuid] = draw
|
||||
val cipherKey = retrieveCipherKey.invoke()
|
||||
if (cipherKey != null) {
|
||||
var draw: Drawable? = customIconMap[icon.uuid] as Drawable?
|
||||
if (draw == null) {
|
||||
var bitmap: Bitmap? = BitmapFactory.decodeStream(icon.binaryFile.getInputDataStream(cipherKey))
|
||||
// Could not understand custom icon
|
||||
bitmap?.let { bitmapIcon ->
|
||||
bitmap = resize(bitmapIcon, patternIcon)
|
||||
draw = BitmapDrawable(resources, bitmap)
|
||||
customIconMap[icon.uuid] = draw
|
||||
return draw!!
|
||||
}
|
||||
} else {
|
||||
return draw!!
|
||||
}
|
||||
} else {
|
||||
return draw!!
|
||||
}
|
||||
return patternIcon.blankDrawable
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.model
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryAttachment
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryFile
|
||||
import com.kunzisoft.keepass.utils.readEnum
|
||||
import com.kunzisoft.keepass.utils.writeEnum
|
||||
|
||||
@@ -33,7 +33,7 @@ data class EntryAttachmentState(var attachment: Attachment,
|
||||
var previewState: AttachmentState = AttachmentState.NULL) : Parcelable {
|
||||
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readParcelable(Attachment::class.java.classLoader) ?: Attachment("", BinaryAttachment()),
|
||||
parcel.readParcelable(Attachment::class.java.classLoader) ?: Attachment("", BinaryFile()),
|
||||
parcel.readEnum<StreamDirection>() ?: StreamDirection.DOWNLOAD,
|
||||
parcel.readEnum<AttachmentState>() ?: AttachmentState.NULL,
|
||||
parcel.readInt(),
|
||||
|
||||
@@ -30,7 +30,7 @@ import androidx.documentfile.provider.DocumentFile
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryAttachment
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryFile
|
||||
import com.kunzisoft.keepass.model.AttachmentState
|
||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||
import com.kunzisoft.keepass.model.StreamDirection
|
||||
@@ -86,7 +86,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
fun onAttachmentAction(fileUri: Uri, entryAttachmentState: EntryAttachmentState)
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
override fun onBind(intent: Intent): IBinder {
|
||||
return mActionTaskBinder
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
StreamDirection.UPLOAD -> {
|
||||
uploadToDatabase(
|
||||
attachmentNotification.uri,
|
||||
attachment.binaryAttachment,
|
||||
attachment.binaryFile,
|
||||
contentResolver, 1024,
|
||||
{ // Cancellation
|
||||
downloadState == AttachmentState.CANCELED
|
||||
@@ -361,7 +361,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
StreamDirection.DOWNLOAD -> {
|
||||
downloadFromDatabase(
|
||||
attachmentNotification.uri,
|
||||
attachment.binaryAttachment,
|
||||
attachment.binaryFile,
|
||||
contentResolver, 1024) { percent ->
|
||||
publishProgress(percent)
|
||||
}
|
||||
@@ -397,15 +397,15 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
}
|
||||
|
||||
fun downloadFromDatabase(attachmentToUploadUri: Uri,
|
||||
binaryAttachment: BinaryAttachment,
|
||||
binaryFile: BinaryFile,
|
||||
contentResolver: ContentResolver,
|
||||
bufferSize: Int = DEFAULT_BUFFER_SIZE,
|
||||
update: ((percent: Int)->Unit)? = null) {
|
||||
var dataDownloaded = 0L
|
||||
val fileSize = binaryAttachment.length
|
||||
val fileSize = binaryFile.length
|
||||
UriUtil.getUriOutputStream(contentResolver, attachmentToUploadUri)?.use { outputStream ->
|
||||
Database.getInstance().loadedCipherKey?.let { binaryCipherKey ->
|
||||
binaryAttachment.getUnGzipInputDataStream(binaryCipherKey).use { inputStream ->
|
||||
binaryFile.getUnGzipInputDataStream(binaryCipherKey).use { inputStream ->
|
||||
inputStream.readAllBytes(bufferSize) { buffer ->
|
||||
outputStream.write(buffer)
|
||||
dataDownloaded += buffer.size
|
||||
@@ -422,7 +422,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
}
|
||||
|
||||
fun uploadToDatabase(attachmentFromDownloadUri: Uri,
|
||||
binaryAttachment: BinaryAttachment,
|
||||
binaryFile: BinaryFile,
|
||||
contentResolver: ContentResolver,
|
||||
bufferSize: Int = DEFAULT_BUFFER_SIZE,
|
||||
canceled: ()-> Boolean = { false },
|
||||
@@ -431,7 +431,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
val fileSize = contentResolver.openFileDescriptor(attachmentFromDownloadUri, "r")?.statSize ?: 0
|
||||
UriUtil.getUriInputStream(contentResolver, attachmentFromDownloadUri)?.use { inputStream ->
|
||||
Database.getInstance().loadedCipherKey?.let { binaryCipherKey ->
|
||||
binaryAttachment.getGzipOutputDataStream(binaryCipherKey).use { outputStream ->
|
||||
binaryFile.getGzipOutputDataStream(binaryCipherKey).use { outputStream ->
|
||||
inputStream.readAllBytes(bufferSize, canceled) { buffer ->
|
||||
outputStream.write(buffer)
|
||||
dataUploaded += buffer.size
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.kunzisoft.keepass.tasks
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
|
||||
class IconUploadWorker(appContext: Context, workerParams: WorkerParameters):
|
||||
Worker(appContext, workerParams) {
|
||||
override fun doWork(): Result {
|
||||
|
||||
//uploadImages()
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,10 @@ class IconPickerViewModel: ViewModel() {
|
||||
MutableLiveData<IconImageCustom>()
|
||||
}
|
||||
|
||||
val iconCustomAdded: MutableLiveData<IconImageCustom> by lazy {
|
||||
MutableLiveData<IconImageCustom>()
|
||||
}
|
||||
|
||||
fun selectIconStandard(icon: IconImageStandard) {
|
||||
iconStandardSelected.value = icon
|
||||
}
|
||||
@@ -22,4 +26,8 @@ class IconPickerViewModel: ViewModel() {
|
||||
fun selectIconCustom(icon: IconImageCustom) {
|
||||
iconCustomSelected.value = icon
|
||||
}
|
||||
|
||||
fun addCustomIcon(icon: IconImageCustom) {
|
||||
iconCustomAdded.value = icon
|
||||
}
|
||||
}
|
||||
@@ -434,7 +434,7 @@
|
||||
<string name="unit_gibibyte">GiB</string>
|
||||
<string name="unit_mebibyte">MiB</string>
|
||||
<string name="unit_kibibyte">KiB</string>
|
||||
<string name="upload_attachment">Last opp</string>
|
||||
<string name="upload_attachment">Last opp %1$s</string>
|
||||
<string name="education_add_attachment_title">Legg til vedlegg</string>
|
||||
<string name="autofill_save_search_info_title">Lagre søkeinfo</string>
|
||||
<string name="autofill_close_database_title">Lukk database</string>
|
||||
|
||||
Reference in New Issue
Block a user