mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
fix: Better icon builder implementation, add ContextualDatabase
This commit is contained in:
@@ -34,6 +34,7 @@ import com.kunzisoft.keepass.database.element.database.DatabaseKDB
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.NUMBER_STANDARD_ICONS
|
||||
import com.kunzisoft.keepass.database.element.icon.IconsManager
|
||||
import com.kunzisoft.keepass.database.element.node.NodeHandler
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
@@ -53,8 +54,6 @@ import com.kunzisoft.keepass.database.merge.DatabaseKDBXMerger
|
||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||
import com.kunzisoft.keepass.database.search.SearchParameters
|
||||
import com.kunzisoft.keepass.hardware.HardwareKey
|
||||
import com.kunzisoft.keepass.icons.IconDrawableFactory
|
||||
import com.kunzisoft.keepass.icons.InterfaceIconPackChooser
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
import com.kunzisoft.keepass.utils.*
|
||||
import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream
|
||||
@@ -63,7 +62,7 @@ import java.io.*
|
||||
import java.util.*
|
||||
|
||||
|
||||
class Database(private val iconPackChooser: InterfaceIconPackChooser) {
|
||||
open class Database {
|
||||
|
||||
// To keep a reference for specific methods provided by version
|
||||
private var mDatabaseKDB: DatabaseKDB? = null
|
||||
@@ -76,12 +75,6 @@ class Database(private val iconPackChooser: InterfaceIconPackChooser) {
|
||||
|
||||
var isReadOnly = false
|
||||
|
||||
val iconDrawableFactory = IconDrawableFactory(
|
||||
iconPackChooser = iconPackChooser,
|
||||
retrieveBinaryCache = { binaryCache },
|
||||
retrieveCustomIconBinary = { iconId -> iconsManager.getBinaryForCustomIcon(iconId) }
|
||||
)
|
||||
|
||||
var loaded = false
|
||||
set(value) {
|
||||
field = value
|
||||
@@ -113,7 +106,8 @@ class Database(private val iconPackChooser: InterfaceIconPackChooser) {
|
||||
|
||||
private val iconsManager: IconsManager
|
||||
get() {
|
||||
return mDatabaseKDB?.iconsManager ?: mDatabaseKDBX?.iconsManager ?: IconsManager()
|
||||
return mDatabaseKDB?.iconsManager ?: mDatabaseKDBX?.iconsManager
|
||||
?: IconsManager(NUMBER_STANDARD_ICONS)
|
||||
}
|
||||
|
||||
fun doForEachStandardIcons(action: (IconImageStandard) -> Unit) {
|
||||
@@ -143,8 +137,11 @@ class Database(private val iconPackChooser: InterfaceIconPackChooser) {
|
||||
return mDatabaseKDBX?.isCustomIconBinaryDuplicate(binaryData) ?: false
|
||||
}
|
||||
|
||||
fun removeCustomIcon(customIcon: IconImageCustom) {
|
||||
iconDrawableFactory.clearFromCache(customIcon)
|
||||
fun getBinaryForCustomIcon(iconId: UUID): BinaryData? {
|
||||
return iconsManager.getBinaryForCustomIcon(iconId)
|
||||
}
|
||||
|
||||
open fun removeCustomIcon(customIcon: IconImageCustom) {
|
||||
iconsManager.removeCustomIcon(customIcon.uuid, binaryCache)
|
||||
mDatabaseKDBX?.addDeletedObject(customIcon.uuid)
|
||||
}
|
||||
@@ -649,7 +646,7 @@ class Database(private val iconPackChooser: InterfaceIconPackChooser) {
|
||||
}
|
||||
|
||||
// New database instance to get new changes
|
||||
val databaseToMerge = Database(iconPackChooser)
|
||||
val databaseToMerge = Database()
|
||||
databaseToMerge.fileUri = databaseToMergeUri ?: this.fileUri
|
||||
|
||||
try {
|
||||
@@ -990,7 +987,7 @@ class Database(private val iconPackChooser: InterfaceIconPackChooser) {
|
||||
dataModifiedSinceLastLoading = true
|
||||
}
|
||||
|
||||
fun clearIndexesAndBinaries(filesDirectory: File? = null) {
|
||||
open fun clearIndexesAndBinaries(filesDirectory: File?) {
|
||||
this.mDatabaseKDB?.clearIndexes()
|
||||
this.mDatabaseKDBX?.clearIndexes()
|
||||
|
||||
@@ -1003,8 +1000,6 @@ class Database(private val iconPackChooser: InterfaceIconPackChooser) {
|
||||
this.mDatabaseKDB?.clearBinaries()
|
||||
this.mDatabaseKDBX?.clearBinaries()
|
||||
|
||||
iconDrawableFactory.clearCache()
|
||||
|
||||
// delete all the files in the temp dir if allowed
|
||||
try {
|
||||
filesDirectory?.let { directory ->
|
||||
@@ -1390,7 +1385,6 @@ class Database(private val iconPackChooser: InterfaceIconPackChooser) {
|
||||
}
|
||||
|
||||
companion object : SingletonHolder<Database>(::Database) {
|
||||
|
||||
private val TAG = Database::class.java.name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.element
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||
|
||||
@@ -29,9 +29,9 @@ enum class SortNodeEnum {
|
||||
DB, TITLE, USERNAME, CREATION_TIME, LAST_MODIFY_TIME, LAST_ACCESS_TIME;
|
||||
|
||||
fun <G: GroupVersionedInterface<G, *>> getNodeComparator(
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters)
|
||||
: Comparator<NodeVersionedInterface<G>> {
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters
|
||||
) : Comparator<NodeVersionedInterface<G>> {
|
||||
return when (this) {
|
||||
DB -> NodeNaturalComparator(database, sortNodeParameters) // Force false because natural order contains recycle bin
|
||||
TITLE -> NodeTitleComparator(database, sortNodeParameters)
|
||||
@@ -110,9 +110,9 @@ enum class SortNodeEnum {
|
||||
* Comparator of node by natural database placement
|
||||
*/
|
||||
class NodeNaturalComparator<G: GroupVersionedInterface<*, *>, T: NodeVersionedInterface<G>>(
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters)
|
||||
: NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters
|
||||
) : NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
|
||||
override fun compareBySpecificOrder(object1: T, object2: T): Int {
|
||||
return object1.nodeIndexInParentForNaturalOrder()
|
||||
@@ -124,9 +124,9 @@ enum class SortNodeEnum {
|
||||
* Comparator of Node by Title
|
||||
*/
|
||||
class NodeTitleComparator<G: GroupVersionedInterface<*, *>, T: NodeVersionedInterface<G>>(
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters)
|
||||
: NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters
|
||||
) : NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
|
||||
override fun compareBySpecificOrder(object1: T, object2: T): Int {
|
||||
val titleCompare = object1.title.compareTo(object2.title, ignoreCase = true)
|
||||
@@ -142,9 +142,9 @@ enum class SortNodeEnum {
|
||||
* Comparator of Node by Username, Groups by title
|
||||
*/
|
||||
class NodeUsernameComparator<G: GroupVersionedInterface<*, *>, T: NodeVersionedInterface<G>>(
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters)
|
||||
: NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters
|
||||
) : NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
|
||||
override fun compareBySpecificOrder(object1: T, object2: T): Int {
|
||||
return if (object1.type == Type.ENTRY && object2.type == Type.ENTRY) {
|
||||
@@ -168,9 +168,9 @@ enum class SortNodeEnum {
|
||||
* Comparator of node by creation
|
||||
*/
|
||||
class NodeCreationComparator<G: GroupVersionedInterface<*, *>, T: NodeVersionedInterface<G>>(
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters)
|
||||
: NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters
|
||||
) : NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
|
||||
override fun compareBySpecificOrder(object1: T, object2: T): Int {
|
||||
val creationCompare = object1.creationTime.date
|
||||
@@ -187,9 +187,9 @@ enum class SortNodeEnum {
|
||||
* Comparator of node by last modification
|
||||
*/
|
||||
class NodeLastModificationComparator<G: GroupVersionedInterface<*, *>, T: NodeVersionedInterface<G>>(
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters)
|
||||
: NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters
|
||||
) : NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
|
||||
override fun compareBySpecificOrder(object1: T, object2: T): Int {
|
||||
val lastModificationCompare = object1.lastModificationTime.date
|
||||
@@ -206,9 +206,9 @@ enum class SortNodeEnum {
|
||||
* Comparator of node by last access
|
||||
*/
|
||||
class NodeLastAccessComparator<G: GroupVersionedInterface<*, *>, T: NodeVersionedInterface<G>>(
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters)
|
||||
: NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
database: Database,
|
||||
sortNodeParameters: SortNodeParameters
|
||||
) : NodeComparator<G, T>(database, sortNodeParameters) {
|
||||
|
||||
override fun compareBySpecificOrder(object1: T, object2: T): Int {
|
||||
val lastAccessCompare = object1.lastAccessTime.date
|
||||
|
||||
@@ -67,7 +67,8 @@ abstract class DatabaseVersioned<
|
||||
* Can be used to temporarily store database elements
|
||||
*/
|
||||
var binaryCache = BinaryCache()
|
||||
var iconsManager = IconsManager()
|
||||
// For now, same number of icons for each database version
|
||||
var iconsManager = IconsManager(IconImageStandard.NUMBER_STANDARD_ICONS)
|
||||
var attachmentPool = AttachmentPool()
|
||||
|
||||
var changeDuplicateId = false
|
||||
|
||||
@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.database.element.icon
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.kunzisoft.keepass.icons.IconPack.Companion.NB_ICONS
|
||||
|
||||
class IconImageStandard : IconImageDraw {
|
||||
|
||||
@@ -74,6 +73,8 @@ class IconImageStandard : IconImageDraw {
|
||||
|
||||
companion object {
|
||||
|
||||
const val NUMBER_STANDARD_ICONS = 69
|
||||
|
||||
const val KEY_ID = 0
|
||||
const val ID_CARD_ID = 9
|
||||
const val WIRELESS_ID = 12
|
||||
@@ -88,7 +89,7 @@ class IconImageStandard : IconImageDraw {
|
||||
const val DOLLAR_ID = 66
|
||||
|
||||
fun isCorrectIconId(iconId: Int): Boolean {
|
||||
return iconId in 0 until NB_ICONS
|
||||
return iconId in 0 until NUMBER_STANDARD_ICONS
|
||||
}
|
||||
|
||||
@JvmField
|
||||
|
||||
@@ -25,12 +25,11 @@ import com.kunzisoft.keepass.database.element.binary.BinaryCache
|
||||
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
||||
import com.kunzisoft.keepass.database.element.binary.CustomIconPool
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard.Companion.KEY_ID
|
||||
import com.kunzisoft.keepass.icons.IconPack.Companion.NB_ICONS
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
|
||||
class IconsManager {
|
||||
class IconsManager(numberOfIcons: Int) {
|
||||
|
||||
private val standardCache = List(NB_ICONS) {
|
||||
private val standardCache = List(numberOfIcons) {
|
||||
IconImageStandard(it)
|
||||
}
|
||||
private val customCache = CustomIconPool()
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.icons
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Resources
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.Log
|
||||
import android.widget.ImageView
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.widget.ImageViewCompat
|
||||
import com.kunzisoft.keepass.database.element.binary.BinaryCache
|
||||
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageDraw
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* 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 iconPackChooser: InterfaceIconPackChooser,
|
||||
private val retrieveBinaryCache: () -> BinaryCache?,
|
||||
private val retrieveCustomIconBinary: (iconId: UUID) -> BinaryData?,
|
||||
) {
|
||||
|
||||
/** customIconMap
|
||||
* Cache for icon drawable.
|
||||
* Keys: UUID, Values: Drawables
|
||||
*/
|
||||
private val customIconMap = HashMap<UUID, WeakReference<Drawable>>()
|
||||
|
||||
/** standardIconMap
|
||||
* Cache for icon drawable.
|
||||
* Keys: Integer, Values: Drawables
|
||||
*/
|
||||
private val standardIconMap = HashMap<CacheKey, WeakReference<Drawable>>()
|
||||
|
||||
/**
|
||||
* To load an icon pack only if current one is different
|
||||
*/
|
||||
private var mCurrentIconPack: IconPack? = null
|
||||
|
||||
/**
|
||||
* Get the [SuperDrawable] [iconDraw] (from cache, or build it and add it to the cache if not exists yet), then tint it with [tintColor] if needed
|
||||
*/
|
||||
private fun getIconSuperDrawable(
|
||||
context: Context,
|
||||
iconDraw: IconImageDraw,
|
||||
width: Int,
|
||||
tintColor: Int = Color.WHITE,
|
||||
): SuperDrawable {
|
||||
val icon = iconDraw.getIconImageToDraw()
|
||||
val customIconBinary = retrieveCustomIconBinary(icon.custom.uuid)
|
||||
val binaryCache = retrieveBinaryCache()
|
||||
if (binaryCache != null && customIconBinary != null && customIconBinary.dataExists()) {
|
||||
getIconDrawable(context.resources, icon.custom, customIconBinary)?.let {
|
||||
return SuperDrawable(it)
|
||||
}
|
||||
}
|
||||
val iconPack = iconPackChooser.getSelectedIconPack(context)
|
||||
if (mCurrentIconPack != iconPack) {
|
||||
this.mCurrentIconPack = iconPack
|
||||
this.clearCache()
|
||||
}
|
||||
iconPack?.iconToResId(icon.standard.id)?.let { iconId ->
|
||||
return SuperDrawable(getIconDrawable(context.resources, iconId, width, tintColor),
|
||||
iconPack.tintable())
|
||||
} ?: run {
|
||||
return SuperDrawable(PatternIcon(iconPackChooser.getDefaultIconSize()).blankDrawable)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a custom [Drawable] from custom [icon]
|
||||
*/
|
||||
private fun getIconDrawable(
|
||||
resources: Resources,
|
||||
icon: IconImageCustom,
|
||||
iconCustomBinary: BinaryData?,
|
||||
): Drawable? {
|
||||
val patternIcon = PatternIcon(iconPackChooser.getDefaultIconSize())
|
||||
val binaryManager = retrieveBinaryCache()
|
||||
if (binaryManager != null) {
|
||||
val draw: Drawable? = customIconMap[icon.uuid]?.get()
|
||||
if (draw == null) {
|
||||
iconCustomBinary?.let { binaryFile ->
|
||||
try {
|
||||
var bitmap: Bitmap? =
|
||||
BitmapFactory.decodeStream(binaryFile.getInputDataStream(binaryManager))
|
||||
bitmap?.let { bitmapIcon ->
|
||||
bitmap = resize(bitmapIcon, patternIcon)
|
||||
val createdDraw = BitmapDrawable(resources, bitmap)
|
||||
customIconMap[icon.uuid] = WeakReference(createdDraw)
|
||||
return createdDraw
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
customIconMap.remove(icon.uuid)
|
||||
Log.e(TAG, "Unable to create the bitmap icon", e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return draw
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the standard [Drawable] icon from [iconId] (cache or build it and add it to the cache if not exists yet)
|
||||
* , then tint it with [tintColor] if needed
|
||||
*/
|
||||
private fun getIconDrawable(
|
||||
resources: Resources,
|
||||
iconId: Int,
|
||||
width: Int,
|
||||
tintColor: Int,
|
||||
): Drawable {
|
||||
val newCacheKey = CacheKey(iconId, width, true, tintColor)
|
||||
|
||||
var draw: Drawable? = standardIconMap[newCacheKey]?.get()
|
||||
if (draw == null) {
|
||||
try {
|
||||
draw = ResourcesCompat.getDrawable(resources, iconId, null)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Can't get icon", e)
|
||||
}
|
||||
|
||||
if (draw != null) {
|
||||
standardIconMap[newCacheKey] = WeakReference(draw)
|
||||
}
|
||||
}
|
||||
|
||||
if (draw == null) {
|
||||
draw = PatternIcon(iconPackChooser.getDefaultIconSize()).blankDrawable
|
||||
}
|
||||
draw.isFilterBitmap = false
|
||||
|
||||
return draw
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize the custom icon to match the built in icons
|
||||
*
|
||||
* @param bitmap Bitmap to resize
|
||||
* @return Bitmap resized
|
||||
*/
|
||||
private fun resize(bitmap: Bitmap, dimensionPattern: PatternIcon): Bitmap {
|
||||
val width = bitmap.width
|
||||
val height = bitmap.height
|
||||
|
||||
return if (width == dimensionPattern.width && height == dimensionPattern.height) {
|
||||
bitmap
|
||||
} else Bitmap.createScaledBitmap(bitmap,
|
||||
dimensionPattern.width,
|
||||
dimensionPattern.height,
|
||||
true)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a database [icon] to an ImageView and tint it with [tintColor] if needed
|
||||
*/
|
||||
fun assignDatabaseIcon(
|
||||
imageView: ImageView,
|
||||
icon: IconImageDraw,
|
||||
tintColor: Int = Color.WHITE,
|
||||
) {
|
||||
try {
|
||||
val context = imageView.context
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
addToCustomCache(context.resources, icon)
|
||||
withContext(Dispatchers.Main) {
|
||||
val superDrawable = getIconSuperDrawable(context,
|
||||
icon,
|
||||
imageView.width,
|
||||
tintColor)
|
||||
imageView.setImageDrawable(superDrawable.drawable)
|
||||
if (superDrawable.tintable) {
|
||||
ImageViewCompat.setImageTintList(imageView,
|
||||
ColorStateList.valueOf(tintColor))
|
||||
} else {
|
||||
ImageViewCompat.setImageTintList(imageView, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(ImageView::class.java.name, "Unable to assign icon in image view", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a bitmap from a database [icon]
|
||||
*/
|
||||
fun getBitmapFromIcon(
|
||||
context: Context,
|
||||
icon: IconImageDraw,
|
||||
tintColor: Int = Color.BLACK,
|
||||
): Bitmap? {
|
||||
try {
|
||||
val superDrawable = getIconSuperDrawable(context,
|
||||
icon,
|
||||
24,
|
||||
tintColor)
|
||||
val bitmap = superDrawable.drawable.toBitmap()
|
||||
// Tint bitmap if it's not a custom icon
|
||||
if (superDrawable.tintable && bitmap.isMutable) {
|
||||
Canvas(bitmap).drawBitmap(bitmap, 0.0F, 0.0F, Paint().apply {
|
||||
colorFilter = PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN)
|
||||
})
|
||||
}
|
||||
return bitmap
|
||||
} catch (e: Exception) {
|
||||
Log.e(RemoteViews::class.java.name, "Unable to create bitmap from icon", e)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple method to init the cache with the custom icon and be much faster next time
|
||||
*/
|
||||
private fun addToCustomCache(resources: Resources, iconDraw: IconImageDraw) {
|
||||
val icon = iconDraw.getIconImageToDraw()
|
||||
val customIconBinary = retrieveCustomIconBinary(icon.custom.uuid)
|
||||
if (customIconBinary != null
|
||||
&& customIconBinary.dataExists()
|
||||
&& !customIconMap.containsKey(icon.custom.uuid)
|
||||
)
|
||||
getIconDrawable(resources, icon.custom, customIconBinary)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a specific icon from the cache
|
||||
*/
|
||||
fun clearFromCache(icon: IconImageCustom) {
|
||||
customIconMap.remove(icon.uuid)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache of icons
|
||||
*/
|
||||
fun clearCache() {
|
||||
standardIconMap.clear()
|
||||
customIconMap.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a blankDrawable drawable
|
||||
* @param res Resource to build the drawable
|
||||
*/
|
||||
private class PatternIcon(defaultIconSize : Int) {
|
||||
|
||||
var blankDrawable: Drawable = ColorDrawable(Color.TRANSPARENT)
|
||||
var width = -1
|
||||
var height = -1
|
||||
|
||||
init {
|
||||
width = defaultIconSize
|
||||
height = defaultIconSize
|
||||
blankDrawable.setBounds(0, 0, width, height)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to prevent a custom icon to be tint
|
||||
*/
|
||||
class SuperDrawable(var drawable: Drawable, var tintable: Boolean = false)
|
||||
|
||||
/**
|
||||
* Key class to retrieve a Drawable in the cache if it's tinted or not
|
||||
*/
|
||||
private inner class CacheKey(
|
||||
var resId: Int,
|
||||
var density: Int,
|
||||
var isTint: Boolean,
|
||||
var color: Int,
|
||||
) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || javaClass != other.javaClass) return false
|
||||
val cacheKey = other as CacheKey
|
||||
return if (isTint)
|
||||
resId == cacheKey.resId
|
||||
&& density == cacheKey.density
|
||||
&& cacheKey.isTint
|
||||
&& color == cacheKey.color
|
||||
else
|
||||
resId == cacheKey.resId
|
||||
&& density == cacheKey.density
|
||||
&& !cacheKey.isTint
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = resId
|
||||
result = 31 * result + density
|
||||
result = 31 * result + isTint.hashCode()
|
||||
result = 31 * result + color
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val TAG = IconDrawableFactory::class.java.name
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePassDX.
|
||||
*
|
||||
* KeePassDX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePassDX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.icons
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.util.SparseIntArray
|
||||
import com.kunzisoft.keepass.database.R
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Class who construct dynamically database icons contains in a separate library
|
||||
*
|
||||
*
|
||||
* It only supports icons with specific nomenclature **[stringId]_[%2d]_32dp**
|
||||
* where [stringId] contains in a string xml attribute with id **resource_id** and
|
||||
* [%2d] 2 numerical numbers between 00 and 68 included,
|
||||
*
|
||||
*
|
||||
* See *icon-pack-classic* module as sample
|
||||
*
|
||||
* Construct dynamically the icon pack provide by the string resource id
|
||||
*
|
||||
* @param packageName Context of the app to retrieve the resources
|
||||
* @param packageName Context of the app to retrieve the resources
|
||||
* @param resourceId String Id of the pack (ex : com.kunzisoft.keepass.icon.classic.R.string.resource_id)
|
||||
*/
|
||||
class IconPack(packageName: String, resources: Resources, resourceId: Int) {
|
||||
|
||||
private val icons: SparseIntArray = SparseIntArray()
|
||||
|
||||
/**
|
||||
* Get the id of the IconPack
|
||||
*
|
||||
* @return String id of the pack
|
||||
*/
|
||||
val id: String?
|
||||
|
||||
/**
|
||||
* Get the name of the IconPack
|
||||
*
|
||||
* @return String visual name of the pack
|
||||
*/
|
||||
val name: String
|
||||
|
||||
private val tintable: Boolean
|
||||
|
||||
/**
|
||||
* @return int Get the default icon resource id
|
||||
*/
|
||||
val defaultIconId: Int
|
||||
get() = iconToResId(0)
|
||||
|
||||
init {
|
||||
|
||||
id = resources.getString(resourceId).removeSuffix("_")
|
||||
|
||||
// Build the list of icons
|
||||
var num = 0
|
||||
while (num < NB_ICONS) {
|
||||
// To construct the id with name_ic_XX_32dp (ex : classic_ic_08_32dp )
|
||||
val resId = resources.getIdentifier(
|
||||
id + "_" + String.format(Locale.ENGLISH, "%02d", num) + "_32dp",
|
||||
"drawable",
|
||||
packageName)
|
||||
icons.put(num, resId)
|
||||
num++
|
||||
}
|
||||
// Get visual name
|
||||
name = resources.getString(
|
||||
resources.getIdentifier(
|
||||
id + "_" + "name",
|
||||
"string",
|
||||
packageName
|
||||
)
|
||||
)
|
||||
// If icons are tintable
|
||||
tintable = resources.getBoolean(
|
||||
resources.getIdentifier(
|
||||
id + "_" + "tintable",
|
||||
"bool",
|
||||
packageName
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if each icon in the pack can be tint
|
||||
*
|
||||
* @return true if icons are tintable
|
||||
*/
|
||||
fun tintable(): Boolean {
|
||||
return tintable
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of icons in this pack
|
||||
*
|
||||
* @return int Number of database icons
|
||||
*/
|
||||
fun numberOfIcons(): Int {
|
||||
return icons.size()
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon as a resourceId
|
||||
*
|
||||
* @param iconId Icon database Id of the icon to retrieve
|
||||
* @return int resourceId
|
||||
*/
|
||||
fun iconToResId(iconId: Int): Int {
|
||||
return icons.get(iconId, R.drawable.ic_blank_32dp)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NB_ICONS = 69
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.kunzisoft.keepass.icons
|
||||
|
||||
import android.content.Context
|
||||
|
||||
interface InterfaceIconPackChooser {
|
||||
fun build(context: Context)
|
||||
fun addDefaultIconPack(context: Context)
|
||||
fun addOrCatchNewIconPack(context: Context, iconPackString: String)
|
||||
fun setSelectedIconPack(iconPackIdString: String?)
|
||||
fun getSelectedIconPack(context: Context): IconPack?
|
||||
fun getIconPackList(context: Context): List<IconPack>
|
||||
fun setDefaultIconSize(int: Int)
|
||||
fun getDefaultIconSize(): Int
|
||||
}
|
||||
@@ -19,27 +19,11 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.utils
|
||||
|
||||
import com.kunzisoft.keepass.icons.InterfaceIconPackChooser
|
||||
|
||||
open class SingletonHolder<out T>(private val constructor: (iconPackChooser : InterfaceIconPackChooser) -> T) {
|
||||
|
||||
@Volatile
|
||||
private var instance: T? = null
|
||||
|
||||
fun getInstance(iconPackChooser: InterfaceIconPackChooser): T {
|
||||
return when {
|
||||
instance != null -> instance!!
|
||||
else -> synchronized(this) {
|
||||
if (instance == null) instance = constructor(iconPackChooser)
|
||||
instance!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class SingletonHolderParameter<out T, in A>(private val constructor: (A) -> T) {
|
||||
|
||||
@Volatile
|
||||
private var instance: T? = null
|
||||
|
||||
fun getInstance(arg: A): T {
|
||||
return when {
|
||||
instance != null -> instance!!
|
||||
@@ -50,3 +34,19 @@ open class SingletonHolderParameter<out T, in A>(private val constructor: (A) ->
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class SingletonHolder<out T>(private val constructor: () -> T) {
|
||||
|
||||
@Volatile
|
||||
private var instance: T? = null
|
||||
|
||||
fun getInstance(): T {
|
||||
return when {
|
||||
instance != null -> instance!!
|
||||
else -> synchronized(this) {
|
||||
if (instance == null) instance = constructor()
|
||||
instance!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user