reactormonk
12/03/2025, 6:31 PMreactormonk
12/03/2025, 6:32 PMreactormonk
12/03/2025, 6:32 PMIvan Kubyshkin [JetBrains]
12/04/2025, 9:13 AMreactormonk
12/04/2025, 9:13 AMreactormonk
12/04/2025, 9:36 AMreactormonk
12/04/2025, 9:49 AMsuspend fun requestAutoCardScan(delayMs: Byte, cardType: Byte, enabled: Boolean = true) {
Log.d(TAG, "Automatic search for cards requested: $enabled, state: $autoCardScanEnabled")
autoCardScanLock.withLock {
if (autoCardScanEnabled != enabled) {
val response = suspendCancellableCoroutine { continuation ->
sendCommand(Command.AutoCardSearch(continuation, enabled, delayMs, cardType))
}
if (!response.isSuccessful() && enabled) {
autoCardScanEnabled = false
throw Exception("Auto card scan request failed: ${response.error.dump()}")
} else {
// somehow disabling it always returns a fail
autoCardScanEnabled = enabled
}
delay(100.milliseconds)
} else {
Timber.d { "Card Scan already in requested state" }
}
}
}Ivan Kubyshkin [JetBrains]
12/04/2025, 9:52 AMautoCardScanLock ?reactormonk
12/04/2025, 9:52 AMreactormonk
12/04/2025, 9:52 AMprivate val autoCardScanLock = Mutex()reactormonk
12/04/2025, 9:56 AMreactormonk
12/04/2025, 9:56 AMimport android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.ObsoleteCoroutinesApi
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ClosedSendChannelException
import kotlinx.coroutines.channels.consumeEach
import kotlinx.coroutines.channels.onFailure
import kotlinx.coroutines.channels.ticker
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.runningFold
import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.select
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import me.getreach.compilerbug.ui.theme.CompilerBugTheme
import kotlin.coroutines.cancellation.CancellationException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.time.Duration.Companion.milliseconds
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
CompilerBugTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
CompilerBugTheme {
Greeting("Android")
}
}
@OptIn(DelicateCoroutinesApi::class, ObsoleteCoroutinesApi::class)
data class MessageProcessor(val job: Job) {
val TAG = "MessageProcessor"
private val requests = Channel<Command>(100, BufferOverflow.DROP_OLDEST, onUndeliveredElement = { it.callback.cancel(
CancellationException("Too many requests have been stacked.")
)})
fun sendCommand(command: Command) {
requests.trySend(command).onFailure {
print(it)
}
}
private var autoCardScanEnabled = false
private val autoCardScanLock = Mutex()
suspend fun requestAutoCardScan(delayMs: Byte, cardType: Byte, enabled: Boolean = true) {
Log.d(TAG, "Automatic search for cards requested: $enabled, state: $autoCardScanEnabled")
autoCardScanLock.withLock {
if (autoCardScanEnabled != enabled) {
val response = suspendCancellableCoroutine { continuation ->
sendCommand(Command.PingCard(continuation, 0b00))
}
if (!response.isSuccessful() && enabled) {
autoCardScanEnabled = false
throw Exception("Auto card scan request failed: ${response.error}")
} else {
// somehow disabling it always returns a fail
autoCardScanEnabled = enabled
}
delay(100.milliseconds)
} else {
println("Card Scan already in requested state")
}
}
}
suspend fun sendCloseRf() {
requestAutoCardScan(0, 0, false)
}
suspend fun onMessage(message: ParsingState.ParsingSuccessful) {
}
}
sealed class Command {
val channel: Channel<ParsingState.ParsingSuccessful> = Channel(1)
abstract val callback: CancellableContinuation<ParsingState.ParsingSuccessful>
abstract val commandByte: Byte
abstract fun toBytes(comByteManager: ComByteManager): ByteArray
data class PingCard(
override val callback: CancellableContinuation<ParsingState.ParsingSuccessful>,
val cardType: Byte,
): Command() {
override val commandByte: Byte = 0b00
override fun toBytes(comByteManager: ComByteManager): ByteArray = byteArrayOf()
}
}
sealed interface ParsingState {
data class ParsingInProgress(val cmd: Byte, val data: ByteArray, val error: ByteArray, val numberOfFrames: Byte, val frameNumber: Byte): ParsingState
data class ParsingSuccessful(val cmd: Byte, val data: ByteArray, val error: ByteArray): ParsingState {
fun isSuccessful(): Boolean {
return error[0] == 0x90.toByte()
}
}
data object NoData: ParsingState
sealed class ParsingFailure(override val message: String?): Exception(message), ParsingState
data class WrongSerialNumber(val expected: Int, val got: Byte): ParsingFailure("Wrong serial number. Got: $got, expected: $expected")
data class MessageTooShort(val expected: Int, val got: Int): ParsingFailure("Message too short. Got: $got, expected: $expected")
data class InvalidFrameTag(val got: Byte): ParsingFailure("Invalid frame tag. Got: $got")
data class NoPriorStartMessage(val got: Byte): ParsingFailure("Not in ParsingInProgress, but got a follow-up frame: $got")
}
data class ComByteManager(var isBle5: Boolean = false) { }reactormonk
12/04/2025, 10:00 AMwithLock call 🤔reactormonk
12/04/2025, 10:02 AMsuspend fun requestAutoCardScan(delayMs: Byte, cardType: Byte, enabled: Boolean = true) {
Log.d(TAG, "Automatic search for cards requested: $enabled, state: $autoCardScanEnabled")
// autoCardScanLock.withLock {
if (autoCardScanEnabled != enabled) {
val response = suspendCancellableCoroutine { continuation ->
sendCommand(Command.AutoCardSearch(continuation, enabled, delayMs, cardType))
}
// if (!response.isSuccessful() && enabled) {
// autoCardScanEnabled = false
// throw Exception("Auto card scan request failed: ${response.error.dump()}")
// } else {
// // somehow disabling it always returns a fail
// autoCardScanEnabled = enabled
// }
// delay(100.milliseconds)
} else {
Timber.d { "Card Scan already in requested state" }
}
// }
}reactormonk
12/04/2025, 10:03 AMsuspend fun requestAutoCardScan(delayMs: Byte, cardType: Byte, enabled: Boolean = true) {
Log.d(TAG, "Automatic search for cards requested: $enabled, state: $autoCardScanEnabled")
autoCardScanLock.withLock {
if (autoCardScanEnabled != enabled) {
val response = suspendCancellableCoroutine { continuation ->
sendCommand(Command.AutoCardSearch(continuation, enabled, delayMs, cardType))
}
// if (!response.isSuccessful() && enabled) {
// autoCardScanEnabled = false
// throw Exception("Auto card scan request failed: ${response.error.dump()}")
// } else {
// // somehow disabling it always returns a fail
// autoCardScanEnabled = enabled
// }
// delay(100.milliseconds)
} else {
Timber.d { "Card Scan already in requested state" }
}
}
}reactormonk
12/04/2025, 10:03 AMsuspend fun requestAutoCardScan(delayMs: Byte, cardType: Byte, enabled: Boolean = true) {
Log.d(TAG, "Automatic search for cards requested: $enabled, state: $autoCardScanEnabled")
autoCardScanLock.withLock {
if (autoCardScanEnabled != enabled) {
val response = suspendCancellableCoroutine { continuation ->
sendCommand(Command.AutoCardSearch(continuation, enabled, delayMs, cardType))
}
print(response)
// if (!response.isSuccessful() && enabled) {
// autoCardScanEnabled = false
// throw Exception("Auto card scan request failed: ${response.error.dump()}")
// } else {
// // somehow disabling it always returns a fail
// autoCardScanEnabled = enabled
// }
// delay(100.milliseconds)
} else {
Timber.d { "Card Scan already in requested state" }
}
}
}reactormonk
12/04/2025, 10:04 AMreactormonk
12/04/2025, 10:04 AMreactormonk
12/04/2025, 10:04 AMsuspend fun requestAutoCardScan(delayMs: Byte, cardType: Byte, enabled: Boolean = true) {
Log.d(TAG, "Automatic search for cards requested: $enabled, state: $autoCardScanEnabled")
autoCardScanLock.withLock {
if (autoCardScanEnabled != enabled) {
val response = suspendCancellableCoroutine { continuation ->
sendCommand(Command.AutoCardSearch(continuation, enabled, delayMs, cardType))
}
if (!response.isSuccessful() && enabled) {
autoCardScanEnabled = false
throw Exception("Auto card scan request failed: ${response.error.dump()}")
} else {
// somehow disabling it always returns a fail
autoCardScanEnabled = enabled
}
// delay(100.milliseconds)
} else {
Timber.d { "Card Scan already in requested state" }
}
}
}reactormonk
12/04/2025, 10:05 AMreactormonk
12/04/2025, 10:06 AMsuspend fun requestAutoCardScan(delayMs: Byte, cardType: Byte, enabled: Boolean = true) {
Log.d(TAG, "Automatic search for cards requested: $enabled, state: $autoCardScanEnabled")
autoCardScanLock.withLock {
if (autoCardScanEnabled != enabled) {
val response = suspendCancellableCoroutine { continuation ->
sendCommand(Command.AutoCardSearch(continuation, enabled, delayMs, cardType))
}
if (!response.isSuccessful() && enabled) {
autoCardScanEnabled = false
throw Exception("Auto card scan request failed: ${response.error.dump()}")
} else {
// somehow disabling it always returns a fail
autoCardScanEnabled = enabled
}
} else {
Timber.d { "Card Scan already in requested state" }
}
delay(100.milliseconds)
}
}reactormonk
12/04/2025, 10:08 AMsuspend fun requestAutoCardScan(delayMs: Byte, cardType: Byte, enabled: Boolean = true) {
Log.d(TAG, "Automatic search for cards requested: $enabled, state: $autoCardScanEnabled")
autoCardScanLock.withLock {
var shouldWait = false
if (autoCardScanEnabled != enabled) {
shouldWait = true
val response = suspendCancellableCoroutine { continuation ->
sendCommand(Command.AutoCardSearch(continuation, enabled, delayMs, cardType))
}
if (!response.isSuccessful() && enabled) {
autoCardScanEnabled = false
throw Exception("Auto card scan request failed: ${response.error.dump()}")
} else {
// somehow disabling it always returns a fail
autoCardScanEnabled = enabled
}
} else {
Timber.d { "Card Scan already in requested state" }
}
if (shouldWait) {
delay(100.milliseconds)
}
}
}
^ this compiles 🤷reactormonk
12/04/2025, 10:43 AMsuspend fun requestAutoCardScan(delayMs: Byte, cardType: Byte, enabled: Boolean = true) {
Log.d(TAG, "Automatic search for cards requested: $enabled, state: $autoCardScanEnabled")
autoCardScanLock.withLock {
// this is a workaround for a compiler bug in kotlin 2.2.21
var shouldWait = false
if (autoCardScanEnabled != enabled) {
shouldWait = true
val response = suspendCancellableCoroutine { continuation ->
sendCommand(Command.AutoCardSearch(continuation, enabled, delayMs, cardType))
}
if (!response.isSuccessful() && enabled) {
autoCardScanEnabled = false
throw Exception("Auto card scan request failed: ${response.error.dump()}")
} else {
// somehow disabling it always returns a fail
autoCardScanEnabled = enabled
}
delay(100.milliseconds)
} else {
Timber.d { "Card Scan already in requested state" }
}
if (shouldWait) {
}
}
}