SavedInstanceState.kt

/*
 * Copyright 2020 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.compose.runtime.savedinstancestate

import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.SnapshotMutationPolicy
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.structuralEqualityPolicy

/**
 * Used to introduce a state value of type [T] into a composition.
 *
 * It behaves similarly to `remember { mutableStateOf(...) }`, but the stored value will survive
 * the activity or process recreation using the saved instance state mechanism (for example it
 * happens when the screen is rotated in the Android application).
 *
 * @sample androidx.compose.runtime.savedinstancestate.samples.SavedInstanceStateSample
 *
 * This function works nicely with immutable values as we wrap them into [MutableState] and
 * update the values in this state. If you work with a mutable object and going to update the
 * state of this object instead of recreating it [rememberSavedInstanceState] can suit you more.
 *
 * If you use it with types which can be stored inside the Bundle then it will be saved and
 * restored automatically using [autoSaver], otherwise you will need to provide a custom [Saver]
 * implementation via the [saver] param.
 *
 * @sample androidx.compose.runtime.savedinstancestate.samples.CustomSaverSample
 *
 * @param inputs A set of inputs such that, when any of them have changed, will cause the state to
 * reset and [init] to be rerun
 * @param saver The [Saver] object which defines how the state is saved and restored.
 * @param key An optional key to be used as a key for the saved value. If not provided we use the
 * automatically generated by the Compose runtime which is unique for the every exact code location
 * in the composition tree
 * @param policy a callback to compare the previous and new instance of [T] when
 * [MutableState.value] is written to. The policy is used to determine how composition should be
 * scheduled. See [SnapshotMutationPolicy].
 * @param init A factory function to create the initial value of this state
 */
@Composable
fun <T> savedInstanceState(
    vararg inputs: Any?,
    saver: Saver<T, out Any> = autoSaver(),
    key: String? = null,
    policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy(),
    init: () -> T
): MutableState<T> = rememberSavedInstanceState(
    *inputs,
    saver = mutableStateSaver(saver, policy),
    key = key,
    init = { mutableStateOf(init(), policy) }
)

private fun <T> mutableStateSaver(
    inner: Saver<T, out Any>,
    policy: SnapshotMutationPolicy<T>
) = Saver<MutableState<T>, Any>(
    save = { state ->
        with(inner) {
            val value = state.value
            if (value == null) {
                EmptyStateValue
            } else {
                save(value)
            }
        }
    },
    restore = @Suppress("UNCHECKED_CAST") {
        val restored = if (it == EmptyStateValue) {
            null
        } else {
            (inner as Saver<T, Any>).restore(it)
        }
        mutableStateOf(restored as T, policy)
    }
)

/**
 * The object we save to indicate that we will need to restore the state with a null value.
 */
private val EmptyStateValue = "[NullValuePlacedInsideTheMutableState]"