Skip to content

Instantly share code, notes, and snippets.

@talenguyen
Created July 7, 2017 07:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save talenguyen/4c895576bf510d6c3ea0d16356871f74 to your computer and use it in GitHub Desktop.
Save talenguyen/4c895576bf510d6c3ea0d16356871f74 to your computer and use it in GitHub Desktop.
Bundle binding inspired by KotterKnife
import android.app.Activity
import android.app.Fragment
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.support.annotation.VisibleForTesting
import android.support.v4.app.FragmentActivity
import android.util.SparseArray
import java.util.*
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
import android.support.v4.app.Fragment as SupportFragment
/**
* Created by Giang Nguyen on 7/5/17.
*/
inline fun <reified T : Activity> Context.intentOf(vararg extras: Pair<String, Any>): Intent {
val intent = Intent(this, T::class.java)
if (extras.isNotEmpty()) {
val bundle = bundle(*extras)
intent.putExtras(bundle)
}
return intent
}
fun bundle(vararg extras: Pair<String, Any>): Bundle {
val bundle = Bundle()
for ((key, value) in extras) {
bundle.put(key, value)
}
return bundle
}
@VisibleForTesting internal fun Bundle.put(key: String, value: Any) {
when (value) {
is Boolean -> putBoolean(key, value)
is BooleanArray -> putBooleanArray(key, value)
is Int -> putInt(key, value)
is IntArray -> putIntArray(key, value)
is Long -> putLong(key, value)
is LongArray -> putLongArray(key, value)
is Float -> putFloat(key, value)
is FloatArray -> putFloatArray(key, value)
is Double -> putDouble(key, value)
is DoubleArray -> putDoubleArray(key, value)
is String -> putString(key, value)
is CharSequence -> putCharSequence(key, value)
is Parcelable -> putParcelable(key, value)
is Array<*> -> putArray(key, value)
is ArrayList<*> -> putArrayList(key, value)
is SparseArray<*> -> @Suppress("UNCHECKED_CAST") putSparseParcelableArray(
key,
value as SparseArray<out Parcelable>?)
else -> notSupportForType(value)
}
}
private fun notSupportForType(value: Any) {
throw IllegalArgumentException("not support for ${value::class}")
}
fun Bundle.putArrayList(key: String, value: ArrayList<*>) {
if (value.isEmpty()) {
return
}
@Suppress("UNCHECKED_CAST")
when (value[0]) {
is Int -> putIntegerArrayList(key, value as ArrayList<Int>?)
is String -> putStringArrayList(key, value as ArrayList<String>?)
is CharSequence -> putCharSequenceArrayList(key, value as ArrayList<CharSequence>?)
is Parcelable -> putParcelableArrayList(key, value as ArrayList<Parcelable>?)
else -> notSupportForType(value)
}
}
private fun Bundle.putArray(key: String, value: Array<*>) {
if (value.isEmpty()) {
return
}
@Suppress("UNCHECKED_CAST")
when (value[0]) {
is String -> putStringArray(key, value as Array<out String>?)
is CharSequence -> putCharSequenceArray(key, value as Array<out String>?)
is Parcelable -> putParcelableArray(key, value as Array<out Parcelable>?)
}
}
fun <V> Activity.extra(key: String):
ReadOnlyProperty<Activity, V> = required(key, extraFinder)
fun <V> FragmentActivity.extra(key: String):
ReadOnlyProperty<FragmentActivity, V> = required(key, extraFinder)
fun <V> Fragment.arg(key: String):
ReadOnlyProperty<Fragment, V> = required(key, extraFinder)
fun <V> SupportFragment.arg(key: String):
ReadOnlyProperty<Fragment, V> = required(key, extraFinder)
private val Activity.extraFinder: Activity.(String) -> Any?
get() = { intent.extras.get(it) }
private val Fragment.extraFinder: Fragment.(String) -> Any?
get() = { arguments.get(it) }
private val SupportFragment.extraFinder: Fragment.(String) -> Any?
get() = { arguments.get(it) }
private fun keyNotFound(key: String, desc: KProperty<*>): Nothing =
throw IllegalStateException("Key $key for '${desc.name}' not found.")
@Suppress("UNCHECKED_CAST")
private fun <T, V> required(id: String, finder: T.(String) -> Any?)
= Lazy { t: T, desc -> t.finder(id) as V? ?: keyNotFound(id, desc) }
// Like Kotlin's lazy delegate but the initializer gets the target and metadata passed to it
private class Lazy<in T, out V>(
private val initializer: (T, KProperty<*>) -> V) : ReadOnlyProperty<T, V> {
private object EMPTY
private var value: Any? = EMPTY
override fun getValue(thisRef: T, property: KProperty<*>): V {
if (value == EMPTY) {
value = initializer(thisRef, property)
}
@Suppress("UNCHECKED_CAST")
return value as V
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment