Skip to content

Instantly share code, notes, and snippets.

@mhrohani1385
Last active July 26, 2023 10:19
Show Gist options
  • Save mhrohani1385/2c6bdd3c2ba899ec67792b9b4a87aa77 to your computer and use it in GitHub Desktop.
Save mhrohani1385/2c6bdd3c2ba899ec67792b9b4a87aa77 to your computer and use it in GitHub Desktop.
rememberSavable alternative wiht a useful feature for saving in Shared Preferences from a Composable scope. Eesy to use (like usage.kt) !
class ShardPreferencesHandler<T>(
private val `class`: Class<T>,
context: Context,
private val initValue : T?,
private val key: String? = null,
private val addNameToKey: Boolean = false
) {
enum class Type(val theClass: KClass<*>) {
Float(kotlin.Float::class),
String(kotlin.String::class),
Long(kotlin.Long::class),
Int(kotlin.Int::class),
Boolean(kotlin.Boolean::class)
}
private var type: Type? = null
init {
kotlin.run {
for (typeFor in Type.values()) {
Log.d("CheckType", typeFor.name + " :: " + typeFor.theClass)
if (checkType(typeFor.theClass)) {
type = typeFor
}
}
if (type == null) {
throw Exception("Type : (${`class`}) isn't supported with preferences. Only (String, Float, Long, Int, Boolean) are supported types")
}
}
}
private lateinit var calculatedKey: String
private val sharedPreferences by lazy {
context.applicationContext.getSharedPreferences("MAIN", Context.MODE_PRIVATE)
}
@Suppress("UNCHECKED_CAST")
private fun getMainValue(key: String?): T? {
if (!::calculatedKey.isInitialized && key != null) {
this.calculatedKey = key
}
return when (type!!) {
Type.String -> sharedPreferences.getString(calculatedKey, initValue as String?)
Type.Long -> sharedPreferences.getLong(
calculatedKey,
initValue?.let { it as Long } ?: Long.MIN_VALUE)
Type.Float -> sharedPreferences.getFloat(
calculatedKey,
initValue?.let { it as Float } ?: Float.MIN_VALUE)
Type.Int -> sharedPreferences.getInt(
calculatedKey,
initValue?.let { it as Int } ?: Int.MIN_VALUE)
Type.Boolean -> sharedPreferences.getBoolean(
calculatedKey,
initValue?.let { it as Boolean } ?: false)
} as T?
}
private fun checkType(t: KClass<*>): Boolean =
`class` == t.javaObjectType
private fun setMainValue(key: String?, value: T?) {
if (!::calculatedKey.isInitialized && key != null) {
this.calculatedKey = key
}
sharedPreferences.edit {
if (value == null) {
remove(calculatedKey)
} else {
when (type) {
Type.String -> putString(calculatedKey, value as String)
Type.Long -> putLong(calculatedKey, value as Long)
Type.Float -> putFloat(calculatedKey, value as Float)
Type.Int -> putInt(calculatedKey, value as Int)
Type.Boolean -> putBoolean(calculatedKey, value as Boolean)
else -> {}
}
}
apply()
}
}
// Delegates
operator fun getValue(
thisRef: T?,
property: KProperty<*>
): T? {
return this.getMainValue(getKey(key, property.name, addNameToKey))
}
// Delegates
operator fun setValue(
thisRef: T?,
property: KProperty<*>,
value: T?
) {
this.setMainValue(getKey(key, property.name, addNameToKey), value)
}
private fun getKey(key: String?, propertyName: String, addNameToKey: Boolean) =
if (key != null) {
if (addNameToKey) {
key + "_" + propertyName
} else {
key
}
} else {
propertyName
}
companion object {
inline operator fun <reified T : Any> invoke(
context: Context,
initValue: T?,
key: String? = null,
addNameToKey: Boolean = false
) =
ShardPreferencesHandler(
T::class.java.kotlin.javaObjectType,
context,
initValue,
key,
addNameToKey
)
}
}
inline fun <reified T : Any> Context.rememberPreference(
initialValue: T,
key: String,
addNameToKey: Boolean = false
): Defs.ShardPreferencesHandler<T> = Defs.ShardPreferencesHandler(context = this, initialValue, key, addNameToKey)
@Deprecated(
"It makes conflate for two variable with same name. if you are using this make variable names different",
replaceWith = ReplaceWith("Context.rememberPreference(initialValue: T? = null, key: String, addNameToKey : Boolean = false)")
)
@Composable
inline fun <reified T : Any> Context.rememberPreference(
initialValue: T
): Defs.ShardPreferencesHandler<T> = Defs.ShardPreferencesHandler(context = this, initialValue)
/**
* T must be primitive
*
*/
@Composable
inline fun <reified T: Any,reified O : Any> rememberPreferenceAsState(
initialValue: O,
key: String,
crossinline convert: @DisallowComposableCalls (valueToConvert: T) -> O,
crossinline reverseConvert: @DisallowComposableCalls (valueToConvert: O) -> T
) : MutableState<O> {
val context = LocalContext.current
val preferencesHandler = remember {
context.rememberPreference(
key = key,
initialValue = reverseConvert(initialValue)
)
}
var valueOfPreference by preferencesHandler
val valueState = remember {
mutableStateOf(convert(valueOfPreference))
}
val updatedValue by valueState
// NOT LaunchedEffect(key1 = valuePreference)
LaunchedEffect(key1 = updatedValue, block = {
valueOfPreference = reverseConvert(updatedValue)
})
return valueState
}
@Composable
inline fun <reified T: Any> rememberPreferenceAsState(
initialValue: T,
key: String
) : MutableState<T> = rememberPreferenceAsState(initialValue = initialValue, key = key, convert = { it }, reverseConvert = { it })
@Composable
fun Content() {
var isTourchEnabled by rememberPreferenceAsState(true) // rememberPreferenceAsState(true, key = "IS_TOURCH_ENABLED") // Returns Mutable State
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment