Skip to content

Instantly share code, notes, and snippets.

@InsanusMokrassar
Last active December 7, 2017 03:28
Show Gist options
  • Save InsanusMokrassar/b7334ab7026cb693ff673c8df2216eff to your computer and use it in GitHub Desktop.
Save InsanusMokrassar/b7334ab7026cb693ff673c8df2216eff to your computer and use it in GitHub Desktop.
KeyValueStore for android on kotlin
import android.content.Context
import android.content.SharedPreferences
import com.github.insanusmokrassar.IObjectK.interfaces.IObject
import com.github.insanusmokrassar.IObjectK.realisations.SimpleIObject
import java.io.Serializable
private val cache = HashMap<String, MutableMap<String, KeyValueStore>>()
fun Context.keyValueStore(
name: String = getString(R.string.defaultKeyValueStore)
): IObject<Any> {
val className = this::class.java.simpleName
return if (cache[className] ?.get(name) == null) {
cache.put(
className,
mutableMapOf(
Pair(
name,
KeyValueStore(this, name)
)
)
)
keyValueStore(name)
} else {
cache[className]!![name]!!
}
}
class KeyValueStore internal constructor (
c: Context,
preferencesName: String = c.getString(R.string.defaultKeyValueStore)
) : IObject<Any>, SharedPreferences.OnSharedPreferenceChangeListener {
private val sharedPreferences = c.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
private val cachedData = SimpleIObject()
private val syncObject = Object()
init {
sharedPreferences.all.forEach {
if (it.value != null) {
cachedData.put(it.key, it.value as Any)
}
}
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onSharedPreferenceChanged(sp: SharedPreferences, key: String) {
synchronized(syncObject, {
val value = sp.all[key]
if (value != null) {
cachedData.put(key, value)
} else {
cachedData.remove(key)
}
})
}
override fun put(key: String, value: Any) {
synchronized(syncObject, {
sharedPreferences.edit()
.put(key, value)
.apply()
})
}
override fun <T: Any> get(key: String): T {
synchronized(syncObject, {
val value = cachedData.get<Any>(key)
return when(value) {
!is String -> value
else -> {
if (canBeSerializable(value)) {
try {
deserialize<Serializable>(value)
} catch (e: ClassCastException) {
value
}
} else {
value
}
}
} as T
})
}
override fun keys(): Set<String> {
synchronized(syncObject, {
return cachedData.keys()
})
}
override fun putAll(toPutMap: Map<String, Any>) {
synchronized(syncObject, {
val editor = sharedPreferences.edit()
toPutMap.forEach {
editor.put(it.key, it.value)
}
editor.apply()
})
}
override fun remove(key: String) {
synchronized(syncObject, {
sharedPreferences.edit()
.remove(key)
.apply()
})
}
private fun SharedPreferences.Editor.put(key: String, value: Any): SharedPreferences.Editor {
when(value) {
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Float -> putFloat(key, value)
is String -> putString(key, value)
is Boolean -> putBoolean(key, value)
is Set<*> -> try {
putStringSet(key, value as Set<String>)
} catch (e: ClassCastException) {
putStringSet(key, LinkedHashSet(value.map { it.toString() }))
}
is Serializable -> putString(key, serialize(value))
else -> throw IllegalArgumentException(
"You can put into SPIObject only: Int, Long, Float, String, Boolean or some set"
)
}
return this
}
}
import java.io.*
val serializableControlWord = "Serializable"
fun addKeywordToSerializable(addTo: String): String
= "$serializableControlWord$addTo"
fun removeKeywordFromSerializable(removeFrom: String): String
= removeFrom.replaceFirst(serializableControlWord, "")
fun canBeSerializable(what: String): Boolean
= what.startsWith(serializableControlWord)
fun serialize(from: Serializable): String {
val byteArrayOS = ByteArrayOutputStream()
val objectOS = ObjectOutputStream(byteArrayOS)
objectOS.writeObject(from)
objectOS.close()
return addKeywordToSerializable(String(byteArrayOS.toByteArray()))
}
fun <T: Serializable> deserialize(from: String): T {
try {
return ObjectInputStream(
ByteArrayInputStream(
removeKeywordFromSerializable(from).toByteArray()
)
).readObject() as T
} catch (e: IOException) {
throw IllegalArgumentException("Input data is not serialized string: $from", e)
} catch (e: ClassCastException) {
throw IllegalArgumentException("Input data is not target serialized class: $from", e)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment