Externalize binary in custom icon

This commit is contained in:
J-Jamet
2021-03-17 15:31:12 +01:00
parent 62d4993e6d
commit 3508f47842
10 changed files with 75 additions and 68 deletions

View File

@@ -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) {

View File

@@ -32,7 +32,7 @@ class IconCustomFragment : IconFragment<IconImageCustom>() {
}
override fun defineIconList() {
mDatabase?.doForEachCustomIcons { customIcon ->
mDatabase?.doForEachCustomIcons { customIcon, _ ->
iconPickerAdapter.addIcon(customIcon, false)
}
}

View File

@@ -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) {

View File

@@ -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
*/

View File

@@ -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 {

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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)
}
/**