这是indexloc提供的服务,不要输入任何密码
Skip to content

Add support for List<Enum> arguments in typesafe navigation #725

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,46 @@ abstract class RouteDecoderTest(val source: ArgumentSource) {
assertThat(result.arg).isEqualTo(listOf(11E123, 11.11))
}

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

val map = mapOf("arg" to listOf(TestEnum.TWO, TestEnum.TWO))
val result =
decode<TestClass>(
map,
listOf(
navArgument("arg") { type = InternalNavType.EnumListType(TestEnum::class.java) }
)
)
assertThat(result.arg).isEqualTo(listOf(TestEnum.TWO, TestEnum.TWO))
}

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

val values = mapOf("arg" to listOf(TestEnum.TWO, TestEnum.TWO))
val result =
decode<TestClass>(
values,
listOf(
navArgument("arg") { type = InternalNavType.EnumListType(TestEnum::class.java) }
)
)
assertThat(result.arg).isEqualTo(listOf(TestEnum.TWO, TestEnum.TWO))

val values2 = mapOf("arg" to null)
val result2 =
decode<TestClass>(
values2,
listOf(
navArgument("arg") { type = InternalNavType.EnumListType(TestEnum::class.java) }
)
)
assertThat(result2.arg).isNull()
}

@Test
fun decodeIntString() {
@Serializable @SerialName(PATH_SERIAL_NAME) class TestClass(val arg: Int, val arg2: String)
Expand Down Expand Up @@ -404,6 +444,12 @@ abstract class RouteDecoderTest(val source: ArgumentSource) {

@Serializable private class CustomType(val nestedArg: Int)

@Serializable
private enum class TestEnum {
ONE,
TWO
}

private val customNavType =
navArgument("arg") {
type =
Expand Down
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 = false
}
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
Loading