Skip to content

Commit

Permalink
Migrate to io.sellmair:evas
Browse files Browse the repository at this point in the history
  • Loading branch information
sellmair committed Sep 23, 2024
1 parent 2dca8d8 commit 800293b
Show file tree
Hide file tree
Showing 63 changed files with 205 additions and 668 deletions.
2 changes: 1 addition & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ data class LoginState(val email: String, val password: String, val isLoggedIn: B
}
}

fun CoroutineScope.launchLoginStateActor() = launchStateProducer(LoginState) {
fun CoroutineScope.launchLoginStateActor() = launchState(LoginState) {
var state = LoginState.default

collectEventsAsync<EmailChangedEvent> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.os.IBinder
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
import com.russhwolf.settings.Settings
import com.russhwolf.settings.SharedPreferencesSettings
import io.sellmair.evas.*
import io.sellmair.pacemaker.ble.AndroidBle
import io.sellmair.pacemaker.ble.Ble
import io.sellmair.pacemaker.bluetooth.HeartRateSensorBluetoothService
Expand All @@ -24,7 +25,7 @@ import kotlin.coroutines.CoroutineContext

class AndroidApplicationBackend : Service(), ApplicationBackend, CoroutineScope {

override val coroutineContext: CoroutineContext = Dispatchers.Main + SupervisorJob() + EventBus() + StateBus() +
override val coroutineContext: CoroutineContext = Dispatchers.Main + SupervisorJob() + Events() + States() +
AndroidContextProvider(this)

inner class MainServiceBinder(
Expand All @@ -34,13 +35,13 @@ class AndroidApplicationBackend : Service(), ApplicationBackend, CoroutineScope
override val sessionService: SessionService,
override val settings: Settings,
) : Binder(), ApplicationBackend {
override val eventBus get() = coroutineContext.eventBus
override val stateBus get() = coroutineContext.stateBus
override val events get() = coroutineContext.eventsOrThrow
override val states get() = coroutineContext.statesOrThrow
}

override val eventBus: EventBus = coroutineContext.eventBus
override val events: Events = coroutineContext.eventsOrThrow

override val stateBus: StateBus = coroutineContext.stateBus
override val states: States = coroutineContext.statesOrThrow

private val ble: Deferred<Ble> = async {
AndroidBle(this@AndroidApplicationBackend) ?: never()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
import io.sellmair.app.core.R
import io.sellmair.evas.flow
import io.sellmair.pacemaker.model.HeartRate
import io.sellmair.pacemaker.utils.get
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -84,7 +84,7 @@ class AndroidHeartRateNotification(private val service: Service) {
service, notificationId, createDefaultNotification().build(), foregroundServiceType
)

MeState.get().filterNotNull().collect { meState ->
MeState.flow().filterNotNull().collect { meState ->
update(
meState.heartRate ?: return@collect,
meState.heartRateLimit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import android.media.AudioFocusRequest
import android.media.AudioManager
import android.speech.tts.TextToSpeech
import androidx.core.content.getSystemService
import io.sellmair.pacemaker.utils.collectEvents
import io.sellmair.evas.collectEvents
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import android.os.CombinedVibration
import android.os.VibrationEffect
import android.os.VibratorManager
import androidx.core.content.getSystemService
import io.sellmair.pacemaker.utils.get
import io.sellmair.evas.value
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
Expand All @@ -16,7 +16,7 @@ internal fun CoroutineScope.launchVibrationWarningActor(context: Context) = laun
val vibratorManager = context.getSystemService<VibratorManager>() ?: return@launch
while (isActive) {
delay(1.seconds)
if (CriticalGroupState.get().value != null && UtteranceState.get().value >= UtteranceState.Warnings) {
if (CriticalGroupState.value() != null && UtteranceState.value() >= UtteranceState.Warnings) {
vibratorManager.vibrate(
CombinedVibration.createParallel(
VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)
Expand Down
18 changes: 11 additions & 7 deletions app-core/src/androidUnitTest/kotlin/GroupStateTest.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import io.sellmair.evas.Events
import io.sellmair.evas.States
import io.sellmair.evas.collect
import io.sellmair.evas.emit
import io.sellmair.evas.flow
import io.sellmair.evas.value
import io.sellmair.pacemaker.*
import io.sellmair.pacemaker.bluetooth.HeartRateMeasurementEvent
import io.sellmair.pacemaker.model.HeartRate
Expand All @@ -9,7 +15,6 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.*
import kotlinx.datetime.Clock
import utils.createInMemoryDatabase
import kotlin.coroutines.CoroutineContext
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.fail
Expand All @@ -27,11 +32,10 @@ class GroupStateTest {
HeartRateMeasurementEvent(HeartRate(128f), meSensorId, Clock.System.now()).emit()

launch {
GroupState.get().collect { println("(${testScheduler.currentTime}ms) s: $it") }
GroupState.collect { println("(${testScheduler.currentTime}ms) s: $it") }
}


val state = GroupState.get().first { it.members.isNotEmpty() }
val state = GroupState.flow().first { it.members.isNotEmpty() }
println("(${testScheduler.currentTime}ms) Received first state with non-empty members")

assertEquals(
Expand All @@ -51,12 +55,12 @@ class GroupStateTest {

println("(${testScheduler.currentTime}ms) Testing current state value...")
assertEquals(
state, GroupState.get().value,
state, GroupState.value(),
"(${testScheduler.currentTime}ms) Expect the current value of 'GroupState' to be the recently emitted state"
)

println("(${testScheduler.currentTime}ms) Waiting until the Group state resets..")
GroupState.get().first { it == GroupState.default }
GroupState.flow().first { it == GroupState.default }

println("(${testScheduler.currentTime}ms) Group state was reset!")
assertEquals(
Expand All @@ -66,7 +70,7 @@ class GroupStateTest {
)
}

private fun test(block: suspend TestScope.() -> Unit) = runTest(EventBus() + StateBus()) {
private fun test(block: suspend TestScope.() -> Unit) = runTest(Events() + States()) {
try {
launchGroupStateActor(userService, actorContext = currentCoroutineContext())
block()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package io.sellmair.pacemaker

import com.russhwolf.settings.Settings
import io.sellmair.evas.Events
import io.sellmair.evas.States
import io.sellmair.pacemaker.bluetooth.HeartRateSensorBluetoothService
import io.sellmair.pacemaker.bluetooth.PacemakerBluetoothService
import io.sellmair.pacemaker.utils.EventBus
import io.sellmair.pacemaker.utils.StateBus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred

Expand All @@ -13,8 +13,8 @@ interface ApplicationBackend {
val heartRateSensorBluetoothService: Deferred<HeartRateSensorBluetoothService>
val sessionService: SessionService
val userService: UserService
val stateBus: StateBus
val eventBus: EventBus
val states: States
val events: Events
val settings: Settings
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package io.sellmair.pacemaker

import com.russhwolf.settings.Settings
import com.russhwolf.settings.set
import io.sellmair.pacemaker.utils.*
import io.sellmair.evas.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlin.time.Duration.Companion.INFINITE
Expand Down Expand Up @@ -45,7 +45,7 @@ sealed class ApplicationFeatureEvent : Event {
data class Toggle(val feature: ApplicationFeature) : ApplicationFeatureEvent()
}

internal fun CoroutineScope.launchApplicationFeatureActor(settings: Settings) = launchStateProducer(
internal fun CoroutineScope.launchApplicationFeatureActor(settings: Settings) = launchState(
coroutineContext = Dispatchers.Main.immediate,
keepActive = INFINITE
) { key: ApplicationFeatureState.Key ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.sellmair.pacemaker

import io.sellmair.pacemaker.utils.State
import io.sellmair.pacemaker.utils.launchStateProducer
import io.sellmair.evas.State
import io.sellmair.evas.launchState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
Expand All @@ -18,7 +18,7 @@ data class BluetoothState(
}
}

internal fun CoroutineScope.launchBluetoothStateActor(): Job = launchStateProducer(BluetoothState) {
internal fun CoroutineScope.launchBluetoothStateActor(): Job = launchState(BluetoothState) {
val isBluetoothEnabledFlow = flow {
while (true) {
val isBluetoothEnabled = isBluetoothEnabled()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package io.sellmair.pacemaker

import io.sellmair.pacemaker.utils.State
import io.sellmair.pacemaker.utils.emit
import io.sellmair.pacemaker.utils.get
import io.sellmair.pacemaker.utils.launchStateProducer
import io.sellmair.evas.State
import io.sellmair.evas.collect
import io.sellmair.evas.launchState
import kotlinx.coroutines.CoroutineScope

data class CriticalGroupState(val criticalMembers: List<UserState>) : State {
Expand All @@ -12,8 +11,8 @@ data class CriticalGroupState(val criticalMembers: List<UserState>) : State {
}
}

internal fun CoroutineScope.launchCriticalGroupStateActor() = launchStateProducer(CriticalGroupState) {
GroupState.get().collect { groupState ->
internal fun CoroutineScope.launchCriticalGroupStateActor() = launchState(CriticalGroupState) {
GroupState.collect { groupState ->
val criticalMembers = groupState.members.filter { userState ->
val heartRateLimit = userState.heartRateLimit ?: return@filter false
userState.heartRate > heartRateLimit
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sellmair.pacemaker

import io.sellmair.evas.*
import io.sellmair.pacemaker.ActorIn.AddMeasurement
import io.sellmair.pacemaker.bluetooth.HeartRateMeasurementEvent
import io.sellmair.pacemaker.bluetooth.PacemakerBroadcastPackageEvent
Expand Down Expand Up @@ -61,7 +62,7 @@ internal fun CoroutineScope.launchGroupStateActor(
}

/* Main actor */
launchStateProducer(GroupState.Key, actorContext) {
launchState(GroupState.Key, actorContext) {
actorIn.consumeEach { event ->
when (event) {
is AddMeasurement -> {
Expand Down Expand Up @@ -113,7 +114,7 @@ internal fun CoroutineScope.launchGroupStateActor(

/* Listen for changes of the current users color */
launch(actorContext) {
MeColorState.get().filterNotNull().collect { meColor ->
MeColorState.flow().filterNotNull().collect { meColor ->
colors[meUserId.await()] = meColor.color
actorIn.send(ActorIn.RecalculateGroup)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import io.sellmair.pacemaker.bluetooth.HeartRateSensor
import io.sellmair.pacemaker.bluetooth.HeartRateSensorBluetoothService
import io.sellmair.pacemaker.bluetooth.toHeartRateSensorId
import io.sellmair.pacemaker.model.HeartRateSensorId
import io.sellmair.pacemaker.utils.Event
import io.sellmair.pacemaker.utils.State
import io.sellmair.pacemaker.utils.collectEventsAsync
import io.sellmair.pacemaker.utils.launchStateProducer
import io.sellmair.evas.Event
import io.sellmair.evas.State
import io.sellmair.evas.collectEventsAsync
import io.sellmair.evas.launchState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -46,7 +46,7 @@ sealed class HeartRateSensorConnectionIntent : Event {
internal fun CoroutineScope.launchHeartRateSensorConnectionStateActor(
userService: UserService,
bluetoothService: Deferred<HeartRateSensorBluetoothService>
) = launchStateProducer(Dispatchers.Main.immediate, keepActive = 1.minutes) { key: HeartRateSensorConnectionState.Key ->
) = launchState(Dispatchers.Main.immediate, keepActive = 1.minutes) { key: HeartRateSensorConnectionState.Key ->
bluetoothService.await().withHeartRateSensor(key.sensorId) { sensor ->
if (sensor == null) return@withHeartRateSensor
launchHeartRateSensorConnectionIntentActor(userService, sensor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import io.sellmair.pacemaker.bluetooth.toHeartRateSensorId
import io.sellmair.pacemaker.model.HeartRate
import io.sellmair.pacemaker.model.HeartRateSensorId
import io.sellmair.pacemaker.model.User
import io.sellmair.pacemaker.utils.State
import io.sellmair.pacemaker.utils.launchStateProducer
import io.sellmair.evas.State
import io.sellmair.evas.launchState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand Down Expand Up @@ -47,7 +47,7 @@ data class HeartRateSensorState(
internal fun CoroutineScope.launchHeartRateSensorStateActor(
userService: UserService,
bluetoothService: Deferred<HeartRateSensorBluetoothService>
) = launchStateProducer(keepActive = 1.minutes) { key: HeartRateSensorState.Key ->
) = launchState(keepActive = 1.minutes) { key: HeartRateSensorState.Key ->
bluetoothService.await().withHeartRateSensor(key.sensorInfo.id) { sensor ->
if (sensor == null) return@withHeartRateSensor key.default.emit()

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package io.sellmair.pacemaker

import androidx.compose.runtime.Immutable
import io.sellmair.evas.State
import io.sellmair.evas.launchState
import io.sellmair.pacemaker.bluetooth.HeartRateSensorBluetoothService
import io.sellmair.pacemaker.bluetooth.toHeartRateSensorId
import io.sellmair.pacemaker.model.HeartRateSensorId
import io.sellmair.pacemaker.utils.State
import io.sellmair.pacemaker.utils.launchStateProducer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.flow.emitAll
Expand All @@ -27,7 +27,7 @@ data class HeartRateSensorsState(val nearbySensors: List<HeartRateSensorInfo>) :

internal fun CoroutineScope.launchHeartRateSensorsStateActor(
bluetoothService: Deferred<HeartRateSensorBluetoothService>
) = launchStateProducer(HeartRateSensorsState) {
) = launchState(HeartRateSensorsState) {
emitAll(bluetoothService.await()
.allSensorsNearby
.map { sensors ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package io.sellmair.pacemaker

import com.russhwolf.settings.Settings
import com.russhwolf.settings.set
import io.sellmair.pacemaker.utils.*
import io.sellmair.evas.Event
import io.sellmair.evas.State
import io.sellmair.evas.collectEvents
import io.sellmair.evas.launchState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

Expand All @@ -18,7 +21,7 @@ sealed class MeColorIntent : Event {

internal fun CoroutineScope.launchMeColorStateActor(
settings: Settings
) = launchStateProducer(MeColorState, coroutineContext = Dispatchers.Main.immediate) {
) = launchState(MeColorState, context = Dispatchers.Main.immediate) {
val initialHue = settings.storedUserHue ?: UserColors.defaultHue(settings.meId)
MeColorState(UserColors.fromHue(initialHue)).emit()

Expand All @@ -28,12 +31,12 @@ internal fun CoroutineScope.launchMeColorStateActor(
}
}

private const val hueSetingsKey = "me.hue"
private const val hueSettingsKey = "me.hue"

private var Settings.storedUserHue: Float?
get() = getFloatOrNull(hueSetingsKey)
get() = getFloatOrNull(hueSettingsKey)
set(value) {
if (value != null) set(hueSetingsKey, value)
else remove(hueSetingsKey)
if (value != null) set(hueSettingsKey, value)
else remove(hueSettingsKey)
}

Loading

0 comments on commit 800293b

Please sign in to comment.