fix: Better icon builder implementation, add ContextualDatabase

This commit is contained in:
J-Jamet
2023-05-14 13:04:49 +02:00
parent 0d2c814b3d
commit ee1a0a53a6
86 changed files with 506 additions and 424 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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