Session.kt
/*
* Copyright 2022 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.glance.session
import android.content.Context
import androidx.annotation.RestrictTo
import androidx.compose.runtime.Composable
import androidx.glance.EmittableWithChildren
import androidx.glance.GlanceComposable
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ClosedReceiveChannelException
/**
* [Session] is implemented by Glance surfaces in order to provide content for the
* composition and process the results of recomposition.
*
* @suppress
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
abstract class Session(val key: String) {
private val eventChannel = Channel<Any>(Channel.UNLIMITED)
/**
* Create the [EmittableWithChildren] that will be used as the [androidx.glance.Applier] root.
*/
abstract fun createRootEmittable(): EmittableWithChildren
/**
* Provide the Glance composable to be run in the [androidx.compose.runtime.Composition].
*/
abstract fun provideGlance(context: Context): @Composable @GlanceComposable () -> Unit
/**
* Process the Emittable tree that results from the running [provideGlance].
*
* This will also be called for the results of future recompositions.
* @return true if the tree has been processed and the session is ready to handle events.
*/
abstract suspend fun processEmittableTree(
context: Context,
root: EmittableWithChildren
): Boolean
/**
* Process an event that was sent to this session.
*/
abstract suspend fun processEvent(context: Context, event: Any)
/**
* Enqueues an [event] to be processed by the session.
*
* These requests may be processed by calling [receiveEvents]. Session implementations should
* wrap sendEvent with public methods to send the event types that their Session supports.
*/
protected suspend fun sendEvent(event: Any) {
eventChannel.send(event)
}
/**
* Process incoming events, additionally running [block] for each event that is received.
*
* This function suspends until [close] is called.
*/
suspend fun receiveEvents(context: Context, block: (Any) -> Unit) {
try {
for (event in eventChannel) {
block(event)
processEvent(context, event)
}
} catch (_: ClosedReceiveChannelException) {
}
}
fun close() {
eventChannel.close()
}
}