• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.kairos
18 
19 import com.android.systemui.kairos.internal.IncrementalImpl
20 import com.android.systemui.kairos.internal.constInit
21 import com.android.systemui.kairos.internal.init
22 import com.android.systemui.kairos.internal.mapImpl
23 import com.android.systemui.kairos.internal.switchDeferredImplSingle
24 import com.android.systemui.kairos.internal.switchPromptImplSingle
25 import com.android.systemui.kairos.util.mapPatchFromFullDiff
26 
27 /**
28  * Returns an [Events] that switches to the [Events] contained within this [State] whenever it
29  * changes.
30  *
31  * This switch does take effect until the *next* transaction after [State] changes. For a switch
32  * that takes effect immediately, see [switchEventsPromptly].
33  *
34  * @sample com.android.systemui.kairos.KairosSamples.switchEvents
35  */
36 @ExperimentalKairosApi
37 fun <A> State<Events<A>>.switchEvents(): Events<A> {
38     val patches =
39         mapImpl({ init.connect(this).changes }) { newEvents, _ -> newEvents.init.connect(this) }
40     return EventsInit(
41         constInit(
42             name = null,
43             switchDeferredImplSingle(
44                 getStorage = {
45                     init.connect(this).getCurrentWithEpoch(this).first.init.connect(this)
46                 },
47                 getPatches = { patches },
48             ),
49         )
50     )
51 }
52 
53 /**
54  * Returns an [Events] that switches to the [Events] contained within this [State] whenever it
55  * changes.
56  *
57  * This switch takes effect immediately within the same transaction that [State] changes. If the
58  * newly-switched-in [Events] is emitting a value within this transaction, then that value will be
59  * emitted from this switch. If not, but the previously-switched-in [Events] *is* emitting, then
60  * that value will be emitted from this switch instead. Otherwise, there will be no emission.
61  *
62  * In general, you should prefer [switchEvents] over this method. It is both safer and more
63  * performant.
64  *
65  * @sample com.android.systemui.kairos.KairosSamples.switchEventsPromptly
66  */
67 // TODO: parameter to handle coincidental emission from both old and new
68 @ExperimentalKairosApi
switchEventsPromptlynull69 fun <A> State<Events<A>>.switchEventsPromptly(): Events<A> {
70     val patches =
71         mapImpl({ init.connect(this).changes }) { newEvents, _ -> newEvents.init.connect(this) }
72     return EventsInit(
73         constInit(
74             name = null,
75             switchPromptImplSingle(
76                 getStorage = {
77                     init.connect(this).getCurrentWithEpoch(this).first.init.connect(this)
78                 },
79                 getPatches = { patches },
80             ),
81         )
82     )
83 }
84 
85 /** Returns an [Incremental] that behaves like current value of this [State]. */
switchIncrementalnull86 fun <K, V> State<Incremental<K, V>>.switchIncremental(): Incremental<K, V> {
87     val stateChangePatches =
88         transitions.mapNotNull { (old, new) ->
89             mapPatchFromFullDiff(old.sample(), new.sample()).takeIf { it.isNotEmpty() }
90         }
91     val innerChanges =
92         map { inner ->
93                 merge(stateChangePatches, inner.updates) { switchPatch, upcomingPatch ->
94                     switchPatch + upcomingPatch
95                 }
96             }
97             .switchEventsPromptly()
98     val flattened = flatten()
99     return IncrementalInit(
100         init("switchIncremental") {
101             val upstream = flattened.init.connect(this)
102             IncrementalImpl(
103                 "switchIncremental",
104                 "switchIncremental",
105                 upstream.changes,
106                 innerChanges.init.connect(this),
107                 upstream.store,
108             )
109         }
110     )
111 }
112