Replace magic numbers with named constants, move remaining hardcoded strings to strings.xml

This commit is contained in:
eddy 2026-07-29 20:56:00 +02:00
commit 106bd25e41
6 changed files with 32 additions and 15 deletions

View file

@ -5,7 +5,8 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/> <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<uses-feature android:name="android.hardware.usb.host" android:required="false"/> <uses-feature android:name="android.hardware.usb.host" android:required="false"/>

View file

@ -40,6 +40,7 @@ class BleRelayManager(
private const val MAX_SCAN_RETRIES = 3 private const val MAX_SCAN_RETRIES = 3
private const val HMAC_PKT_SIZE = 37 private const val HMAC_PKT_SIZE = 37
private const val HMAC_LEN = 32 private const val HMAC_LEN = 32
private const val BLE_MTU = 256
} }
private var bluetoothGatt: BluetoothGatt? = null private var bluetoothGatt: BluetoothGatt? = null
@ -55,6 +56,7 @@ class BleRelayManager(
private val scanRunnable = Runnable { startScan() } private val scanRunnable = Runnable { startScan() }
private var scanTimeoutRunnable: Runnable? = null private var scanTimeoutRunnable: Runnable? = null
@SuppressLint("MissingPermission")
private val scanCallback = object : ScanCallback() { private val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) { override fun onScanResult(callbackType: Int, result: ScanResult) {
val device = result.device ?: return val device = result.device ?: return
@ -139,6 +141,7 @@ class BleRelayManager(
} }
} }
@SuppressLint("MissingPermission")
fun destroy() { fun destroy() {
handler.removeCallbacksAndMessages(null) handler.removeCallbacksAndMessages(null)
stopScan() stopScan()
@ -220,7 +223,7 @@ class BleRelayManager(
return return
} }
Log.d(TAG, "Services discovered, requesting MTU...") Log.d(TAG, "Services discovered, requesting MTU...")
gatt.requestMtu(256) gatt.requestMtu(BLE_MTU)
} }
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")

View file

@ -18,6 +18,10 @@ class EspFlasher(private val context: Context) {
companion object { companion object {
private const val TAG = "EspFlasher" private const val TAG = "EspFlasher"
private const val USB_SETTLE_MS = 500L
private const val KB = 1024
private const val FLASH_RESET_PCT = 95
private const val FLASH_DONE_PCT = 100
} }
interface FlashCallback { interface FlashCallback {
@ -48,7 +52,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(USB_SETTLE_MS)
coroutineContext.ensureActive() coroutineContext.ensureActive()
val connection = usbManager.openDevice(device) val connection = usbManager.openDevice(device)
@ -108,20 +112,20 @@ class EspFlasher(private val context: Context) {
for (step in steps) { for (step in steps) {
coroutineContext.ensureActive() coroutineContext.ensureActive()
val stepName = context.getString(step.nameResId) val stepName = context.getString(step.nameResId)
val sizeStr = if (step.data.size > 1024) "${step.data.size / 1024}KB" else "${step.data.size} bytes" val sizeStr = if (step.data.size > KB) "${step.data.size / KB}KB" else "${step.data.size} bytes"
callback.onProgress(step.baseProgress, "$stepName schrijven ($sizeStr)...") callback.onProgress(step.baseProgress, context.getString(R.string.flash_writing, stepName, sizeStr))
val writeStub = if (step.compress) stub.withCompression(true) else stub.withCompression(false) val writeStub = if (step.compress) stub.withCompression(true) else stub.withCompression(false)
writeStub.writeFlash(step.address, step.data) writeStub.writeFlash(step.address, step.data)
val endProgress = step.baseProgress + step.range val endProgress = step.baseProgress + step.range
callback.onProgress(endProgress.coerceAtMost(95), "$stepName geschreven") callback.onProgress(endProgress.coerceAtMost(FLASH_RESET_PCT), context.getString(R.string.flash_written, stepName))
} }
callback.onProgress(95, "ESP resetten...") callback.onProgress(FLASH_RESET_PCT, context.getString(R.string.flash_resetting))
stub.reset() stub.reset()
callback.onProgress(100, context.getString(R.string.flash_done)) callback.onProgress(FLASH_DONE_PCT, context.getString(R.string.flash_done))
} finally { } finally {
connection.close() connection.close()
} }

View file

@ -55,6 +55,11 @@ class SettingsActivity : AppCompatActivity() {
private const val ACTION_USB_PERMISSION = "com.eddy.atomrelay.USB_PERMISSION" private const val ACTION_USB_PERMISSION = "com.eddy.atomrelay.USB_PERMISSION"
private const val STYLE_SPINNER = 0 private const val STYLE_SPINNER = 0
private const val STYLE_HORIZONTAL = 1 private const val STYLE_HORIZONTAL = 1
private const val USB_READ_BUF_SIZE = 512
private const val HMAC_HEX_LEN = 64
private const val UUID_HEX_LEN = 32
private const val UUID_BYTE_COUNT = 16
private const val HMAC_BYTE_COUNT = 32
} }
private enum class UsbAction { NONE, EXCHANGE_KEY, FLASH } private enum class UsbAction { NONE, EXCHANGE_KEY, FLASH }
@ -480,8 +485,8 @@ class SettingsActivity : AppCompatActivity() {
if (keyLine != null && uuidLine != null) { if (keyLine != null && uuidLine != null) {
val hexKey = keyLine.removePrefix("HMAC_KEY:").trim() val hexKey = keyLine.removePrefix("HMAC_KEY:").trim()
val hexUuid = uuidLine.removePrefix("DEVICE_ID:").trim() val hexUuid = uuidLine.removePrefix("DEVICE_ID:").trim()
if (hexKey.length == 64 && hexKey.all { it in "0123456789ABCDEFabcdef" } && if (hexKey.length == HMAC_HEX_LEN && hexKey.all { it in "0123456789ABCDEFabcdef" } &&
hexUuid.length == 32 && hexUuid.all { it in "0123456789ABCDEFabcdef" }) { hexUuid.length == UUID_HEX_LEN && hexUuid.all { it in "0123456789ABCDEFabcdef" }) {
val espKeyUpper = hexKey.uppercase() val espKeyUpper = hexKey.uppercase()
val espUuidUpper = hexUuid.uppercase() val espUuidUpper = hexUuid.uppercase()
if (appUuid != null && appKey != null && if (appUuid != null && appKey != null &&
@ -502,7 +507,7 @@ class SettingsActivity : AppCompatActivity() {
} else { } else {
Log.d(TAG, "Both have no keys, generating new pair") Log.d(TAG, "Both have no keys, generating new pair")
newKey = generateRandomHexKey() newKey = generateRandomHexKey()
newUuid = generateRandomHex(16) newUuid = generateRandomHex(UUID_BYTE_COUNT)
} }
val setKeyCmd = "SET_KEY:$newKey\n".toByteArray(StandardCharsets.US_ASCII) val setKeyCmd = "SET_KEY:$newKey\n".toByteArray(StandardCharsets.US_ASCII)
@ -551,7 +556,7 @@ class SettingsActivity : AppCompatActivity() {
} }
private fun flushReadBuffer(connection: UsbDeviceConnection, epIn: UsbEndpoint) { private fun flushReadBuffer(connection: UsbDeviceConnection, epIn: UsbEndpoint) {
val buf = ByteArray(512) val buf = ByteArray(USB_READ_BUF_SIZE)
while (true) { while (true) {
val len = connection.bulkTransfer(epIn, buf, buf.size, FLUSH_TIMEOUT_MS) val len = connection.bulkTransfer(epIn, buf, buf.size, FLUSH_TIMEOUT_MS)
if (len <= 0) break if (len <= 0) break
@ -560,7 +565,7 @@ class SettingsActivity : AppCompatActivity() {
} }
private fun readResponse(connection: UsbDeviceConnection, epIn: UsbEndpoint, timeoutMs: Long): String { private fun readResponse(connection: UsbDeviceConnection, epIn: UsbEndpoint, timeoutMs: Long): String {
val buf = ByteArray(512) val buf = ByteArray(USB_READ_BUF_SIZE)
val sb = StringBuilder() val sb = StringBuilder()
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
@ -613,7 +618,7 @@ class SettingsActivity : AppCompatActivity() {
private fun generateRandomHexKey(): String { private fun generateRandomHexKey(): String {
val random = SecureRandom() val random = SecureRandom()
val bytes = ByteArray(32) val bytes = ByteArray(HMAC_BYTE_COUNT)
random.nextBytes(bytes) random.nextBytes(bytes)
return bytes.joinToString("") { "%02X".format(it) } return bytes.joinToString("") { "%02X".format(it) }
} }

View file

@ -16,6 +16,7 @@ class UsbSerialTransport(
companion object { companion object {
private const val USB_TIMEOUT_MS = 5000 private const val USB_TIMEOUT_MS = 5000
private const val CONTROL_LINE_TIMEOUT_MS = 1000 private const val CONTROL_LINE_TIMEOUT_MS = 1000
private const val MIN_USB_PACKET = 64
} }
override fun read(buffer: ByteArray, length: Int): Int { override fun read(buffer: ByteArray, length: Int): Int {
@ -42,6 +43,6 @@ class UsbSerialTransport(
} }
override fun readBufferSize(): Int { override fun readBufferSize(): Int {
return epIn.maxPacketSize.coerceAtLeast(64) return epIn.maxPacketSize.coerceAtLeast(MIN_USB_PACKET)
} }
} }

View file

@ -86,6 +86,9 @@
<string name="flash_chip">Chip: %s</string> <string name="flash_chip">Chip: %s</string>
<string name="flash_stub">Stub laden…</string> <string name="flash_stub">Stub laden…</string>
<string name="flash_done">Firmware geflashed!</string> <string name="flash_done">Firmware geflashed!</string>
<string name="flash_writing">%s schrijven (%s)…</string>
<string name="flash_written">%s geschreven</string>
<string name="flash_resetting">ESP resetten…</string>
<!-- Label customization --> <!-- Label customization -->
<string name="section_labels">Knoptekst</string> <string name="section_labels">Knoptekst</string>