1 /*
<lambda>null2 * Copyright 2023 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 @file:JvmName("SnapshotLongStateKt")
18 @file:JvmMultifileClass
19
20 package androidx.compose.runtime
21
22 import androidx.compose.runtime.internal.JvmDefaultWithCompatibility
23 import androidx.compose.runtime.snapshots.AutoboxingStateValueProperty
24 import androidx.compose.runtime.snapshots.GlobalSnapshot
25 import androidx.compose.runtime.snapshots.Snapshot
26 import androidx.compose.runtime.snapshots.SnapshotId
27 import androidx.compose.runtime.snapshots.SnapshotMutableState
28 import androidx.compose.runtime.snapshots.StateFactoryMarker
29 import androidx.compose.runtime.snapshots.StateObjectImpl
30 import androidx.compose.runtime.snapshots.StateRecord
31 import androidx.compose.runtime.snapshots.currentSnapshot
32 import androidx.compose.runtime.snapshots.overwritable
33 import androidx.compose.runtime.snapshots.readable
34 import androidx.compose.runtime.snapshots.toSnapshotId
35 import androidx.compose.runtime.snapshots.withCurrent
36 import kotlin.jvm.JvmMultifileClass
37 import kotlin.jvm.JvmName
38 import kotlin.reflect.KProperty
39
40 /**
41 * Return a new [MutableLongState] initialized with the passed in [value]
42 *
43 * The MutableLongState class is a single value holder whose reads and writes are observed by
44 * Compose. Additionally, writes to it are transacted as part of the [Snapshot] system. On the JVM,
45 * values are stored in memory as the primitive `long` type, avoiding the autoboxing that occurs
46 * when using `MutableState<Long>`.
47 *
48 * @param value the initial value for the [MutableLongState]
49 * @see LongState
50 * @see MutableLongState
51 * @see mutableStateOf
52 * @see mutableIntStateOf
53 * @see mutableFloatStateOf
54 * @see mutableDoubleStateOf
55 */
56 @StateFactoryMarker
57 fun mutableLongStateOf(value: Long): MutableLongState = createSnapshotMutableLongState(value)
58
59 /**
60 * A value holder where reads to the [longValue] property during the execution of a [Composable]
61 * function cause the current [RecomposeScope] to subscribe to changes of that value.
62 *
63 * @see MutableLongState
64 * @see mutableDoubleStateOf
65 */
66 @Stable
67 @JvmDefaultWithCompatibility
68 interface LongState : State<Long> {
69 @get:AutoboxingStateValueProperty("longValue")
70 override val value: Long
71 @Suppress("AutoBoxing") get() = longValue
72
73 val longValue: Long
74 }
75
76 /** Permits property delegation of `val`s using `by` for [LongState]. */
77 @Suppress("NOTHING_TO_INLINE")
getValuenull78 inline operator fun LongState.getValue(thisObj: Any?, property: KProperty<*>): Long = longValue
79
80 /**
81 * A value holder where reads to the [longValue] property during the execution of a [Composable]
82 * function cause the current [RecomposeScope] to subscribe to changes of that value. When the
83 * [longValue] property is written to and changed, a recomposition of any subscribed
84 * [RecomposeScope]s will be scheduled. If [longValue] is written to with the same value, no
85 * recompositions will be scheduled.
86 *
87 * @see [LongState]
88 * @see [mutableDoubleStateOf]
89 */
90 @Stable
91 @JvmDefaultWithCompatibility
92 interface MutableLongState : LongState, MutableState<Long> {
93 @get:AutoboxingStateValueProperty("longValue")
94 @set:AutoboxingStateValueProperty("longValue")
95 override var value: Long
96 @Suppress("AutoBoxing") get() = longValue
97 set(value) {
98 longValue = value
99 }
100
101 override var longValue: Long
102 }
103
104 /** Permits property delegation of `var`s using `by` for [MutableLongState]. */
105 @Suppress("NOTHING_TO_INLINE")
setValuenull106 inline operator fun MutableLongState.setValue(thisObj: Any?, property: KProperty<*>, value: Long) {
107 longValue = value
108 }
109
createSnapshotMutableLongStatenull110 internal expect fun createSnapshotMutableLongState(value: Long): MutableLongState
111
112 /**
113 * A single value holder whose reads and writes are observed by Compose.
114 *
115 * Additionally, writes to it are transacted as part of the [Snapshot] system.
116 *
117 * @param value the wrapped value
118 * @see [mutableDoubleStateOf]
119 */
120 internal open class SnapshotMutableLongStateImpl(value: Long) :
121 StateObjectImpl(), MutableLongState, SnapshotMutableState<Long> {
122
123 private var next =
124 currentSnapshot().let { snapshot ->
125 LongStateStateRecord(snapshot.snapshotId, value).also {
126 if (snapshot !is GlobalSnapshot) {
127 it.next =
128 LongStateStateRecord(Snapshot.PreexistingSnapshotId.toSnapshotId(), value)
129 }
130 }
131 }
132
133 override val firstStateRecord: StateRecord
134 get() = next
135
136 override var longValue: Long
137 get() = next.readable(this).value
138 set(value) =
139 next.withCurrent {
140 if (it.value != value) {
141 next.overwritable(this, it) { this.value = value }
142 }
143 }
144
145 // Arbitrary policies are not allowed. The underlying `==` implementation
146 // for primitive types corresponds to structural equality
147 override val policy: SnapshotMutationPolicy<Long>
148 get() = structuralEqualityPolicy()
149
150 override fun component1(): Long = longValue
151
152 override fun component2(): (Long) -> Unit = { longValue = it }
153
154 override fun prependStateRecord(value: StateRecord) {
155 next = value as LongStateStateRecord
156 }
157
158 override fun mergeRecords(
159 previous: StateRecord,
160 current: StateRecord,
161 applied: StateRecord
162 ): StateRecord? {
163 val currentRecord = current as LongStateStateRecord
164 val appliedRecord = applied as LongStateStateRecord
165 return if (currentRecord.value == appliedRecord.value) {
166 current
167 } else {
168 null
169 }
170 }
171
172 override fun toString(): String =
173 next.withCurrent { "MutableLongState(value=${it.value})@${hashCode()}" }
174
175 private class LongStateStateRecord(snapshotId: SnapshotId, var value: Long) :
176 StateRecord(snapshotId) {
177 override fun assign(value: StateRecord) {
178 this.value = (value as LongStateStateRecord).value
179 }
180
181 override fun create(): StateRecord = create(currentSnapshot().snapshotId)
182
183 override fun create(snapshotId: SnapshotId): StateRecord =
184 LongStateStateRecord(snapshotId, value)
185 }
186 }
187