Loading packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt +80 −12 Original line number Original line Diff line number Diff line Loading @@ -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 Loading @@ -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. 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 = 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. * * Loading Loading @@ -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 Loading @@ -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 } } } } Loading Loading @@ -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 } } } } } } Loading Loading @@ -263,7 +331,7 @@ private class SaveableSessionImpl( v?.let { StorageEntry.Unrestored(v) } 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 Original line Diff line number Diff line Loading @@ -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 Loading @@ -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. 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 = 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. * * Loading Loading @@ -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 Loading @@ -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 } } } } Loading Loading @@ -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 } } } } } } Loading Loading @@ -263,7 +331,7 @@ private class SaveableSessionImpl( v?.let { StorageEntry.Unrestored(v) } v?.let { StorageEntry.Unrestored(v) } } } ) ) } }, ) ) } } Loading