RouteDecoder.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.
*/
package androidx.navigation.serialization
import android.os.Bundle
import androidx.navigation.NavType
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.AbstractDecoder
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule
@OptIn(ExperimentalSerializationApi::class)
internal class RouteDecoder(
bundle: Bundle,
typeMap: Map<String, NavType<*>>
) : AbstractDecoder() {
private val decoder = Decoder(bundle, typeMap)
@Suppress("DEPRECATION") // deprecated in 1.6.3
override val serializersModule: SerializersModule = EmptySerializersModule
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
return decoder.incrementElement(descriptor)
}
override fun decodeValue(): Any = decoder.decodeValue()
override fun decodeNull(): Nothing? = null
// we want to know if it is not null, so its !isNull
override fun decodeNotNullMark(): Boolean = !decoder.isCurrentElementNull()
// value from decodeValue() rather than decodeInt, decodeBoolean etc.. needs to be casted
@Suppress("UNCHECKED_CAST")
override fun <T> decodeSerializableElement(
descriptor: SerialDescriptor,
index: Int,
deserializer: DeserializationStrategy<T>,
previousValue: T?
): T = decoder.decodeValue() as T
}
private class Decoder(
private val bundle: Bundle,
private val typeMap: Map<String, NavType<*>>
) {
private var elementIndex: Int = -1
private var elementName: String = ""
@OptIn(ExperimentalSerializationApi::class)
fun incrementElement(descriptor: SerialDescriptor): Int {
if (++elementIndex >= descriptor.elementsCount) return CompositeDecoder.DECODE_DONE
elementName = descriptor.getElementName(elementIndex)
return elementIndex
}
fun decodeValue(): Any {
val navType = typeMap[elementName]
val arg = navType?.get(bundle, elementName)
checkNotNull(arg) {
"Unexpected null value for non-nullable argument $elementName"
}
return arg
}
fun isCurrentElementNull(): Boolean {
val navType = typeMap[elementName]
return navType?.isNullableAllowed == true && navType[bundle, elementName] == null
}
}