From 5aa3f796168ebbe5fa45fbb1588b22e9882ab1ca Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 1 Mar 2021 15:50:11 +0100 Subject: [PATCH] Upload custom attachment and fix warning --- ...aryAttachmentTest.kt => BinaryFileTest.kt} | 34 +++--- .../keepass/activities/EntryEditActivity.kt | 5 +- .../keepass/activities/IconPickerActivity.kt | 49 +++++++- .../keepass/activities/ImageViewerActivity.kt | 2 +- .../fragments/IconCustomFragment.kt | 7 +- .../activities/fragments/IconFragment.kt | 4 +- .../fragments/IconStandardFragment.kt | 2 +- .../adapters/EntryAttachmentsItemsAdapter.kt | 8 +- .../adapters/SearchEntryCursorAdapter.kt | 4 +- .../action/node/DeleteNodesRunnable.kt | 2 +- .../action/node/UpdateEntryRunnable.kt | 8 +- .../keepass/database/cursor/EntryCursor.kt | 8 +- .../database/cursor/EntryCursorKDBX.kt | 6 +- .../keepass/database/element/Attachment.kt | 12 +- .../keepass/database/element/Database.kt | 34 +++--- .../keepass/database/element/Entry.kt | 20 ++-- .../element/database/AttachmentPool.kt | 70 +++++++++++ .../{BinaryAttachment.kt => BinaryFile.kt} | 18 +-- .../database/element/database/BinaryPool.kt | 113 ++++++++---------- .../database/element/database/DatabaseKDB.kt | 8 +- .../database/element/database/DatabaseKDBX.kt | 37 +++--- .../element/database/DatabaseVersioned.kt | 13 +- .../database/element/entry/EntryKDB.kt | 8 +- .../database/element/entry/EntryKDBX.kt | 24 ++-- .../database/element/icon/IconImageCustom.kt | 31 +++-- .../icon/{IconPool.kt => IconsManager.kt} | 44 ++++--- .../database/file/input/DatabaseInputKDBX.kt | 17 +-- .../file/output/DatabaseOutputKDBX.kt | 10 +- .../keepass/icons/IconDrawableFactory.kt | 27 +++-- .../keepass/model/EntryAttachmentState.kt | 4 +- .../AttachmentFileNotificationService.kt | 18 +-- .../keepass/tasks/IconUploadWorker.kt | 15 +++ .../keepass/viewmodels/IconPickerViewModel.kt | 8 ++ app/src/main/res/values-nb/strings.xml | 2 +- 34 files changed, 405 insertions(+), 267 deletions(-) rename app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/{BinaryAttachmentTest.kt => BinaryFileTest.kt} (84%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/database/AttachmentPool.kt rename app/src/main/java/com/kunzisoft/keepass/database/element/database/{BinaryAttachment.kt => BinaryFile.kt} (94%) rename app/src/main/java/com/kunzisoft/keepass/database/element/icon/{IconPool.kt => IconsManager.kt} (59%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/tasks/IconUploadWorker.kt diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/BinaryAttachmentTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/BinaryFileTest.kt similarity index 84% rename from app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/BinaryAttachmentTest.kt rename to app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/BinaryFileTest.kt index 4e1b651c6..ea110a45d 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/BinaryAttachmentTest.kt +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/BinaryFileTest.kt @@ -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))) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt index d0ac6b541..4eaa6d176 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -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) } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt index 7262dc09c..2a6544faf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt @@ -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) } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ImageViewerActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/ImageViewerActivity.kt index 5d12da108..1e15f35c8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ImageViewerActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ImageViewerActivity.kt @@ -55,7 +55,7 @@ class ImageViewerActivity : LockingActivity() { intent.getParcelableExtra(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) { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconCustomFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconCustomFragment.kt index 4ff2b0c1f..9a1cf71af 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconCustomFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconCustomFragment.kt @@ -18,12 +18,17 @@ class IconCustomFragment : IconFragment() { } override fun defineIconList(database: Database): List { - 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) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconFragment.kt index e60a881b3..6b7232161 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconFragment.kt @@ -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)) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconStandardFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconStandardFragment.kt index c5f111df7..efbd51a78 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconStandardFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/IconStandardFragment.kt @@ -18,7 +18,7 @@ class IconStandardFragment : IconFragment() { } override fun defineIconList(database: Database): List { - return database.iconPool.getStandardIconList() + return database.iconsManager.getStandardIconList() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt index 904aac8b6..da7445365 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryAttachmentsItemsAdapter.kt @@ -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 { diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt index eee45b078..05ecbf376 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt @@ -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) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteNodesRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteNodesRunnable.kt index c3a0c002f..e9102a69d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteNodesRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteNodesRunnable.kt @@ -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) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt index 0f211332a..be71e580e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt @@ -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(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) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.kt b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.kt index d8f6fc34e..45f257bc4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.kt @@ -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> abstract fun getPwNodeId(): NodeId - 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) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorKDBX.kt index 23bdb3d50..e0c82ee89 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorKDBX.kt @@ -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() { @@ -50,8 +50,8 @@ class EntryCursorKDBX : EntryCursorUUID() { 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()) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt index 9c200d372..d0ac61491 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Attachment.kt @@ -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 diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index fc1eb46ff..c5a5a565c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -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() { 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) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt index f8519a65a..1d0eeee1a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt @@ -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 { entryKDBX?.stopToManageFieldReferences() } - fun getAttachments(binaryPool: BinaryPool, inHistory: Boolean = false): List { + fun getAttachments(attachmentPool: AttachmentPool, inHistory: Boolean = false): List { val attachments = ArrayList() 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 { 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 { @@ -371,8 +369,8 @@ class Entry : Node, EntryVersionedInterface { 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 { // 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 { url = newEntryInfo.url notes = newEntryInfo.notes addExtraFields(newEntryInfo.customFields) - database?.binaryPool?.let { binaryPool -> + database?.attachmentPool?.let { binaryPool -> newEntryInfo.attachments.forEach { attachment -> putAttachment(attachment, binaryPool) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/AttachmentPool.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/AttachmentPool.kt new file mode 100644 index 000000000..565a16f06 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/AttachmentPool.kt @@ -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 . + * + */ +package com.kunzisoft.keepass.database.element.database + +class AttachmentPool : BinaryPool() { + + /** + * 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> { + val keyBinaryList = ArrayList>() + 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) + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryAttachment.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryFile.kt similarity index 94% rename from app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryAttachment.kt rename to app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryFile.kt index 7be0d7afb..c3b63b017 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryAttachment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryFile.kt @@ -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 = object : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): BinaryAttachment { - return BinaryAttachment(parcel) + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): BinaryFile { + return BinaryFile(parcel) } - override fun newArray(size: Int): Array { + override fun newArray(size: Int): Array { return arrayOfNulls(size) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryPool.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryPool.kt index 55400742b..1d7e71d96 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryPool.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/BinaryPool.kt @@ -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() +abstract class BinaryPool { + + protected val pool = LinkedHashMap() + + 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 { + 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 { - val keyBinaryList = ArrayList() - 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() + class KeyBinary(val binary: BinaryFile, key: T) { + val keys = HashSet() init { addKey(key) } - fun addKey(key: Int) { + fun addKey(key: T) { keys.add(key) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt index d815851c4..6592325bb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt @@ -69,7 +69,7 @@ class DatabaseKDB : DatabaseVersioned() { getGroupById(backupGroupId) } - override val kdfEngine: KdfEngine? + override val kdfEngine: KdfEngine get() = kdfListV3[0] override val kdfAvailableList: List @@ -177,7 +177,7 @@ class DatabaseKDB : DatabaseVersioned() { } 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() { 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 { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt index af5cebb8f..ebc623d7f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt @@ -108,8 +108,7 @@ class DatabaseKDBX : DatabaseVersioned { val deletedObjects = ArrayList() val customData = HashMap() - 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 { } 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 { } 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 { } 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 { 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() + fun removeUnlinkedAttachment(binary: BinaryFile, clear: Boolean) { + val listBinaries = ArrayList() listBinaries.add(binary) removeUnlinkedAttachments(listBinaries, clear) } @@ -656,11 +649,11 @@ class DatabaseKDBX : DatabaseVersioned { removeUnlinkedAttachments(emptyList(), clear) } - private fun removeUnlinkedAttachments(binaries: List, clear: Boolean) { + private fun removeUnlinkedAttachments(binaries: List, clear: Boolean) { // Build binaries to remove with all binaries known - val binariesToRemove = ArrayList() + val binariesToRemove = ArrayList() if (binaries.isEmpty()) { - binaryPool.doForEachBinary { binary -> + binaryPool.doForEachBinary { _, binary -> binariesToRemove.add(binary) } } else { @@ -670,7 +663,7 @@ class DatabaseKDBX : DatabaseVersioned { rootGroup?.doForEachChild(object : NodeHandler() { override fun operate(node: EntryKDBX): Boolean { node.getAttachments(binaryPool, true).forEach { - binariesToRemove.remove(it.binaryAttachment) + binariesToRemove.remove(it.binaryFile) } return binariesToRemove.isNotEmpty() } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt index 3d003f9f8..54d1b85c4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt @@ -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 diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt index 96f46254d..3e642b896 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt @@ -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, 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, 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, NodeKDBInterface fun putAttachment(attachment: Attachment) { this.binaryDescription = attachment.name - this.binaryData = attachment.binaryAttachment + this.binaryData = attachment.binaryFile } fun removeAttachment(attachment: Attachment? = null) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt index 997483fd5..351561d81 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt @@ -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, 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, 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, NodeKDBXInte } for (entry in history) { - size += entry.getSize(binaryPool) + size += entry.getSize(attachmentPool) } size += overrideURL.length.toLong() @@ -262,16 +262,16 @@ class EntryKDBX : EntryVersioned, NodeKDBXInte /** * It's a list because history labels can be defined multiple times */ - fun getAttachments(binaryPool: BinaryPool, inHistory: Boolean = false): List { + fun getAttachments(attachmentPool: AttachmentPool, inHistory: Boolean = false): List { val entryAttachmentList = ArrayList() 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, 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, 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, NodeKDBXInte history.add(entry) } - fun removeEntryFromHistory(position: Int): EntryKDBX? { + fun removeEntryFromHistory(position: Int): EntryKDBX { return history.removeAt(position) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageCustom.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageCustom.kt index 1bb36e9ec..dcf2e1b93 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageCustom.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageCustom.kt @@ -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 { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconPool.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconsManager.kt similarity index 59% rename from app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconPool.kt rename to app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconsManager.kt index c27cf51db..06ccabd24 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconPool.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconsManager.kt @@ -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() + 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 { - val list = ArrayList(customCache.size) - for ((_, customIcon) in customCache) { - list.add(IconImage(customIcon!!)) + val list = ArrayList() + customCache.doForEachBinary { key, binary -> + list.add(IconImage(IconImageCustom(key, binary))) } return list } + class CustomIconPool : BinaryPool() { + override fun findUnusedKey(): UUID { + var newUUID = UUID.randomUUID() + while (pool.containsKey(newUUID)) { + newUUID = UUID.randomUUID() + } + return newUUID + } + } + /** * Clear the cache of icons */ diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt index ad98ab22c..8378efc9a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt @@ -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 diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt index 34f695529..c886a5228 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt @@ -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) } diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt index 06a244b70..48ae64bde 100644 --- a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt +++ b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt @@ -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 } diff --git a/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt b/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt index 26b9ed5e4..c297edada 100644 --- a/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt +++ b/app/src/main/java/com/kunzisoft/keepass/model/EntryAttachmentState.kt @@ -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.DOWNLOAD, parcel.readEnum() ?: AttachmentState.NULL, parcel.readInt(), diff --git a/app/src/main/java/com/kunzisoft/keepass/services/AttachmentFileNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/AttachmentFileNotificationService.kt index adfd276ca..a97555d86 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/AttachmentFileNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/AttachmentFileNotificationService.kt @@ -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 diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/IconUploadWorker.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/IconUploadWorker.kt new file mode 100644 index 000000000..f226ad05d --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/tasks/IconUploadWorker.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/viewmodels/IconPickerViewModel.kt b/app/src/main/java/com/kunzisoft/keepass/viewmodels/IconPickerViewModel.kt index ff72017e2..5697ca86f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/viewmodels/IconPickerViewModel.kt +++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/IconPickerViewModel.kt @@ -15,6 +15,10 @@ class IconPickerViewModel: ViewModel() { MutableLiveData() } + val iconCustomAdded: MutableLiveData by lazy { + MutableLiveData() + } + 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 + } } \ No newline at end of file diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index b34a5b7c0..400c0a9d4 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -434,7 +434,7 @@ GiB MiB KiB - Last opp + Last opp %1$s Legg til vedlegg Lagre søkeinfo Lukk database