diff --git a/coroutines/compose/build.gradle b/coroutines/compose/build.gradle
index 7405e30efa0..898c2da2de9 100644
--- a/coroutines/compose/build.gradle
+++ b/coroutines/compose/build.gradle
@@ -16,5 +16,19 @@ kotlin {
api project(":micro_utils.common.compose")
}
}
+// androidUnitTest {
+// dependencies {
+// implementation libs.ui.test.junit4
+// implementation libs.ui.test.manifest
+// implementation libs.android.compose.material3
+// }
+// }
+// jvmTest {
+// dependencies {
+// implementation libs.ui.test.junit4
+// implementation libs.ui.test.manifest
+// implementation libs.android.compose.material3
+// }
+// }
}
}
diff --git a/coroutines/compose/src/androidMain/AndroidManifest.xml b/coroutines/compose/src/androidMain/AndroidManifest.xml
deleted file mode 100644
index 9b65eb06cff..00000000000
--- a/coroutines/compose/src/androidMain/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowState.kt b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowState.kt
deleted file mode 100644
index c3729ac32cf..00000000000
--- a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowState.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package dev.inmo.micro_utils.coroutines.compose
-
-import androidx.compose.runtime.MutableState
-import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-
-/**
- * This type works like [MutableState], [kotlinx.coroutines.flow.StateFlow] and [kotlinx.coroutines.flow.MutableSharedFlow].
- * Based on [SpecialMutableStateFlow]
- */
-@Deprecated("Will be removed soon")
-class FlowState(
- initial: T,
- internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
-) : MutableState,
- SpecialMutableStateFlow(initial, internalScope) {
- private var internalValue: T = initial
- override var value: T
- get() = internalValue
- set(value) {
- internalValue = value
- tryEmit(value)
- }
-
- override fun onChangeWithoutSync(value: T) {
- internalValue = value
- super.onChangeWithoutSync(value)
- }
-
- override fun component1(): T = value
-
- override fun component2(): (T) -> Unit = { tryEmit(it) }
-
- override fun tryEmit(value: T): Boolean {
- internalValue = value
- return super.tryEmit(value)
- }
-
- override suspend fun emit(value: T) {
- internalValue = value
- super.emit(value)
- }
-}
-
-//fun MutableState.asFlowState(scope: CoroutineScope = CoroutineScope(Dispatchers.Main)) = FlowState(this, scope)
diff --git a/coroutines/compose/src/jvmTest/kotlin/FlowStateTests.kt b/coroutines/compose/src/jvmTest/kotlin/FlowStateTests.kt
new file mode 100644
index 00000000000..2f4ad81e9f0
--- /dev/null
+++ b/coroutines/compose/src/jvmTest/kotlin/FlowStateTests.kt
@@ -0,0 +1,24 @@
+import androidx.compose.material.Button
+import androidx.compose.material.Text
+import androidx.compose.runtime.collectAsState
+import androidx.compose.ui.test.*
+import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
+import org.jetbrains.annotations.TestOnly
+import kotlin.test.Test
+
+class FlowStateTests {
+ @OptIn(ExperimentalTestApi::class)
+ @Test
+ @TestOnly
+ fun simpleTest() = runComposeUiTest {
+ val flowState = SpecialMutableStateFlow(0)
+ setContent {
+ Button({ flowState.value++ }) { Text("Click") }
+ Text(flowState.collectAsState().value.toString())
+ }
+
+ onNodeWithText(0.toString()).assertExists()
+ onNodeWithText("Click").performClick()
+ onNodeWithText(1.toString()).assertExists()
+ }
+}
\ No newline at end of file
diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt
index ac892987fc9..5114da6db10 100644
--- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt
+++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt
@@ -18,75 +18,49 @@ import kotlin.coroutines.CoroutineContext
* each active subscriber
*/
open class SpecialMutableStateFlow(
- initialValue: T,
- internalScope: CoroutineScope
+ initialValue: T
) : MutableStateFlow, FlowCollector, MutableSharedFlow {
@OptIn(InternalCoroutinesApi::class)
private val syncObject = SynchronizedObject()
- protected val internalSharedFlow: MutableSharedFlow = MutableSharedFlow(
- replay = 0,
- extraBufferCapacity = 2,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
- )
- protected val publicSharedFlow: MutableSharedFlow = MutableSharedFlow(
+ protected val sharingFlow: MutableSharedFlow = MutableSharedFlow(
replay = 1,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
- protected var _value: T = initialValue
- override var value: T
- get() = _value
+ @OptIn(InternalCoroutinesApi::class)
+ override var value: T = initialValue
set(value) {
- internalSharedFlow.tryEmit(value)
+ synchronized(syncObject) {
+ if (field != value) {
+ field = value
+ sharingFlow.tryEmit(value)
+ }
+ }
}
- protected val job = internalSharedFlow.subscribe(internalScope) {
- doOnChangeAction(it)
- }
override val replayCache: List
- get() = publicSharedFlow.replayCache
+ get() = sharingFlow.replayCache
override val subscriptionCount: StateFlow
- get() = publicSharedFlow.subscriptionCount
+ get() = sharingFlow.subscriptionCount
- constructor(
- initialValue: T,
- internalContext: CoroutineContext = Dispatchers.Default
- ) : this(initialValue, CoroutineScope(internalContext))
-
- @OptIn(InternalCoroutinesApi::class)
override fun compareAndSet(expect: T, update: T): Boolean {
- return synchronized(syncObject) {
- if (expect == _value && update != _value) {
- doOnChangeAction(update)
- }
- expect == _value
- }
- }
-
- protected open fun onChangeWithoutSync(value: T) {
- _value = value
- publicSharedFlow.tryEmit(value)
- }
- @OptIn(InternalCoroutinesApi::class)
- protected open fun doOnChangeAction(value: T) {
- synchronized(syncObject) {
- if (_value != value) {
- onChangeWithoutSync(value)
- }
+ if (expect == value) {
+ value = update
}
+ return expect == value
}
@ExperimentalCoroutinesApi
- override fun resetReplayCache() = publicSharedFlow.resetReplayCache()
+ override fun resetReplayCache() = sharingFlow.resetReplayCache()
override fun tryEmit(value: T): Boolean {
- return internalSharedFlow.tryEmit(value)
+ return compareAndSet(this.value, value)
}
override suspend fun emit(value: T) {
- internalSharedFlow.emit(value)
+ compareAndSet(this.value, value)
}
- override suspend fun collect(collector: FlowCollector) = publicSharedFlow.collect(collector)
+ override suspend fun collect(collector: FlowCollector) = sharingFlow.collect(collector)
}
diff --git a/coroutines/src/commonTest/kotlin/SpecialMutableStateFlowTests.kt b/coroutines/src/commonTest/kotlin/SpecialMutableStateFlowTests.kt
index 36501bdf2e7..8e3aa105621 100644
--- a/coroutines/src/commonTest/kotlin/SpecialMutableStateFlowTests.kt
+++ b/coroutines/src/commonTest/kotlin/SpecialMutableStateFlowTests.kt
@@ -1,35 +1,33 @@
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
+import dev.inmo.micro_utils.coroutines.asDeferred
import dev.inmo.micro_utils.coroutines.subscribe
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
+import kotlin.test.assertTrue
class SpecialMutableStateFlowTests {
@Test
- fun simpleTest() {
+ fun simpleTest() = runTest {
val specialMutableStateFlow = SpecialMutableStateFlow(0)
- runTest {
- specialMutableStateFlow.value = 1
- specialMutableStateFlow.first { it == 1 }
- }
+ specialMutableStateFlow.value = 1
+ specialMutableStateFlow.first { it == 1 }
assertEquals(1, specialMutableStateFlow.value)
}
@Test
- fun specialTest() {
+ fun specialTest() = runTest {
val specialMutableStateFlow = SpecialMutableStateFlow(0)
- runTest {
- lateinit var subscriberJob: Job
- subscriberJob = specialMutableStateFlow.subscribe(this) {
- when (it) {
- 1 -> specialMutableStateFlow.value = 2
- 2 -> subscriberJob.cancel()
- }
+ lateinit var subscriberJob: Job
+ subscriberJob = specialMutableStateFlow.subscribe(this) {
+ when (it) {
+ 1 -> specialMutableStateFlow.value = 2
+ 2 -> subscriberJob.cancel()
}
- specialMutableStateFlow.value = 1
- subscriberJob.join()
}
+ specialMutableStateFlow.value = 1
+ subscriberJob.join()
assertEquals(2, specialMutableStateFlow.value)
}
}
\ No newline at end of file
diff --git a/mppProjectWithSerializationAndCompose.gradle b/mppProjectWithSerializationAndCompose.gradle
index a9be6b2cd7c..31bc1e0a5dc 100644
--- a/mppProjectWithSerializationAndCompose.gradle
+++ b/mppProjectWithSerializationAndCompose.gradle
@@ -28,8 +28,8 @@ kotlin {
commonMain {
dependencies {
implementation kotlin('stdlib')
- api libs.kt.serialization
implementation compose.runtime
+ api libs.kt.serialization
}
}
commonTest {
@@ -47,6 +47,7 @@ kotlin {
jvmTest {
dependencies {
implementation kotlin('test-junit')
+ implementation compose.uiTest
}
}
jsMain {
@@ -61,6 +62,14 @@ kotlin {
}
}
androidUnitTest {
+ dependencies {
+ implementation kotlin('test-junit')
+ implementation libs.android.test.junit
+ implementation libs.android.espresso
+ implementation compose.uiTest
+ }
+ }
+ androidInstrumentedTest {
dependencies {
implementation kotlin('test-junit')
implementation libs.android.test.junit