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

Commit f22a18ce authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[kairos] API cleanup, KDoc updates, and samples" into main

parents 08012add 27fadb58
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.systemui.kairos

/** Returns a [State] that is `true` only when all of [states] are `true`. */
@ExperimentalKairosApi
fun allOf(vararg states: State<Boolean>): State<Boolean> = combine(*states) { it.allTrue() }

/** Returns a [State] that is `true` when any of [states] are `true`. */
@ExperimentalKairosApi
fun anyOf(vararg states: State<Boolean>): State<Boolean> = combine(*states) { it.anyTrue() }

/** Returns a [State] containing the inverse of the Boolean held by the original [State]. */
@ExperimentalKairosApi fun not(state: State<Boolean>): State<Boolean> = state.mapCheapUnsafe { !it }

private fun Iterable<Boolean>.allTrue() = all { it }

private fun Iterable<Boolean>.anyTrue() = any { it }
+105 −103

File changed.

Preview size limit exceeded, changes collapsed.

+201 −0

File added.

Preview size limit exceeded, changes collapsed.

+37 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 com.android.systemui.kairos

import com.android.systemui.kairos.internal.CompletableLazy

/**
 * A value that may not be immediately (synchronously) available, but is guaranteed to be available
 * before this transaction is completed.
 */
@ExperimentalKairosApi
class DeferredValue<out A> internal constructor(internal val unwrapped: Lazy<A>) {
    /**
     * Returns the value held by this [DeferredValue], or throws [IllegalStateException] if it is
     * not yet available.
     */
    val value: A
        get() = unwrapped.value
}

/** Returns an already-available [DeferredValue] containing [value]. */
@ExperimentalKairosApi
fun <A> deferredOf(value: A): DeferredValue<A> = DeferredValue(CompletableLazy(value))
+31 −18
Original line number Diff line number Diff line
@@ -16,33 +16,46 @@

package com.android.systemui.kairos

import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Job

/**
 * Scope for external side-effects triggered by the Kairos network. This still occurs within the
 * context of a transaction, so general suspending calls are disallowed to prevent blocking the
 * transaction. You can use [effectCoroutineScope] to [launch][kotlinx.coroutines.launch] new
 * coroutines to perform long-running asynchronous work. This scope is alive for the duration of the
 * containing [BuildScope] that this side-effect scope is running in.
 * Scope for external side-effects triggered by the Kairos network.
 *
 * This still occurs within the context of a transaction, so general suspending calls are disallowed
 * to prevent blocking the transaction. You can [launch] new coroutines to perform long-running
 * asynchronous work. These coroutines are kept alive for the duration of the containing
 * [BuildScope] that this side-effect scope is running in.
 */
@ExperimentalKairosApi
interface EffectScope : TransactionScope {
interface EffectScope : HasNetwork, TransactionScope {
    /**
     * A [CoroutineScope] whose lifecycle lives for as long as this [EffectScope] is alive. This is
     * generally until the [Job][kotlinx.coroutines.Job] returned by [BuildScope.effect] is
     * cancelled.
     * Creates a coroutine that is a child of this [EffectScope], and returns its future result as a
     * [Deferred].
     *
     * @see kotlinx.coroutines.async
     */
    @ExperimentalKairosApi val effectCoroutineScope: CoroutineScope
    fun <R> async(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend KairosCoroutineScope.() -> R,
    ): Deferred<R>

    /**
     * A [KairosNetwork] instance that can be used to transactionally query / modify the Kairos
     * network.
     * Launches a new coroutine that is a child of this [EffectScope] without blocking the current
     * thread and returns a reference to the coroutine as a [Job].
     *
     * The lambda passed to [KairosNetwork.transact] on this instance will receive an [BuildScope]
     * that is lifetime-bound to this [EffectScope]. Once this [EffectScope] is no longer alive, any
     * modifications to the Kairos network performed via this [KairosNetwork] instance will be
     * undone (any registered [observers][BuildScope.observe] are unregistered, and any pending
     * [side-effects][BuildScope.effect] are cancelled).
     * @see kotlinx.coroutines.launch
     */
    @ExperimentalKairosApi val kairosNetwork: KairosNetwork
    fun launch(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend KairosCoroutineScope.() -> Unit,
    ): Job = async(context, start, block)
}

@ExperimentalKairosApi interface KairosCoroutineScope : HasNetwork, CoroutineScope
Loading