RouteEncoder.kt
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
package androidx.navigation.serialization
import androidx.annotation.RestrictTo
import androidx.navigation.NavType
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.AbstractEncoder
import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule
/**
* Encodes KClass of type T into a route filled with arguments
*/
@OptIn(ExperimentalSerializationApi::class)
internal class RouteEncoder<T : Any>(
private val serializer: KSerializer<T>,
private val typeMap: Map<String, NavType<Any?>>
) : AbstractEncoder() {
@Suppress("DEPRECATION") // deprecated in 1.6.3
override val serializersModule: SerializersModule = EmptySerializersModule
private val builder = RouteBuilder.Filled(serializer, typeMap)
/**
* Entry point to set up and start encoding [T].
*
* The default entry point is [encodeSerializableValue] but we need to override it to handle
* primitive and non-primitive values by converting them directly to string (instead of the
* default implementation which further serializes nested non-primitive values). So we
* delegate to the default entry by directly calling [super.encodeSerializableValue].
*/
fun encodeRouteWithArgs(value: T): String {
super.encodeSerializableValue(serializer, value)
return builder.build()
}
/**
* Can handle both primitives and non-primitives. This method is called in three possible
* scenarios:
* 1. nullable primitive type with non-null value
* 2. nullable non-primitive type with non-null value
* 3. non-nullable non-primitive type
*
* String literal "null" is considered non-null value.
*/
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
if (value == "null") {
builder.addNull(value)
} else {
builder.addArg(value)
}
}
/**
* Essentially called for every single argument.
*/
override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
builder.setElementIndex(index)
return true
}
/**
* Called for non-nullable primitives of non-null value.
*
* String literal "null" is considered non-null value.
*/
override fun encodeValue(value: Any) {
if (value == "null") {
builder.addNull(value)
} else {
builder.addArg(value)
}
}
/**
* Called for primitive / non-primitives of null value
*/
override fun encodeNull() {
builder.addNull(null)
}
}