mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Externalize binary in custom icon
This commit is contained in:
@@ -229,13 +229,17 @@ class IconPickerActivity : LockingActivity() {
|
||||
if (documentFile.length() > MAX_ICON_SIZE) {
|
||||
iconCustomState.errorStringId = R.string.error_file_to_big
|
||||
} else {
|
||||
mDatabase?.buildNewCustomIcon(UriUtil.getBinaryDir(this@IconPickerActivity))?.let { customIcon ->
|
||||
mDatabase?.buildNewCustomIcon(UriUtil.getBinaryDir(this@IconPickerActivity)) { customIcon, binary ->
|
||||
if (customIcon != null) {
|
||||
iconCustomState.iconCustom = customIcon
|
||||
BinaryDatabaseManager.resizeBitmapAndStoreDataInBinaryFile(contentResolver,
|
||||
iconToUploadUri, customIcon.binaryFile)
|
||||
iconToUploadUri, binary)
|
||||
when {
|
||||
customIcon.binaryFile?.length ?: 0 <= 0 -> {}
|
||||
mDatabase?.isCustomIconBinaryDuplicate(customIcon) == true -> {
|
||||
binary == null -> {
|
||||
}
|
||||
binary.length <= 0 -> {
|
||||
}
|
||||
mDatabase?.isCustomIconBinaryDuplicate(binary) == true -> {
|
||||
iconCustomState.errorStringId = R.string.error_duplicate_file
|
||||
}
|
||||
else -> {
|
||||
@@ -248,6 +252,7 @@ class IconPickerActivity : LockingActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
iconCustomState
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
|
||||
@@ -32,7 +32,7 @@ class IconCustomFragment : IconFragment<IconImageCustom>() {
|
||||
}
|
||||
|
||||
override fun defineIconList() {
|
||||
mDatabase?.doForEachCustomIcons { customIcon ->
|
||||
mDatabase?.doForEachCustomIcons { customIcon, _ ->
|
||||
iconPickerAdapter.addIcon(customIcon, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,10 @@ class Database {
|
||||
|
||||
var isReadOnly = false
|
||||
|
||||
val iconDrawableFactory = IconDrawableFactory { loadedCipherKey }
|
||||
val iconDrawableFactory = IconDrawableFactory(
|
||||
{ loadedCipherKey },
|
||||
{ iconId -> iconsManager.getBinaryForCustomIcon(iconId) }
|
||||
)
|
||||
|
||||
var loaded = false
|
||||
set(value) {
|
||||
@@ -114,7 +117,7 @@ class Database {
|
||||
val allowCustomIcons: Boolean
|
||||
get() = mDatabaseKDBX != null
|
||||
|
||||
fun doForEachCustomIcons(action: (IconImageCustom) -> Unit) {
|
||||
fun doForEachCustomIcons(action: (IconImageCustom, BinaryFile) -> Unit) {
|
||||
return iconsManager.doForEachCustomIcon(action)
|
||||
}
|
||||
|
||||
@@ -122,12 +125,13 @@ class Database {
|
||||
return iconsManager.getIcon(iconId)
|
||||
}
|
||||
|
||||
fun buildNewCustomIcon(cacheDirectory: File): IconImageCustom? {
|
||||
return mDatabaseKDBX?.buildNewCustomIcon(cacheDirectory)
|
||||
fun buildNewCustomIcon(cacheDirectory: File,
|
||||
result: (IconImageCustom?, BinaryFile?) -> Unit) {
|
||||
mDatabaseKDBX?.buildNewCustomIcon(cacheDirectory, null, result)
|
||||
}
|
||||
|
||||
fun isCustomIconBinaryDuplicate(customIcon: IconImageCustom): Boolean {
|
||||
return mDatabaseKDBX?.isCustomIconBinaryDuplicate(customIcon) ?: false
|
||||
fun isCustomIconBinaryDuplicate(binaryFile: BinaryFile): Boolean {
|
||||
return mDatabaseKDBX?.isCustomIconBinaryDuplicate(binaryFile) ?: false
|
||||
}
|
||||
|
||||
fun removeCustomIcon(customIcon: IconImageCustom) {
|
||||
|
||||
@@ -177,6 +177,10 @@ class BinaryFile : Parcelable {
|
||||
throw IOException("Unable to delete temp file " + dataFile!!.absolutePath)
|
||||
}
|
||||
|
||||
fun dataExists(): Boolean {
|
||||
return dataFile != null && length > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* MD5 of the raw encrypted file in temp folder, only to compare binary data
|
||||
*/
|
||||
|
||||
@@ -310,12 +310,14 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
|
||||
return this.iconsManager.getIcon(iconId)
|
||||
}
|
||||
|
||||
fun buildNewCustomIcon(cacheDirectory: File, customIconId: UUID? = null): IconImageCustom {
|
||||
return iconsManager.buildNewCustomIcon(cacheDirectory, customIconId)
|
||||
fun buildNewCustomIcon(cacheDirectory: File,
|
||||
customIconId: UUID? = null,
|
||||
result: (IconImageCustom, BinaryFile?) -> Unit) {
|
||||
iconsManager.buildNewCustomIcon(cacheDirectory, customIconId, result)
|
||||
}
|
||||
|
||||
fun isCustomIconBinaryDuplicate(customIcon: IconImageCustom): Boolean {
|
||||
return iconsManager.isCustomIconBinaryDuplicate(customIcon)
|
||||
fun isCustomIconBinaryDuplicate(binary: BinaryFile): Boolean {
|
||||
return iconsManager.isCustomIconBinaryDuplicate(binary)
|
||||
}
|
||||
|
||||
fun getCustomIcon(iconUuid: UUID): IconImageCustom {
|
||||
|
||||
@@ -21,34 +21,23 @@ 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, IconImageDraw {
|
||||
|
||||
var uuid: UUID
|
||||
var binaryFile: BinaryFile?
|
||||
|
||||
constructor() {
|
||||
uuid = DatabaseVersioned.UUID_ZERO
|
||||
binaryFile = null
|
||||
}
|
||||
|
||||
constructor(uuid: UUID,
|
||||
binaryFile: BinaryFile) {
|
||||
this.uuid = uuid
|
||||
this.binaryFile = binaryFile
|
||||
}
|
||||
|
||||
constructor(uuid: UUID) {
|
||||
this.uuid = uuid
|
||||
binaryFile = BinaryFile()
|
||||
}
|
||||
|
||||
constructor(parcel: Parcel) {
|
||||
uuid = parcel.readSerializable() as UUID
|
||||
binaryFile = parcel.readParcelable(BinaryFile::class.java.classLoader)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
@@ -57,7 +46,6 @@ class IconImageCustom : Parcelable, IconImageDraw {
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeSerializable(uuid)
|
||||
dest.writeParcelable(binaryFile, flags)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
@@ -84,12 +72,6 @@ class IconImageCustom : Parcelable, IconImageDraw {
|
||||
val isUnknown: Boolean
|
||||
get() = uuid == DatabaseVersioned.UUID_ZERO
|
||||
|
||||
val dataExists: Boolean
|
||||
get() {
|
||||
val iconData = binaryFile
|
||||
return !isUnknown && iconData != null && iconData.length > 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package com.kunzisoft.keepass.database.element.icon
|
||||
|
||||
import android.util.Log
|
||||
import com.kunzisoft.keepass.database.element.database.BinaryFile
|
||||
import com.kunzisoft.keepass.database.element.database.CustomIconPool
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.KEY_ID
|
||||
import com.kunzisoft.keepass.icons.IconPack.Companion.NB_ICONS
|
||||
@@ -49,20 +50,19 @@ class IconsManager {
|
||||
* Custom
|
||||
*/
|
||||
|
||||
fun buildNewCustomIcon(cacheDirectory: File, key: UUID? = null): IconImageCustom {
|
||||
fun buildNewCustomIcon(cacheDirectory: File,
|
||||
key: UUID? = null,
|
||||
result: (IconImageCustom, BinaryFile?) -> Unit) {
|
||||
val keyBinary = customCache.put(cacheDirectory, key)
|
||||
return IconImageCustom(keyBinary.keys.first(), keyBinary.binary)
|
||||
result.invoke(IconImageCustom(keyBinary.keys.first()), keyBinary.binary)
|
||||
}
|
||||
|
||||
fun getIcon(iconUuid: UUID): IconImageCustom {
|
||||
customCache[iconUuid]?.let {
|
||||
return IconImageCustom(iconUuid, it)
|
||||
}
|
||||
return IconImageCustom(iconUuid)
|
||||
}
|
||||
|
||||
fun isCustomIconBinaryDuplicate(icon: IconImageCustom): Boolean {
|
||||
return customCache.isBinaryDuplicate(icon.binaryFile)
|
||||
fun isCustomIconBinaryDuplicate(binaryFile: BinaryFile): Boolean {
|
||||
return customCache.isBinaryDuplicate(binaryFile)
|
||||
}
|
||||
|
||||
fun removeCustomIcon(iconUuid: UUID) {
|
||||
@@ -75,9 +75,13 @@ class IconsManager {
|
||||
}
|
||||
}
|
||||
|
||||
fun doForEachCustomIcon(action: (IconImageCustom) -> Unit) {
|
||||
fun getBinaryForCustomIcon(iconUuid: UUID): BinaryFile? {
|
||||
return customCache[iconUuid]
|
||||
}
|
||||
|
||||
fun doForEachCustomIcon(action: (IconImageCustom, BinaryFile) -> Unit) {
|
||||
customCache.doForEachBinary { key, binary ->
|
||||
action.invoke(IconImageCustom(key, binary))
|
||||
action.invoke(IconImageCustom(key), binary)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -704,13 +704,14 @@ 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 customIcon = mDatabase.buildNewCustomIcon(cacheDirectory, customIconID)
|
||||
mDatabase.buildNewCustomIcon(cacheDirectory, customIconID) { _, binary ->
|
||||
mDatabase.loadedCipherKey?.let { cipherKey ->
|
||||
customIcon.binaryFile?.getOutputDataStream(cipherKey)?.use { outputStream ->
|
||||
binary?.getOutputDataStream(cipherKey)?.use { outputStream ->
|
||||
outputStream.write(customIconData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customIconID = DatabaseVersioned.UUID_ZERO
|
||||
customIconData = null
|
||||
|
||||
@@ -363,7 +363,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
||||
writeObject(DatabaseKDBXXML.ElemNotes, group.notes)
|
||||
writeObject(DatabaseKDBXXML.ElemIcon, group.icon.standard.id.toLong())
|
||||
|
||||
if (group.icon.custom.dataExists) {
|
||||
if (!group.icon.custom.isUnknown) {
|
||||
writeUuid(DatabaseKDBXXML.ElemCustomIconID, group.icon.custom.uuid)
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
||||
writeUuid(DatabaseKDBXXML.ElemUuid, entry.id)
|
||||
writeObject(DatabaseKDBXXML.ElemIcon, entry.icon.standard.id.toLong())
|
||||
|
||||
if (entry.icon.custom.dataExists) {
|
||||
if (!entry.icon.custom.isUnknown) {
|
||||
writeUuid(DatabaseKDBXXML.ElemCustomIconID, entry.icon.custom.uuid)
|
||||
}
|
||||
|
||||
@@ -701,8 +701,8 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
||||
private fun writeCustomIconList() {
|
||||
mDatabaseKDBX.loadedCipherKey?.let { cipherKey ->
|
||||
var firstElement = true
|
||||
mDatabaseKDBX.iconsManager.doForEachCustomIcon { iconCustom ->
|
||||
if (iconCustom.dataExists) {
|
||||
mDatabaseKDBX.iconsManager.doForEachCustomIcon { iconCustom, binary ->
|
||||
if (binary.dataExists()) {
|
||||
// Write the parent tag
|
||||
if (firstElement) {
|
||||
xml.startTag(null, DatabaseKDBXXML.ElemCustomIcons)
|
||||
@@ -714,7 +714,7 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
|
||||
writeUuid(DatabaseKDBXXML.ElemCustomIconItemID, iconCustom.uuid)
|
||||
var customImageData = ByteArray(0)
|
||||
try {
|
||||
iconCustom.binaryFile?.getInputDataStream(cipherKey)?.use { inputStream ->
|
||||
binary.getInputDataStream(cipherKey).use { inputStream ->
|
||||
customImageData = inputStream.readBytes()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -34,6 +34,7 @@ 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.database.BinaryFile
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageDraw
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -47,7 +48,8 @@ import kotlin.collections.HashMap
|
||||
/**
|
||||
* 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(private val retrieveCipherKey : () -> Database.LoadedKey?) {
|
||||
class IconDrawableFactory(private val retrieveCipherKey : () -> Database.LoadedKey?,
|
||||
private val retrieveCustomIconBinary : (iconId: UUID) -> BinaryFile?) {
|
||||
|
||||
/** customIconMap
|
||||
* Cache for icon drawable.
|
||||
@@ -66,8 +68,9 @@ class IconDrawableFactory(private val retrieveCipherKey : () -> Database.LoadedK
|
||||
*/
|
||||
private fun getIconSuperDrawable(context: Context, iconDraw: IconImageDraw, width: Int, tintColor: Int = Color.WHITE): SuperDrawable {
|
||||
val icon = iconDraw.getIconImageToDraw()
|
||||
if (icon.custom.dataExists) {
|
||||
getIconDrawable(context.resources, icon.custom)?.let {
|
||||
val customIconBinary = retrieveCustomIconBinary(icon.custom.uuid)
|
||||
if (customIconBinary != null && customIconBinary.dataExists()) {
|
||||
getIconDrawable(context.resources, icon.custom, customIconBinary)?.let {
|
||||
return SuperDrawable(it)
|
||||
}
|
||||
}
|
||||
@@ -82,13 +85,13 @@ class IconDrawableFactory(private val retrieveCipherKey : () -> Database.LoadedK
|
||||
/**
|
||||
* Build a custom [Drawable] from custom [icon]
|
||||
*/
|
||||
private fun getIconDrawable(resources: Resources, icon: IconImageCustom): Drawable? {
|
||||
private fun getIconDrawable(resources: Resources, icon: IconImageCustom, iconCustomBinary: BinaryFile?): Drawable? {
|
||||
val patternIcon = PatternIcon(resources)
|
||||
val cipherKey = retrieveCipherKey.invoke()
|
||||
val cipherKey = retrieveCipherKey()
|
||||
if (cipherKey != null) {
|
||||
val draw: Drawable? = customIconMap[icon.uuid]?.get()
|
||||
if (draw == null) {
|
||||
icon.binaryFile?.let { binaryFile ->
|
||||
iconCustomBinary?.let { binaryFile ->
|
||||
try {
|
||||
var bitmap: Bitmap? = BitmapFactory.decodeStream(binaryFile.getInputDataStream(cipherKey))
|
||||
bitmap?.let { bitmapIcon ->
|
||||
@@ -211,9 +214,11 @@ class IconDrawableFactory(private val retrieveCipherKey : () -> Database.LoadedK
|
||||
*/
|
||||
private fun addToCustomCache(resources: Resources, iconDraw: IconImageDraw) {
|
||||
val icon = iconDraw.getIconImageToDraw()
|
||||
if (icon.custom.dataExists
|
||||
val customIconBinary = retrieveCustomIconBinary(icon.custom.uuid)
|
||||
if (customIconBinary != null
|
||||
&& customIconBinary.dataExists()
|
||||
&& !customIconMap.containsKey(icon.custom.uuid))
|
||||
getIconDrawable(resources, icon.custom)
|
||||
getIconDrawable(resources, icon.custom, customIconBinary)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user