Loading packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt +80 −12 Original line number Diff line number Diff line Loading @@ -17,10 +17,14 @@ package com.android.systemui.scene.session.ui.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.currentCompositeKeyHash import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCompositionContext import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.SaverScope import androidx.compose.runtime.saveable.mapSaver Loading @@ -28,6 +32,10 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import com.android.systemui.scene.session.shared.SessionStorage 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. Loading Loading @@ -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 = 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. * Loading Loading @@ -147,15 +204,10 @@ interface SaveableSession : Session { * location in the composition tree. */ @Composable fun rememberSaveableSession( vararg inputs: Any?, key: String? = null, ): SaveableSession = fun rememberSaveableSession(vararg inputs: Any?, key: String? = null): SaveableSession = rememberSaveable(*inputs, SaveableSessionImpl.SessionSaver, key) { SaveableSessionImpl() } private class SessionImpl( private val storage: SessionStorage = SessionStorage(), ) : Session { private class SessionImpl(private val storage: SessionStorage = SessionStorage()) : Session { @Composable override fun <T> rememberSession(key: String?, vararg inputs: Any?, init: () -> T): T { val storage = storage.storage Loading @@ -169,16 +221,31 @@ private class SessionImpl( } if (finalKey !in storage) { 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 } val entry = storage[finalKey]!! if (!inputs.contentEquals(entry.keys)) { 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 } @Suppress("UNCHECKED_CAST") return entry.stored as T @Suppress("UNCHECKED_CAST") return entry.stored as T } } Loading Loading @@ -228,7 +295,8 @@ private class SaveableSessionImpl( } return value } @Suppress("UNCHECKED_CAST") return entry.stored as T @Suppress("UNCHECKED_CAST") return entry.stored as T } } } Loading Loading @@ -263,7 +331,7 @@ private class SaveableSessionImpl( v?.let { StorageEntry.Unrestored(v) } } ) } }, ) } Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt +80 −12 Original line number Diff line number Diff line Loading @@ -17,10 +17,14 @@ package com.android.systemui.scene.session.ui.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.currentCompositeKeyHash import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCompositionContext import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.SaverScope import androidx.compose.runtime.saveable.mapSaver Loading @@ -28,6 +32,10 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import com.android.systemui.scene.session.shared.SessionStorage 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. Loading Loading @@ -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 = 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. * Loading Loading @@ -147,15 +204,10 @@ interface SaveableSession : Session { * location in the composition tree. */ @Composable fun rememberSaveableSession( vararg inputs: Any?, key: String? = null, ): SaveableSession = fun rememberSaveableSession(vararg inputs: Any?, key: String? = null): SaveableSession = rememberSaveable(*inputs, SaveableSessionImpl.SessionSaver, key) { SaveableSessionImpl() } private class SessionImpl( private val storage: SessionStorage = SessionStorage(), ) : Session { private class SessionImpl(private val storage: SessionStorage = SessionStorage()) : Session { @Composable override fun <T> rememberSession(key: String?, vararg inputs: Any?, init: () -> T): T { val storage = storage.storage Loading @@ -169,16 +221,31 @@ private class SessionImpl( } if (finalKey !in storage) { 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 } val entry = storage[finalKey]!! if (!inputs.contentEquals(entry.keys)) { 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 } @Suppress("UNCHECKED_CAST") return entry.stored as T @Suppress("UNCHECKED_CAST") return entry.stored as T } } Loading Loading @@ -228,7 +295,8 @@ private class SaveableSessionImpl( } return value } @Suppress("UNCHECKED_CAST") return entry.stored as T @Suppress("UNCHECKED_CAST") return entry.stored as T } } } Loading Loading @@ -263,7 +331,7 @@ private class SaveableSessionImpl( v?.let { StorageEntry.Unrestored(v) } } ) } }, ) } Loading