Created
November 27, 2020 23:10
-
-
Save esensar/4b1d317aec0fb5b20e5a022ed0487e7e to your computer and use it in GitHub Desktop.
Kotlin extensions way to handle fragment arguments
This file contains 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 java.io.Serializable | |
import java.util.UUID | |
import kotlin.properties.ReadOnlyProperty | |
import kotlin.reflect.KProperty | |
abstract class ArgumentCompanion { | |
private val arguments: MutableMap<String, NullableArgument<*>> = hashMapOf() | |
fun <T : Serializable> ArgumentCompanion.nullableArgument( | |
key: String = UUID.randomUUID().toString(), | |
defaultValueProvider: () -> T? = { null } | |
): NullableArgumentPropertyDelegate<T> = | |
NullableArgumentPropertyDelegate(key, defaultValueProvider) | |
fun <T : Serializable> ArgumentCompanion.argument( | |
key: String = UUID.randomUUID().toString(), | |
defaultValueProvider: () -> T = { throw NullPointerException("Missing required value for argument $key") } | |
): ArgumentPropertyDelegate<T> = ArgumentPropertyDelegate(key, defaultValueProvider) | |
abstract inner class BaseArgumentPropertyDelegate<T : Serializable, | |
ARG_TYPE : NullableArgument<T>, | |
PROVIDER_TYPE : () -> T?>( | |
private val key: String, | |
private val defaultValueProvider: PROVIDER_TYPE, | |
private val argBuilder: (String, PROVIDER_TYPE) -> ARG_TYPE | |
) : ReadOnlyProperty<ArgumentCompanion, ARG_TYPE> { | |
override fun getValue(thisRef: ArgumentCompanion, property: KProperty<*>): ARG_TYPE { | |
return thisRef.arguments.getOrPut(key) { | |
argBuilder( | |
key, | |
defaultValueProvider | |
) | |
} as ARG_TYPE | |
} | |
} | |
inner class NullableArgumentPropertyDelegate<T : Serializable>( | |
key: String, | |
defaultValueProvider: () -> T? | |
) : BaseArgumentPropertyDelegate<T, NullableArgument<T>, () -> T?>( | |
key, | |
defaultValueProvider, | |
{ k, provider -> NullableArgumentImpl(k, provider) } | |
) | |
inner class ArgumentPropertyDelegate<T : Serializable>( | |
key: String, | |
defaultValueProvider: () -> T | |
) : BaseArgumentPropertyDelegate<T, Argument<T>, () -> T>( | |
key, | |
defaultValueProvider, | |
{ k, provider -> ArgumentImpl(k, provider) } | |
) | |
} |
This file contains 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 androidx.fragment.app.Fragment | |
import java.io.Serializable | |
import kotlin.properties.ReadOnlyProperty | |
import kotlin.reflect.KProperty | |
interface NullableArgument<T : Serializable> : ReadOnlyProperty<Fragment, T?> { | |
val key: String | |
fun getValue(fragRef: Fragment): T? | |
operator fun invoke(value: T): Pair<String, T> | |
override fun getValue(thisRef: Fragment, property: KProperty<*>): T? { | |
return getValue(thisRef) | |
} | |
} | |
interface Argument<T : Serializable> : NullableArgument<T> { | |
override fun getValue(thisRef: Fragment, property: KProperty<*>): T { | |
return getValue(thisRef)!! | |
} | |
} | |
internal open class NullableArgumentImpl<T : Serializable>( | |
override val key: String, | |
private val defaultValueProvider: () -> T? | |
) : NullableArgument<T> { | |
private var cachedValue: T? = null | |
override operator fun invoke(value: T): Pair<String, T> = key to value | |
override fun getValue(fragRef: Fragment): T? { | |
if (cachedValue == null) { | |
cachedValue = fragRef.getArgument(key, defaultValueProvider) | |
} | |
return cachedValue | |
} | |
} | |
internal class ArgumentImpl<T : Serializable>( | |
key: String, | |
defaultValueProvider: () -> T | |
) : Argument<T>, NullableArgumentImpl<T>(key, defaultValueProvider) { | |
override fun getValue(fragRef: Fragment): T { | |
return super<NullableArgumentImpl>.getValue(fragRef)!! | |
} | |
} | |
private fun <T : Serializable> Fragment.getArgument( | |
key: String, | |
defaultValue: () -> T? | |
): T? { | |
return (arguments?.getSerializable(key) ?: defaultValue().also { | |
arguments?.putSerializable(key, it) | |
}) as T? | |
} |
This file contains 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
abstract class BaseFragment : Fragment() { | |
val <T : Serializable> Argument<T>.value: T? | |
get() { | |
return this.getValue(this@BaseFragment) | |
} | |
} | |
class SourceFragment : BaseFragment() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
findNavController().navigate( | |
R.id.action_sourceFragment_to_targetFragment, | |
TargetFragment.Args("A title", "A subtitle") | |
) | |
} | |
} | |
class TargetFragment : BaseFragment() { | |
object Args : ArgumentCompanion() { | |
val title by argument<String>() | |
val subtitle by argument<String>() | |
operator fun invoke(title: String, subtitle: String) = bundleOf( | |
Args.title(title), | |
Args.subtitle(subtitle) | |
) | |
} | |
private val title by Args.title | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
println("Title: $title") | |
println("Subtitle: ${Args.subtitle.value}") | |
findNavController().navigate( | |
R.id.action_targetFragment_to_targetFragmentTwo, | |
TargetFragmentTwo.Args.let { | |
bundleOf( | |
it.title("Second title"), | |
it.subtitle("Second subtitle") | |
) | |
} | |
) | |
} | |
} | |
class TargetFragmentTwo : BaseFragment() { | |
object Args : ArgumentCompanion() { | |
val title by argument<String>() | |
val subtitle by argument<String>() | |
} | |
private val title by Args.title | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
println("Title: $title") | |
println("Subtitle: ${Args.subtitle.value}") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment