Replace volatile cancel flag with coroutine Job cancellation in EspFlasher

This commit is contained in:
eddy 2026-07-26 22:58:08 +02:00
commit 71b13c51ee
2 changed files with 13 additions and 24 deletions

View file

@ -24,20 +24,13 @@ class EspFlasher(private val context: Context) {
fun onProgress(percent: Int, message: String) fun onProgress(percent: Int, message: String)
} }
@Volatile
private var cancelled = false
fun cancel() {
cancelled = true
}
suspend fun flashDevice(usbManager: UsbManager, device: UsbDevice, callback: FlashCallback) { suspend fun flashDevice(usbManager: UsbManager, device: UsbDevice, callback: FlashCallback) {
cancelled = false
try { try {
withContext(kotlinx.coroutines.Dispatchers.IO) { withContext(kotlinx.coroutines.Dispatchers.IO) {
doFlash(usbManager, device, callback) doFlash(usbManager, device, callback)
} }
} catch (e: CancellationException) { } catch (e: CancellationException) {
Log.i(TAG, "Flash cancelled")
throw e throw e
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Flash failed", e) Log.e(TAG, "Flash failed", e)
@ -56,7 +49,7 @@ class EspFlasher(private val context: Context) {
private suspend fun doFlash(usbManager: UsbManager, device: UsbDevice, callback: FlashCallback) { private suspend fun doFlash(usbManager: UsbManager, device: UsbDevice, callback: FlashCallback) {
kotlinx.coroutines.delay(500) kotlinx.coroutines.delay(500)
checkCancelled() coroutineContext.ensureActive()
val connection = usbManager.openDevice(device) val connection = usbManager.openDevice(device)
?: throw IOException("Kan USB-apparaat niet openen") ?: throw IOException("Kan USB-apparaat niet openen")
@ -77,7 +70,7 @@ class EspFlasher(private val context: Context) {
callback.onProgress(0, "Verbinden met ESP bootloader...") callback.onProgress(0, "Verbinden met ESP bootloader...")
val transport = UsbSerialTransport(connection, epOut, epIn) val transport = UsbSerialTransport(connection, epOut, epIn)
checkCancelled() coroutineContext.ensureActive()
val assets = context.assets val assets = context.assets
val bootloader = assets.open("firmware/bootloader.bin").readBytes() val bootloader = assets.open("firmware/bootloader.bin").readBytes()
@ -116,7 +109,7 @@ class EspFlasher(private val context: Context) {
stub.eraseFlash() stub.eraseFlash()
for (step in steps) { for (step in steps) {
checkCancelled() coroutineContext.ensureActive()
val sizeStr = if (step.data.size > 1024) "${step.data.size / 1024}KB" else "${step.data.size} bytes" val sizeStr = if (step.data.size > 1024) "${step.data.size / 1024}KB" else "${step.data.size} bytes"
callback.onProgress(step.baseProgress, "${step.name} schrijven ($sizeStr)...") callback.onProgress(step.baseProgress, "${step.name} schrijven ($sizeStr)...")
@ -136,9 +129,4 @@ class EspFlasher(private val context: Context) {
} }
} }
private suspend fun checkCancelled() {
if (cancelled) {
coroutineContext.ensureActive()
}
}
} }

View file

@ -22,9 +22,9 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.security.SecureRandom import java.security.SecureRandom
@ -47,7 +47,7 @@ class SettingsActivity : AppCompatActivity() {
private var currentAction = UsbAction.NONE private var currentAction = UsbAction.NONE
private var progressDialog: AlertDialog? = null private var progressDialog: AlertDialog? = null
private var dialog: AlertDialog? = null private var dialog: AlertDialog? = null
private var flasher: EspFlasher? = null private var flasherJob: Job? = null
private val usbAttachedReceiver = object : BroadcastReceiver() { private val usbAttachedReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
@ -124,8 +124,8 @@ class SettingsActivity : AppCompatActivity() {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
currentAction = UsbAction.NONE currentAction = UsbAction.NONE
flasher?.cancel() flasherJob?.cancel()
flasher = null flasherJob = null
try { unregisterReceiver(usbAttachedReceiver) } catch (_: Exception) {} try { unregisterReceiver(usbAttachedReceiver) } catch (_: Exception) {}
try { unregisterReceiver(usbPermissionReceiver) } catch (_: Exception) {} try { unregisterReceiver(usbPermissionReceiver) } catch (_: Exception) {}
try { unregisterReceiver(usbDetachedReceiver) } catch (_: Exception) {} try { unregisterReceiver(usbDetachedReceiver) } catch (_: Exception) {}
@ -211,7 +211,7 @@ class SettingsActivity : AppCompatActivity() {
dialog = null dialog = null
showProgressDialog("Firmware Flashen", "Initialiseren...", STYLE_HORIZONTAL) showProgressDialog("Firmware Flashen", "Initialiseren...", STYLE_HORIZONTAL)
flasher = EspFlasher(this) val flasher = EspFlasher(this)
val progressCallback = object : EspFlasher.FlashCallback { val progressCallback = object : EspFlasher.FlashCallback {
override fun onProgress(percent: Int, message: String) { override fun onProgress(percent: Int, message: String) {
lifecycleScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
@ -221,9 +221,9 @@ class SettingsActivity : AppCompatActivity() {
} }
} }
} }
lifecycleScope.launch { flasherJob = lifecycleScope.launch {
try { try {
flasher?.flashDevice(usbManager, device, progressCallback) flasher.flashDevice(usbManager, device, progressCallback)
if (isFinishing || isDestroyed) return@launch if (isFinishing || isDestroyed) return@launch
currentAction = UsbAction.NONE currentAction = UsbAction.NONE
progressDialog?.dismiss() progressDialog?.dismiss()
@ -240,6 +240,7 @@ class SettingsActivity : AppCompatActivity() {
.show() .show()
} catch (e: kotlinx.coroutines.CancellationException) { } catch (e: kotlinx.coroutines.CancellationException) {
cleanupAction() cleanupAction()
throw e
} catch (e: Exception) { } catch (e: Exception) {
progressDialog?.dismiss() progressDialog?.dismiss()
progressDialog = null progressDialog = null
@ -290,7 +291,7 @@ class SettingsActivity : AppCompatActivity() {
.setTitle(title) .setTitle(title)
.setView(view) .setView(view)
.setNegativeButton("Annuleren") { _, _ -> .setNegativeButton("Annuleren") { _, _ ->
flasher?.cancel() flasherJob?.cancel()
cleanupAction() cleanupAction()
} }
.setCancelable(false) .setCancelable(false)