Last active
October 1, 2021 09:56
-
-
Save Aidanvii7/f5a4183697c153f5e8cc9c722b0bf4cc to your computer and use it in GitHub Desktop.
A Kotlin way to use Android Bundles. Offers type safety unlike android ktx's version of bundleOf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.os.Bundle | |
import android.os.Parcelable | |
import android.util.Size | |
import java.io.Serializable | |
typealias BundleContext = Bundle.() -> Unit | |
/** | |
* ``` | |
* fun example(someParcelable: Parcelable, someOtherBundle: Bundle) { | |
* val bundle = bundleOf { | |
* strings["string_key"] = "value" | |
* integers["int_key"] = 1 | |
* integers["int_key"] = null // removes key/value | |
* booleans["bool_key"] = true | |
* parcelables["parcelable_key"] = someParcelable | |
* bundles["bundle_key"] = someOtherBundle | |
* bundles.flattenWith(someOtherBundle) | |
* } | |
* } | |
* ``` | |
*/ | |
inline fun bundleOf(bundleContext: BundleContext): Bundle = | |
Bundle().apply(bundleContext) | |
inline val Bundle.strings | |
get() = BundleStrings(this) | |
inline val Bundle.integers | |
get() = BundleIntegers(this) | |
inline val Bundle.floats | |
get() = BundleFloats(this) | |
inline val Bundle.bytes | |
get() = BundleBytes(this) | |
inline val Bundle.booleans | |
get() = BundleBooleans(this) | |
inline val Bundle.parcelables | |
get() = BundleParcelables(this) | |
inline val Bundle.sizes | |
get() = BundleSizes(this) | |
inline val Bundle.bundles | |
get() = BundleBundles(this) | |
@JvmInline | |
value class BundleStrings(val bundle: Bundle) { | |
operator fun set(key: String, value: String) { | |
bundle.putString(key, value) | |
} | |
fun remove(key: String) { | |
bundle.remove(key) | |
} | |
operator fun get(key: String): String? = bundle.getString(key) | |
} | |
@JvmInline | |
value class BundleParcelables(val bundle: Bundle) { | |
operator fun set(key: String, value: Parcelable?) { | |
bundle.apply { | |
if (value != null) putParcelable(key, value) else remove(key) | |
} | |
} | |
inline fun <reified T : Parcelable> set(value: T?) { | |
this[T::class.qualifiedName!!] = value | |
} | |
inline operator fun <reified T : Parcelable> get(key: String = T::class.qualifiedName!!): T? = | |
bundle.getParcelable(key) | |
} | |
@JvmInline | |
value class BundleSerializables(val bundle: Bundle) { | |
operator fun set(key: String, value: Serializable?) { | |
bundle.apply { | |
if (value != null) putSerializable(key, value) else remove(key) | |
} | |
} | |
inline fun <reified T : Serializable> set(value: T?) { | |
this[T::class.qualifiedName!!] = value | |
} | |
inline operator fun <reified T : Serializable> get(key: String = T::class.qualifiedName!!): T? = | |
bundle.getSerializable(key) as? T | |
} | |
@JvmInline | |
value class BundleIntegers(val bundle: Bundle) { | |
operator fun set(key: String, value: Int) { | |
bundle.putInt(key, value) | |
} | |
operator fun set(key: String, value: Int?) { | |
bundle.apply { | |
if (value != null) putInt(key, value) else remove(key) | |
} | |
} | |
operator fun get(key: String): Int? = | |
if (bundle.containsKey(key)) bundle.getInt(key) else null | |
} | |
@JvmInline | |
value class BundleFloats(val bundle: Bundle) { | |
operator fun set(key: String, value: Float) { | |
bundle.putFloat(key, value) | |
} | |
operator fun set(key: String, value: Float?) { | |
bundle.apply { | |
if (value != null) putFloat(key, value) else remove(key) | |
} | |
} | |
operator fun get(key: String): Float? = | |
if (bundle.containsKey(key)) bundle.getFloat(key) else null | |
} | |
@JvmInline | |
value class BundleBytes(val bundle: Bundle) { | |
operator fun set(key: String, value: Byte) { | |
bundle.putByte(key, value) | |
} | |
operator fun set(key: String, value: Byte?) { | |
bundle.apply { | |
if (value != null) putByte(key, value) else remove(key) | |
} | |
} | |
operator fun get(key: String): Byte? = | |
if (bundle.containsKey(key)) bundle.getByte(key) else null | |
} | |
@JvmInline | |
value class BundleBooleans(val bundle: Bundle) { | |
operator fun set(key: String, value: Boolean) { | |
bundle.putBoolean(key, value) | |
} | |
operator fun set(key: String, value: Boolean?) { | |
bundle.apply { | |
if (value != null) putBoolean(key, value) else remove(key) | |
} | |
} | |
operator fun get(key: String): Boolean? = | |
if (bundle.containsKey(key)) bundle.getBoolean(key) else null | |
} | |
@JvmInline | |
value class BundleSizes(val bundle: Bundle) { | |
operator fun set(key: String, value: Size?) { | |
bundle.apply { | |
if (value != null) putSize(key, value) else remove(key) | |
} | |
} | |
operator fun get(key: String): Size? = | |
bundle.getSize(key) | |
} | |
@JvmInline | |
value class BundleBundles(val bundle: Bundle) { | |
operator fun set(key: String, value: Bundle) { | |
bundle.putBundle(key, value) | |
} | |
operator fun get(key: String): Bundle? = bundle.getBundle(key) | |
fun flattenWith(otherBundle: Bundle, recursive: Boolean = false): Bundle = | |
bundle.apply { | |
otherBundle.toMap().forEach { (key, value) -> | |
when (value) { | |
is String -> strings[key] = value | |
is Int -> integers[key] = value | |
is Float -> floats[key] = value | |
is Byte -> bytes[key] = value | |
is Boolean -> booleans[key] = value | |
is Parcelable -> parcelables[key] = value | |
is Size -> sizes[key] = value | |
is Bundle -> if (recursive) flattenWith(value) | |
} | |
} | |
} | |
} | |
fun Bundle.toMap(): Map<String, Any> = | |
keySet().mapNotNull { key -> | |
when (val value = this[key]) { | |
null -> null | |
is String -> value | |
is Int -> value | |
is Float -> value | |
is Byte -> value | |
is Boolean -> value | |
is Parcelable -> value | |
is Size -> value | |
is Bundle -> value | |
else -> null //unsupported type for now | |
}?.let { value -> key to value } | |
}.toMap() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment