Skip to content
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

Correctly save/restore of NavController #1508

Merged
merged 2 commits into from
Aug 30, 2024
Merged
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 @@ -41,8 +41,9 @@ expect class Bundle() {
fun putDouble(key: String?, value: Double)
fun putString(key: String?, value: String?)
fun putCharSequence(key: String?, value: CharSequence?)
fun putIntegerArrayList(key: String?, value: ArrayList<Int>?)
fun putStringArrayList(key: String?, value: ArrayList<String>?)
fun putBundle(key: String?, value: Bundle?)
fun putIntegerArrayList(key: String?, value: ArrayList<Int?>?)
fun putStringArrayList(key: String?, value: ArrayList<String?>?)
fun putBooleanArray(key: String?, value: BooleanArray?)
fun putByteArray(key: String?, value: ByteArray?)
fun putShortArray(key: String?, value: ShortArray?)
Expand All @@ -51,9 +52,8 @@ expect class Bundle() {
fun putLongArray(key: String?, value: LongArray?)
fun putFloatArray(key: String?, value: FloatArray?)
fun putDoubleArray(key: String?, value: DoubleArray?)
fun putStringArray(key: String?, value: Array<String>?)
fun putCharSequenceArray(key: String?, value: Array<CharSequence>?)
fun putBundle(key: String?, value: Bundle?)
fun putStringArray(key: String?, value: Array<String?>?)
fun putCharSequenceArray(key: String?, value: Array<CharSequence?>?)

fun getBoolean(key: String?): Boolean
fun getBoolean(key: String?, defaultValue: Boolean): Boolean
Expand All @@ -75,8 +75,9 @@ expect class Bundle() {
fun getString(key: String?, defaultValue: String): String
fun getCharSequence(key: String?): CharSequence?
fun getCharSequence(key: String?, defaultValue: CharSequence): CharSequence
fun getIntegerArrayList(key: String?): ArrayList<Int>?
fun getStringArrayList(key: String?): ArrayList<String>?
fun getBundle(key: String?): Bundle?
fun getIntegerArrayList(key: String?): ArrayList<Int?>?
fun getStringArrayList(key: String?): ArrayList<String?>?
fun getBooleanArray(key: String?): BooleanArray?
fun getByteArray(key: String?): ByteArray?
fun getShortArray(key: String?): ShortArray?
Expand All @@ -85,15 +86,16 @@ expect class Bundle() {
fun getLongArray(key: String?): LongArray?
fun getFloatArray(key: String?): FloatArray?
fun getDoubleArray(key: String?): DoubleArray?
fun getStringArray(key: String?): Array<String>?
fun getCharSequenceArray(key: String?): Array<CharSequence>?
fun getBundle(key: String?): Bundle?
fun getStringArray(key: String?): Array<String?>?
fun getCharSequenceArray(key: String?): Array<CharSequence?>?

@Deprecated("Use the type-safe specific APIs depending on the type of the item to be retrieved")
operator fun get(key: String?): Any?
}

/**
* Returns a new [Bundle] with the given key/value pairs as elements.
*
* @throws IllegalArgumentException When a value is not a supported type of [Bundle].
*/
expect fun bundleOf(vararg pairs: Pair<String, Any?>): Bundle
107 changes: 65 additions & 42 deletions core/core-bundle/src/jbMain/kotlin/androidx/core/bundle/Bundle.jb.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,20 @@ package androidx.core.bundle
import kotlin.reflect.KClass

@Suppress("ReplaceGetOrSet")
actual class Bundle {
private val mMap: MutableMap<String?, Any?>
actual constructor() { mMap = LinkedHashMap() }
actual constructor(initialCapacity: Int) { mMap = LinkedHashMap(initialCapacity) }
actual constructor(bundle: Bundle) { mMap = LinkedHashMap(bundle.mMap) }

actual fun size() = mMap.size
actual fun isEmpty() = mMap.isEmpty()
actual fun clear() = mMap.clear()
actual fun containsKey(key: String?) = mMap.containsKey(key)
actual fun remove(key: String?) { mMap.remove(key) }
actual fun keySet(): Set<String?> = mMap.keys
actual fun putAll(bundle: Bundle) { mMap.putAll(bundle.mMap) }
actual class Bundle internal constructor(
private val bundleData: MutableMap<String?, Any?>
) {
actual constructor() : this(bundleData = LinkedHashMap())
actual constructor(initialCapacity: Int) : this(bundleData = LinkedHashMap(initialCapacity))
actual constructor(bundle: Bundle) : this(bundleData = LinkedHashMap(bundle.bundleData))

actual fun size(): Int = bundleData.size
actual fun isEmpty(): Boolean = bundleData.isEmpty()
actual fun clear() { bundleData.clear() }
actual fun containsKey(key: String?): Boolean = bundleData.containsKey(key)
actual fun remove(key: String?) { bundleData.remove(key) }
actual fun keySet(): Set<String?> = bundleData.keys
actual fun putAll(bundle: Bundle) { bundleData.putAll(bundle.bundleData) }

actual fun putBoolean(key: String?, value: Boolean) { setObject(key, value) }
actual fun putByte(key: String?, value: Byte) { setObject(key, value) }
Expand All @@ -43,8 +44,9 @@ actual class Bundle {
actual fun putDouble(key: String?, value: Double) { setObject(key, value) }
actual fun putString(key: String?, value: String?) { setObject(key, value) }
actual fun putCharSequence(key: String?, value: CharSequence?) { setObject(key, value) }
actual fun putIntegerArrayList(key: String?, value: ArrayList<Int>?) { setObject(key, value) }
actual fun putStringArrayList(key: String?, value: ArrayList<String>?) { setObject(key, value) }
actual fun putBundle(key: String?, value: Bundle?) { setObject(key, value) }
actual fun putIntegerArrayList(key: String?, value: ArrayList<Int?>?) { setObject(key, value) }
actual fun putStringArrayList(key: String?, value: ArrayList<String?>?) { setObject(key, value) }
actual fun putBooleanArray(key: String?, value: BooleanArray?) { setObject(key, value) }
actual fun putByteArray(key: String?, value: ByteArray?) { setObject(key, value) }
actual fun putShortArray(key: String?, value: ShortArray?) { setObject(key, value) }
Expand All @@ -53,12 +55,15 @@ actual class Bundle {
actual fun putLongArray(key: String?, value: LongArray?) { setObject(key, value) }
actual fun putFloatArray(key: String?, value: FloatArray?) { setObject(key, value) }
actual fun putDoubleArray(key: String?, value: DoubleArray?) { setObject(key, value) }
actual fun putStringArray(key: String?, value: Array<String>?) { setObject(key, value) }
actual fun putCharSequenceArray(key: String?, value: Array<CharSequence>?) { setObject(key, value) }
actual fun putBundle(key: String?, value: Bundle?) { setObject(key, value) }
actual fun putStringArray(key: String?, value: Array<String?>?) { setObject(key, value) }
actual fun putCharSequenceArray(key: String?, value: Array<CharSequence?>?) { setObject(key, value) }

private inline fun setObject(key: String?, value: Any?) {
mMap[key] = value
// Narrowed alternative of Android's [putParcelableArray]
fun putBundleArray(key: String?, value: Array<Bundle?>?) { setObject(key, value) }

@Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
private inline fun <T> setObject(key: String?, value: T) {
bundleData[key] = value
}

actual fun getBoolean(key: String?): Boolean = getBoolean(key, defaultValue = false)
Expand All @@ -84,8 +89,9 @@ actual class Bundle {
actual fun getCharSequence(key: String?): CharSequence? = getObject(key)
actual fun getCharSequence(key: String?, defaultValue: CharSequence): CharSequence =
getCharSequence(key) ?: defaultValue
actual fun getIntegerArrayList(key: String?): ArrayList<Int>? = getArrayList(key)
actual fun getStringArrayList(key: String?): ArrayList<String>? = getArrayList(key)
actual fun getBundle(key: String?): Bundle? = getObject(key)
actual fun getIntegerArrayList(key: String?): ArrayList<Int?>? = getArrayList(key)
actual fun getStringArrayList(key: String?): ArrayList<String?>? = getArrayList(key)
actual fun getBooleanArray(key: String?): BooleanArray? = getObject(key)
actual fun getByteArray(key: String?): ByteArray? = getObject(key)
actual fun getShortArray(key: String?): ShortArray? = getObject(key)
Expand All @@ -94,51 +100,53 @@ actual class Bundle {
actual fun getLongArray(key: String?): LongArray? = getObject(key)
actual fun getFloatArray(key: String?): FloatArray? = getObject(key)
actual fun getDoubleArray(key: String?): DoubleArray? = getObject(key)
actual fun getStringArray(key: String?): Array<String>? = getArray(key)
actual fun getCharSequenceArray(key: String?): Array<CharSequence>? = getArray(key)
actual fun getBundle(key: String?): Bundle? = getObject(key)
actual fun getStringArray(key: String?): Array<String?>? = getArray(key)
actual fun getCharSequenceArray(key: String?): Array<CharSequence?>? = getArray(key)

// Narrowed alternative of Android's [getParcelableArray]
fun getBundleArray(key: String?): Array<Bundle?>? = getArray(key)

@Deprecated("Use the type-safe specific APIs depending on the type of the item to be retrieved")
actual operator fun get(key: String?): Any? = mMap.get(key)
actual operator fun get(key: String?): Any? = bundleData.get(key)

private inline fun <reified T: Any> getObject(key: String?): T? {
val o = mMap.get(key) ?: return null
private inline fun <reified T : Any> getObject(key: String?): T? {
val value = bundleData.get(key) ?: return null
return try {
o as T?
value as T?
} catch (e: ClassCastException) {
typeWarning(key, o, T::class.canonicalName!!, e)
typeWarning(key, value, T::class.canonicalName!!, e)
null
}
}

private inline fun <reified T: Any> getObject(key: String?, defaultValue: T): T {
val o = mMap.get(key) ?: return defaultValue
private inline fun <reified T : Any> getObject(key: String?, defaultValue: T): T {
val value = bundleData.get(key) ?: return defaultValue
return try {
o as T
value as T
} catch (e: ClassCastException) {
typeWarning(key, o, T::class.canonicalName!!, defaultValue, e)
typeWarning(key, value, T::class.canonicalName!!, defaultValue, e)
defaultValue
}
}

@Suppress("UNCHECKED_CAST")
private inline fun <reified T: Any> getArrayList(key: String?): ArrayList<T>? {
val o = mMap.get(key) ?: return null
private inline fun <reified T : Any> getArrayList(key: String?): ArrayList<T?>? {
val value = bundleData.get(key) ?: return null
return try {
o as ArrayList<T>?
value as ArrayList<T?>?
} catch (e: ClassCastException) {
typeWarning(key, o, "ArrayList<" + T::class.canonicalName!! + ">", e)
typeWarning(key, value, "ArrayList<" + T::class.canonicalName!! + ">", e)
null
}
}

@Suppress("UNCHECKED_CAST")
private inline fun <reified T: Any> getArray(key: String?): Array<T>? {
val o = mMap.get(key) ?: return null
private inline fun <reified T : Any> getArray(key: String?): Array<T?>? {
val value = bundleData.get(key) ?: return null
return try {
o as Array<T>?
value as Array<T?>?
} catch (e: ClassCastException) {
typeWarning(key, o, "Array<" + T::class.canonicalName!! + ">", e)
typeWarning(key, value, "Array<" + T::class.canonicalName!! + ">", e)
null
}
}
Expand Down Expand Up @@ -172,6 +180,8 @@ actual class Bundle {
private fun typeWarning(key: String?, value: Any?, className: String, e: RuntimeException) {
typeWarning(key, value, className, "<null>", e)
}

override fun toString(): String = "$bundleData"
}

actual fun bundleOf(vararg pairs: Pair<String, Any?>): Bundle = Bundle(pairs.size).apply {
Expand All @@ -191,10 +201,23 @@ actual fun bundleOf(vararg pairs: Pair<String, Any?>): Bundle = Bundle(pairs.siz

// References
is Bundle -> putBundle(key, value)
is String -> putString(key, value)
is CharSequence -> putCharSequence(key, value)

// Scalar arrays
is BooleanArray -> putBooleanArray(key, value)
is ByteArray -> putByteArray(key, value)
is CharArray -> putCharArray(key, value)
is DoubleArray -> putDoubleArray(key, value)
is FloatArray -> putFloatArray(key, value)
is IntArray -> putIntArray(key, value)
is LongArray -> putLongArray(key, value)
is ShortArray -> putShortArray(key, value)

else -> {
val valueType = value::class.canonicalName
throw IllegalArgumentException("Illegal value type $valueType for key \"$key\"")
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,8 @@ public actual abstract class NavType<T> actual constructor(
get() = "string[]"

override fun put(bundle: Bundle, key: String, value: Array<String>?) {
bundle.putStringArray(key, value)
@Suppress("UNCHECKED_CAST")
bundle.putStringArray(key, value as Array<String?>?)
}

@Suppress("UNCHECKED_CAST", "DEPRECATION")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package androidx.navigation
import androidx.core.bundle.Bundle
import androidx.lifecycle.Lifecycle

// TODO: Make @Serializable
internal data class NavBackStackEntryState(
val id: String,
val destinationId: Int,
Expand Down Expand Up @@ -49,4 +48,32 @@ internal data class NavBackStackEntryState(
savedState
)
}

fun toBundle(): Bundle = Bundle().apply {
putString(KEY_ID, id)
putInt(KEY_DESTINATION_ID, destinationId)
putBundle(KEY_ARGS, args)
putBundle(KEY_SAVED_STATE, savedState)
}

companion object {
private const val KEY_ID = "NavBackStackEntryState.id"
private const val KEY_DESTINATION_ID = "NavBackStackEntryState.destinationId"
private const val KEY_ARGS = "NavBackStackEntryState.args"
private const val KEY_SAVED_STATE = "NavBackStackEntryState.savedState"

fun fromBundle(bundle: Bundle?): NavBackStackEntryState? {
if (bundle == null) return null
val id = bundle.getString(KEY_ID) ?: return null
val destinationId = bundle.getInt(KEY_DESTINATION_ID)
val args = bundle.getBundle(KEY_ARGS) ?: return null
val savedState = bundle.getBundle(KEY_SAVED_STATE) ?: return null
return NavBackStackEntryState(
id = id,
destinationId = destinationId,
args = args,
savedState = savedState
)
}
}
}
Loading