Skip to content

Instantly share code, notes, and snippets.

@Andrew0000
Last active October 17, 2023 10:22
Show Gist options
  • Select an option

  • Save Andrew0000/401496c66b026b1dc6673449b8fe9603 to your computer and use it in GitHub Desktop.

Select an option

Save Andrew0000/401496c66b026b1dc6673449b8fe9603 to your computer and use it in GitHub Desktop.
Destination
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.os.Parcelable
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.NavOptions
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.Serializable
sealed class Destination {
data class Navigate(
@IdRes
val id: Int,
val args: Map<String, Any>? = null,
val clearStack: Boolean = false,
) : Destination() {
fun plusArgs(argsNew: Map<String, Any>) = copy(
id = id,
args = (args ?: mapOf()) + argsNew
)
}
object Back : Destination()
object BackCloseKeyboard : Destination()
data class BackTo(
@IdRes
val id: Int,
val closeKeyboard: Boolean = true,
) : Destination()
data class Combined(
val list: List<Destination>,
) : Destination() {
constructor(vararg destinations: Destination) : this(destinations.toList())
}
data class OpenBrowser(
val url: String,
) : Destination()
data class NavigateCustom(
val id: Int,
) : Destination()
}
fun Destination.Navigate.getBundleFromDestination(): Bundle? {
if (args.isNullOrEmpty()) {
return null
}
val bundle = Bundle()
args.entries.forEach { (key, value) ->
when (value) {
is String -> bundle.putString(key, value)
is Boolean -> bundle.putBoolean(key, value)
is Int -> bundle.putInt(key, value)
is Long -> bundle.putLong(key, value)
is Float -> bundle.putFloat(key, value)
is Double -> bundle.putDouble(key, value)
is Serializable -> bundle.putSerializable(key, value)
is Parcelable -> bundle.putParcelable(key, value)
else -> {
Timber.w("Unhandled type: $value")
if (BuildConfig.DEBUG) {
throw IllegalArgumentException("Arg isn't handled: $value. Need to write handler code.")
}
}
}
}
return bundle
}
fun NavController.navigate(
dest: Destination,
lifecycleOwner: LifecycleOwner? = null,
context: Context? = null,
customNavigation: ((Int) -> Unit)? = null,
) {
when (dest) {
is Destination.Navigate -> {
val options = when {
dest.clearStack -> NavOptions.Builder()
.setPopUpTo(graph.startDestinationId, inclusive = true)
.appendCommonAnimationOptions()
.build()
else -> null
}
navigate(
dest.id,
dest.getBundleFromDestination(),
options,
)
}
is Destination.Combined -> {
dest.list.forEach {
navigate(it)
}
}
Destination.Back -> {
popBackStack()
}
is Destination.BackCloseKeyboard -> {
if (lifecycleOwner != null) {
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.closeKeyboard()
popBackStack()
}
} else {
DebugAssert.fail("Can't close keyboard without lifecycle")
popBackStack()
}
}
is Destination.BackTo -> {
if (dest.closeKeyboard && lifecycleOwner != null) {
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.closeKeyboard()
popBackStack(dest.id, false)
}
} else {
popBackStack(dest.id, false)
}
}
is Destination.OpenBrowser -> {
context?.openBrowser(dest.url)
}
is Destination.NavigateCustom -> {
customNavigation?.invoke(dest.id)
}
}
}
private suspend fun LifecycleOwner.closeKeyboard() {
if (this is Fragment) {
closeKeyboard()
delay(200)
}
if (this is Activity) {
closeKeyboard()
delay(200)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment