Skip to content

Commit

Permalink
Add support for List<Enum> arguments in typesafe navigation
Browse files Browse the repository at this point in the history
Test: ./gradlew navigation:navigation-common:test
Test: ./gradlew navigation:navigation-common:cC
Test: ./gradlew navigation:navigation-runtime:cC
Bug: 375559962
  • Loading branch information
WonderCsabo committed Nov 9, 2024
1 parent 089d840 commit eb06a5b
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,32 @@ class RouteFilledTest {
}
assertThatRouteFilledFrom(clazz, listOf(arg)).isEqualTo("$PATH_SERIAL_NAME")
}

@Test
fun encodeEnumList() {
@Serializable @SerialName(PATH_SERIAL_NAME) class TestClass(val arg: List<TestEnum>)

val clazz = TestClass(listOf(TestEnum.ONE, TestEnum.TWO))
val arg =
navArgument("arg") {
type = InternalNavType.EnumListType(TestEnum::class.java)
nullable = true
}
assertThatRouteFilledFrom(clazz, listOf(arg)).isEqualTo("$PATH_SERIAL_NAME?arg=ONE&arg=TWO")
}

@Test
fun encodeEnumListNullable() {
@Serializable @SerialName(PATH_SERIAL_NAME) class TestClass(val arg: List<TestEnum>?)

val clazz = TestClass(null)
val arg =
navArgument("arg") {
type = InternalNavType.EnumListType(TestEnum::class.java)
nullable = true
}
assertThatRouteFilledFrom(clazz, listOf(arg)).isEqualTo(PATH_SERIAL_NAME)
}
}

private fun <T : Any> assertThatRouteFilledFrom(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ internal fun SerialDescriptor.getNavType(): NavType<*> {
InternalType.LONG -> NavType.LongListType
InternalType.STRING -> NavType.StringListType
InternalType.STRING_NULLABLE -> InternalNavType.StringNullableListType
InternalType.ENUM ->
@Suppress("UNCHECKED_CAST")
InternalNavType.EnumListType(
getElementDescriptor(0).getClass() as Class<Enum<*>>
)
else -> UNKNOWN
}
}
Expand Down Expand Up @@ -471,6 +476,46 @@ internal object InternalNavType {
override fun emptyCollection(): List<Double> = emptyList()
}

class EnumListType<D : Enum<*>>(type: Class<D>) : CollectionNavType<List<D>?>(true) {
private val enumNavType = EnumType(type)

override val name: String
get() = "List<${enumNavType.name}}>"

override fun put(bundle: Bundle, key: String, value: List<D>?) {
bundle.putSerializable(key, value?.let { ArrayList(value) })
}

@Suppress("DEPRECATION", "UNCHECKED_CAST")
override fun get(bundle: Bundle, key: String): List<D>? = (bundle[key] as? List<D>?)

override fun parseValue(value: String): List<D> = listOf(enumNavType.parseValue(value))

override fun parseValue(value: String, previousValue: List<D>?): List<D>? =
previousValue?.plus(parseValue(value)) ?: parseValue(value)

override fun valueEquals(value: List<D>?, other: List<D>?): Boolean {
val valueArrayList = value?.let { ArrayList(value) }
val otherArrayList = other?.let { ArrayList(other) }
return valueArrayList == otherArrayList
}

override fun serializeAsValues(value: List<D>?): List<String> =
value?.map { it.toString() } ?: emptyList()

override fun emptyCollection(): List<D> = emptyList()

public override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is EnumListType<*>) return false
return enumNavType == other.enumNavType
}

public override fun hashCode(): Int {
return enumNavType.hashCode()
}
}

class EnumNullableType<D : Enum<*>?>(type: Class<D?>) : SerializableNullableType<D?>(type) {
private val type: Class<D?>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,34 @@ class NavArgumentGeneratorTest {
assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
}

@Test
fun convertToEnumList() {
@Serializable class TestClass(val arg: List<TestEnum>)

val converted = serializer<TestClass>().generateNavArguments()
val expected =
navArgument("arg") {
type = InternalNavType.EnumListType(TestEnum::class.java)
nullable = false
}
assertThat(converted).containsExactlyInOrder(expected)
assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
}

@Test
fun convertToEnumListNullable() {
@Serializable class TestClass(val arg: List<TestEnum>?)

val converted = serializer<TestClass>().generateNavArguments()
val expected =
navArgument("arg") {
type = InternalNavType.EnumListType(TestEnum::class.java)
nullable = true
}
assertThat(converted).containsExactlyInOrder(expected)
assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
}

@Test
fun convertToParcelable() {
@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5250,6 +5250,42 @@ class NavControllerRouteTest {
assertThat(route2!!.arg).containsExactly(11E123, 11.11)
}

@UiThreadTest
@Test
fun testNavigateWithObjectEnumList() {
@Serializable @SerialName("test") class TestClass(val arg: List<TestEnum>)

val navController = createNavController()
navController.graph =
navController.createGraph(
startDestination = TestClass(listOf(TestEnum.ONE, TestEnum.TWO))
) {
test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo("test?arg={arg}")
val route = navController.currentBackStackEntry?.toRoute<TestClass>()
assertThat(route!!.arg).containsExactly(TestEnum.ONE, TestEnum.TWO)
}

@UiThreadTest
@Test
fun testNavigateWithObjectNullEnumList() {
@Serializable @SerialName("test") class TestClass(val arg: List<TestEnum>? = null)

val navController = createNavController()
navController.graph =
navController.createGraph(startDestination = TestClass(null)) { test<TestClass>() }

assertThat(navController.currentDestination?.route).isEqualTo("test?arg={arg}")
val route = navController.currentBackStackEntry?.toRoute<TestClass>()
assertThat(route!!.arg).isNull()

navController.navigate(TestClass(listOf(TestEnum.ONE, TestEnum.TWO)))
assertThat(navController.currentDestination?.route).isEqualTo("test?arg={arg}")
val route2 = navController.currentBackStackEntry?.toRoute<TestClass>()
assertThat(route2!!.arg).containsExactly(TestEnum.ONE, TestEnum.TWO)
}

@UiThreadTest
@Test
fun testNavigateWithObjectValueClass() {
Expand Down

0 comments on commit eb06a5b

Please sign in to comment.