Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 110d593a authored by Steve Elliott's avatar Steve Elliott
Browse files

[flexiglass] SessionDisposableEffect + sessionCoroutineScope

Flag: com.android.systemui.scene_container
Bug: 403285138
Test: atest
Change-Id: Ib5c80dd1f1ed34f681cc18f1c994f5a28e39172d
parent 1695035a
Loading
Loading
Loading
Loading
+80 −12
Original line number Original line Diff line number Diff line
@@ -17,10 +17,14 @@
package com.android.systemui.scene.session.ui.composable
package com.android.systemui.scene.session.ui.composable


import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffectResult
import androidx.compose.runtime.DisposableEffectScope
import androidx.compose.runtime.RememberObserver
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.currentCompositeKeyHash
import androidx.compose.runtime.currentCompositeKeyHash
import androidx.compose.runtime.getValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCompositionContext
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.SaverScope
import androidx.compose.runtime.saveable.SaverScope
import androidx.compose.runtime.saveable.mapSaver
import androidx.compose.runtime.saveable.mapSaver
@@ -28,6 +32,10 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.setValue
import com.android.systemui.scene.session.shared.SessionStorage
import com.android.systemui.scene.session.shared.SessionStorage
import com.android.systemui.util.kotlin.mapValuesNotNullTo
import com.android.systemui.util.kotlin.mapValuesNotNullTo
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job


/**
/**
 * An explicit storage for remembering composable state outside of the lifetime of a composition.
 * An explicit storage for remembering composable state outside of the lifetime of a composition.
@@ -88,6 +96,55 @@ fun Session(storage: SessionStorage = SessionStorage()): Session = SessionImpl(s
fun <T> Session.rememberSession(vararg inputs: Any?, key: String? = null, init: () -> T): T =
fun <T> Session.rememberSession(vararg inputs: Any?, key: String? = null, init: () -> T): T =
    rememberSession(key, *inputs, init = init)
    rememberSession(key, *inputs, init = init)


/**
 * A side effect of composition that must be reversed or cleaned up if the [Session] ends.
 *
 * @see androidx.compose.runtime.DisposableEffect
 */
@Composable
fun Session.SessionDisposableEffect(
    vararg inputs: Any?,
    key: String? = null,
    effect: DisposableEffectScope.() -> DisposableEffectResult,
) {
    rememberSession(inputs, key) {
        object : RememberObserver {

            var onDispose: DisposableEffectResult? = null

            override fun onAbandoned() {
                // no-op
            }

            override fun onForgotten() {
                onDispose?.dispose()
                onDispose = null
            }

            override fun onRemembered() {
                onDispose = DisposableEffectScope().effect()
            }
        }
    }
}

/**
 * Return a [CoroutineScope] bound to this [Session] using the optional [CoroutineContext] provided
 * by [getContext]. [getContext] will only be called once and the same [CoroutineScope] instance
 * will be returned for the duration of the [Session].
 *
 * @see androidx.compose.runtime.rememberCoroutineScope
 */
@Composable
fun Session.sessionCoroutineScope(
    getContext: () -> CoroutineContext = { EmptyCoroutineContext }
): CoroutineScope {
    val effectContext: CoroutineContext = rememberCompositionContext().effectCoroutineContext
    val job = rememberSession { Job() }
    SessionDisposableEffect { onDispose { job.cancel() } }
    return rememberSession { CoroutineScope(effectContext + job + getContext()) }
}

/**
/**
 * An explicit storage for remembering composable state outside of the lifetime of a composition.
 * An explicit storage for remembering composable state outside of the lifetime of a composition.
 *
 *
@@ -147,15 +204,10 @@ interface SaveableSession : Session {
 *   location in the composition tree.
 *   location in the composition tree.
 */
 */
@Composable
@Composable
fun rememberSaveableSession(
fun rememberSaveableSession(vararg inputs: Any?, key: String? = null): SaveableSession =
    vararg inputs: Any?,
    key: String? = null,
): SaveableSession =
    rememberSaveable(*inputs, SaveableSessionImpl.SessionSaver, key) { SaveableSessionImpl() }
    rememberSaveable(*inputs, SaveableSessionImpl.SessionSaver, key) { SaveableSessionImpl() }


private class SessionImpl(
private class SessionImpl(private val storage: SessionStorage = SessionStorage()) : Session {
    private val storage: SessionStorage = SessionStorage(),
) : Session {
    @Composable
    @Composable
    override fun <T> rememberSession(key: String?, vararg inputs: Any?, init: () -> T): T {
    override fun <T> rememberSession(key: String?, vararg inputs: Any?, init: () -> T): T {
        val storage = storage.storage
        val storage = storage.storage
@@ -169,16 +221,31 @@ private class SessionImpl(
            }
            }
        if (finalKey !in storage) {
        if (finalKey !in storage) {
            val value = init()
            val value = init()
            SideEffect { storage[finalKey] = SessionStorage.StorageEntry(inputs, value) }
            SideEffect {
                storage[finalKey] = SessionStorage.StorageEntry(inputs, value)
                if (value is RememberObserver) {
                    value.onRemembered()
                }
            }
            return value
            return value
        }
        }
        val entry = storage[finalKey]!!
        val entry = storage[finalKey]!!
        if (!inputs.contentEquals(entry.keys)) {
        if (!inputs.contentEquals(entry.keys)) {
            val value = init()
            val value = init()
            SideEffect { entry.stored = value }
            SideEffect {
                val oldValue = entry.stored
                if (oldValue is RememberObserver) {
                    oldValue.onForgotten()
                }
                entry.stored = value
                if (value is RememberObserver) {
                    value.onRemembered()
                }
            }
            return value
            return value
        }
        }
        @Suppress("UNCHECKED_CAST") return entry.stored as T
        @Suppress("UNCHECKED_CAST")
        return entry.stored as T
    }
    }
}
}


@@ -228,7 +295,8 @@ private class SaveableSessionImpl(
                    }
                    }
                    return value
                    return value
                }
                }
                @Suppress("UNCHECKED_CAST") return entry.stored as T
                @Suppress("UNCHECKED_CAST")
                return entry.stored as T
            }
            }
        }
        }
    }
    }
@@ -263,7 +331,7 @@ private class SaveableSessionImpl(
                            v?.let { StorageEntry.Unrestored(v) }
                            v?.let { StorageEntry.Unrestored(v) }
                        }
                        }
                )
                )
            }
            },
        )
        )
}
}