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

Commit 19933aeb authored by Andreas Miko's avatar Andreas Miko Committed by Android (Google) Code Review
Browse files

Merge changes I81feff6a,Ibea95a6d into main

* changes:
  Provide onDeactivated method
  Set traceName automatically for Hydrator
parents 8c4f5b3f ff7425aa
Loading
Loading
Loading
Loading
+6 −8
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ abstract class ExclusiveActivatable : Activatable {
            awaitCancellation()
        } finally {
            isActive = false
            onDeactivated()
        }
    }

@@ -50,13 +51,7 @@ abstract class ExclusiveActivatable : Activatable {
     * Notifies that the [Activatable] has been activated.
     *
     * Serves as an entrypoint to kick off coroutine work that the object requires in order to keep
     * its state fresh and/or perform side-effects.
     *
     * The method suspends and doesn't return until all work required by the object is finished. In
     * most cases, it's expected for the work to remain ongoing forever so this method will forever
     * suspend its caller until the coroutine that called it is canceled.
     *
     * Implementations could follow this pattern:
     * its state fresh and/or perform side-effects. Implementations could follow this pattern:
     * ```kotlin
     * override suspend fun onActivated() {
     *     coroutineScope {
@@ -69,5 +64,8 @@ abstract class ExclusiveActivatable : Activatable {
     *
     * @see activate
     */
    protected abstract suspend fun onActivated()
    protected open suspend fun onActivated() {}

    /** Notifies that the [Activatable] has been deactivated. */
    protected open suspend fun onDeactivated() {}
}
+25 −4
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ abstract class HydratedActivatable(
            } finally {
                requestChannel?.cancel()
                requestChannel = null
                onDeactivated()
            }
        }
    }
@@ -69,10 +70,6 @@ abstract class HydratedActivatable(
     * Serves as an entrypoint to kick off coroutine work that the object requires in order to keep
     * its state fresh and/or perform side-effects.
     *
     * The method suspends and doesn't return until all work required by the object is finished. In
     * most cases, it's expected for the work to remain ongoing forever so this method will forever
     * suspend its caller until the coroutine that called it is canceled.
     *
     * Implementations could follow this pattern:
     * ```kotlin
     * override suspend fun onActivated() {
@@ -88,6 +85,9 @@ abstract class HydratedActivatable(
     */
    protected open suspend fun onActivated() {}

    /** Notifies that the [Activatable] has been deactivated. */
    protected open suspend fun onDeactivated() {}

    /**
     * Queues [block] for execution on the activated scope. Requests are executed sequentially.
     *
@@ -108,4 +108,25 @@ abstract class HydratedActivatable(
    /** @see [Hydrator.hydratedStateOf] */
    protected fun <T> Flow<T>.hydratedStateOf(traceName: String, initialValue: T): State<T> =
        hydrator.hydratedStateOf(traceName, initialValue, this)

    /**
     * Returns a [Hydrator.StateDelegateProvider] which will automatically set the [traceName]. Use
     * with the `by` keyword.
     *
     * Usage: `val myState by hydratedStateOf()`
     *
     * @see [Hydrator.hydratedStateOf]
     */
    protected fun <T> StateFlow<T>.hydratedStateOf() = hydrator.hydratedStateOf(this)

    /**
     * Returns a [Hydrator.StateDelegateProvider] which will automatically set the [traceName]. Use
     * with the `by` keyword.
     *
     * Usage: `val myState by hydratedStateOf(initialValue)`
     *
     * @see [Hydrator.hydratedStateOf]
     */
    protected fun <T> Flow<T>.hydratedStateOf(initialValue: T) =
        hydrator.hydratedStateOf(initialValue, this)
}
+59 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ import androidx.compose.runtime.snapshots.StateFactoryMarker
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.coroutines.traceCoroutine
import com.android.systemui.log.table.TableLogBuffer
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
@@ -140,4 +142,61 @@ class Hydrator(
    }

    private data class NamedActivatable(val traceName: String?, val activatable: Activatable)

    /**
     * Returns a snapshot [State] that's kept up-to-date as long as its owner is active. Returns
     * [StateDelegateProvider] which is used by the `by` keyword. It will automatically set the
     * traceName to be the property name.
     *
     * Usage: `val myState by hydrator.hydratedStateOf(initialValue, source)`
     *
     * @param initialValue The first value to place on the [State]
     * @param source The upstream [Flow] to collect from; values emitted to it will be automatically
     *   set on the returned [State].
     */
    fun <T> hydratedStateOf(initialValue: T, source: Flow<T>): StateDelegateProvider<T> {
        return StateDelegateProvider(initialValue, source)
    }

    /**
     * Returns a snapshot [State] that's kept up-to-date as long as its owner is active. Returns
     * [StateDelegateProvider] which is used by the `by` keyword. It will automatically set the
     * traceName to be the property name.
     *
     * Usage: `val myState by hydrator.hydratedStateOf(source)`
     *
     * @param initialValue The first value to place on the [State]
     * @param source The upstream [Flow] to collect from; values emitted to it will be automatically
     *   set on the returned [State].
     */
    fun <T> hydratedStateOf(source: StateFlow<T>): StateDelegateProvider<T> {
        return StateDelegateProvider(source.value, source)
    }

    inner class StateDelegateProvider<T>
    internal constructor(
        private val initialValue: T,
        private val sourceFlow: Flow<T>,
        private val explicitTraceName: String? = null,
    ) {
        operator fun provideDelegate(
            thisRef: Any?,
            property: KProperty<*>,
        ): ReadOnlyProperty<Any?, T> {
            val finalTraceName = explicitTraceName ?: property.name

            val internalState: State<T> =
                hydratedStateOf(
                    traceName = finalTraceName,
                    initialValue = initialValue,
                    source = sourceFlow,
                )

            return object : ReadOnlyProperty<Any?, T> {
                override fun getValue(thisRef: Any?, property: KProperty<*>): T {
                    return internalState.value
                }
            }
        }
    }
}
+6 −9
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.systemui.lifecycle

import kotlinx.coroutines.awaitCancellation

class FakeActivatable(
    private val onActivation: () -> Unit = {},
    private val onDeactivation: () -> Unit = {},
@@ -25,14 +23,13 @@ class FakeActivatable(
    var activationCount = 0
    var cancellationCount = 0

    override suspend fun onActivated(): Nothing {
    override suspend fun onActivated() {
        activationCount++
        onActivation()
        try {
            awaitCancellation()
        } finally {
    }

    override suspend fun onDeactivated() {
        cancellationCount++
        onDeactivation()
    }
}
}
+8 −12
Original line number Diff line number Diff line
@@ -16,9 +16,7 @@

package com.android.systemui.ui.viewmodel

import androidx.compose.runtime.getValue
import com.android.systemui.lifecycle.HydratedActivatable
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -34,22 +32,20 @@ class FakeHydratedViewModel(
    var activationCount = 0
    var cancellationCount = 0

    val stateBackedByFlow: Boolean by
        upstreamFlow.hydratedStateOf(traceName = "test", initialValue = true)
    val stateBackedByFlow by upstreamFlow.hydratedStateOf(initialValue = true)

    val stateBackedByStateFlow: Boolean by upstreamStateFlow.hydratedStateOf(traceName = "test")
    val stateBackedByStateFlow by upstreamStateFlow.hydratedStateOf()

    fun publicEnqueueOnActivatedScope(runnable: suspend () -> Unit) =
        enqueueOnActivatedScope(runnable)

    override suspend fun onActivated(): Nothing {
    override suspend fun onActivated() {
        activationCount++
        onActivation()
        try {
            awaitCancellation()
        } finally {
    }

    override suspend fun onDeactivated() {
        cancellationCount++
        onDeactivation()
    }
}
}