diff --git a/android/app/src/main/java/com/eddy/atomrelay/EspFlasher.kt b/android/app/src/main/java/com/eddy/atomrelay/EspFlasher.kt
index 8d4b111..8c7704a 100644
--- a/android/app/src/main/java/com/eddy/atomrelay/EspFlasher.kt
+++ b/android/app/src/main/java/com/eddy/atomrelay/EspFlasher.kt
@@ -39,7 +39,7 @@ class EspFlasher(private val context: Context) {
}
private class FlashStep(
- val name: String,
+ val nameResId: Int,
val address: Int,
val data: ByteArray,
val baseProgress: Int,
@@ -52,22 +52,22 @@ class EspFlasher(private val context: Context) {
coroutineContext.ensureActive()
val connection = usbManager.openDevice(device)
- ?: throw IOException("Kan USB-apparaat niet openen")
+ ?: throw IOException(context.getString(R.string.err_usb_open))
try {
val intf = UsbUtils.findCdcDataInterface(device)
- ?: throw IOException("Geen CDC data interface gevonden")
+ ?: throw IOException(context.getString(R.string.err_no_cdc))
if (!connection.claimInterface(intf, true)) {
- throw IOException("Kan USB interface niet claimen")
+ throw IOException(context.getString(R.string.err_usb_claim))
}
val epOut = UsbUtils.findBulkEndpoint(intf, UsbConstants.USB_DIR_OUT)
- ?: throw IOException("Geen USB OUT endpoint gevonden")
+ ?: throw IOException(context.getString(R.string.err_no_out))
val epIn = UsbUtils.findBulkEndpoint(intf, UsbConstants.USB_DIR_IN)
- ?: throw IOException("Geen USB IN endpoint gevonden")
+ ?: throw IOException(context.getString(R.string.err_no_in))
- callback.onProgress(0, "Verbinden met ESP bootloader...")
+ callback.onProgress(0, context.getString(R.string.flash_connecting))
val transport = UsbSerialTransport(connection, epOut, epIn)
coroutineContext.ensureActive()
@@ -78,20 +78,14 @@ class EspFlasher(private val context: Context) {
val bootApp0 = assets.open("firmware/boot_app0.bin").readBytes()
val firmware = assets.open("firmware/firmware.bin").readBytes()
- // Flash sectie layout (partition table check):
- // NVS 0x9000-0xDFFF — NIET overschreven (key/UUID blijft behouden)
- // otadata 0xE000-0xFFFF — overschreven door boot_app0
- // app0 0x10000-... — firmware overschrijft
- // Alleen schrijven naar benodigde adressen, geen full erase
-
val steps = listOf(
- FlashStep("Bootloader", 0x0, bootloader, 30, 10, false),
- FlashStep("Partitie tabel", 0x8000, partitions, 40, 10, false),
- FlashStep("Boot app0", 0xe000, bootApp0, 50, 10, false),
- FlashStep("Firmware", 0x10000, firmware, 60, 35, true)
+ FlashStep(R.string.flash_step_bootloader, 0x0, bootloader, 30, 10, false),
+ FlashStep(R.string.flash_step_partitions, 0x8000, partitions, 40, 10, false),
+ FlashStep(R.string.flash_step_boot_app0, 0xe000, bootApp0, 50, 10, false),
+ FlashStep(R.string.flash_step_firmware, 0x10000, firmware, 60, 35, true)
)
- callback.onProgress(5, "Sync met bootloader...")
+ callback.onProgress(5, context.getString(R.string.flash_sync))
val api = EspFlasherApi.connect(transport)
.withCallBack(object : ProgressCallback {
override fun onProgress(pct: Float) {
@@ -102,31 +96,32 @@ class EspFlasher(private val context: Context) {
}
})
- callback.onProgress(10, "Chip detecteren...")
+ callback.onProgress(10, context.getString(R.string.flash_detect))
val detected = api.chipDetect()
val chipId = detected.getChipId()
Log.i(TAG, "Detected chip: $chipId")
- callback.onProgress(15, "Chip: ${chipId.readableName}")
+ callback.onProgress(15, context.getString(R.string.flash_chip, chipId.readableName))
- callback.onProgress(20, "Stub laden...")
+ callback.onProgress(20, context.getString(R.string.flash_stub))
val stub = detected.loadStub()
for (step in steps) {
coroutineContext.ensureActive()
+ val stepName = context.getString(step.nameResId)
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, "$stepName schrijven ($sizeStr)...")
val writeStub = if (step.compress) stub.withCompression(true) else stub.withCompression(false)
writeStub.writeFlash(step.address, step.data)
val endProgress = step.baseProgress + step.range
- callback.onProgress(endProgress.coerceAtMost(95), "${step.name} geschreven")
+ callback.onProgress(endProgress.coerceAtMost(95), "$stepName geschreven")
}
callback.onProgress(95, "ESP resetten...")
stub.reset()
- callback.onProgress(100, "Firmware geflashed!")
+ callback.onProgress(100, context.getString(R.string.flash_done))
} finally {
connection.close()
}
diff --git a/android/app/src/main/java/com/eddy/atomrelay/SettingsActivity.kt b/android/app/src/main/java/com/eddy/atomrelay/SettingsActivity.kt
index 7380b34..fc4895e 100644
--- a/android/app/src/main/java/com/eddy/atomrelay/SettingsActivity.kt
+++ b/android/app/src/main/java/com/eddy/atomrelay/SettingsActivity.kt
@@ -278,7 +278,8 @@ class SettingsActivity : AppCompatActivity() {
showConflictDialog(usbManager, device, result.espUuid, result.espKey, appKey ?: "")
}
is KeyResult.Error -> {
- Toast.makeText(this@SettingsActivity, getString(R.string.key_exchange_failed), Toast.LENGTH_LONG).show()
+ val msg = if (result.arg != null) getString(result.resId, result.arg!!) else getString(result.resId)
+ Toast.makeText(this@SettingsActivity, msg, Toast.LENGTH_LONG).show()
cleanupAction()
}
null -> {
@@ -431,25 +432,25 @@ class SettingsActivity : AppCompatActivity() {
sealed class KeyResult {
data class Success(val deviceUuid: String, val hexKey: String) : KeyResult()
data class Conflict(val espUuid: String, val espKey: String) : KeyResult()
- data class Error(val message: String) : KeyResult()
+ data class Error(val resId: Int, val arg: String? = null) : KeyResult()
}
private fun readKeyFromEsp(usbManager: UsbManager, device: UsbDevice, appUuid: String?, appKey: String?): KeyResult? {
val intf = UsbUtils.findCdcDataInterface(device)
- ?: return KeyResult.Error("Geen CDC interface gevonden")
+ ?: return KeyResult.Error(R.string.err_no_cdc)
val connection = usbManager.openDevice(device)
- ?: return KeyResult.Error("Kan USB apparaat niet openen")
+ ?: return KeyResult.Error(R.string.err_usb_open)
if (!connection.claimInterface(intf, true)) {
connection.close()
- return KeyResult.Error("Kan USB interface niet claimen")
+ return KeyResult.Error(R.string.err_usb_claim)
}
val epOut = UsbUtils.findBulkEndpoint(intf, UsbConstants.USB_DIR_OUT)
- ?: run { connection.close(); return KeyResult.Error("Geen USB OUT endpoint gevonden") }
+ ?: run { connection.close(); return KeyResult.Error(R.string.err_no_out) }
val epIn = UsbUtils.findBulkEndpoint(intf, UsbConstants.USB_DIR_IN)
- ?: run { connection.close(); return KeyResult.Error("Geen USB IN endpoint gevonden") }
+ ?: run { connection.close(); return KeyResult.Error(R.string.err_no_in) }
try {
setupCdcControl(connection, device)
@@ -489,7 +490,7 @@ class SettingsActivity : AppCompatActivity() {
}
return KeyResult.Success(espUuidUpper, espKeyUpper)
} else {
- return KeyResult.Error("Ongeldig sleutel/UUID formaat")
+ return KeyResult.Error(R.string.err_invalid_key)
}
} else if (noKey) {
val newKey: String
@@ -508,7 +509,7 @@ class SettingsActivity : AppCompatActivity() {
val w = connection.bulkTransfer(epOut, setKeyCmd, setKeyCmd.size, USB_BULK_TIMEOUT_MS)
Log.d(TAG, "Sent SET_KEY ($w bytes)")
if (w < 0) {
- return KeyResult.Error("SET_KEY schrijven mislukt")
+ return KeyResult.Error(R.string.err_set_key_write)
}
Thread.sleep(RESPONSE_WAIT_MS)
@@ -516,14 +517,14 @@ class SettingsActivity : AppCompatActivity() {
Log.d(TAG, "SET_KEY response: ${okKeyResponse.take(200)}")
if (!okKeyResponse.contains("OK")) {
- return KeyResult.Error("ESP bevestigde sleutel niet")
+ return KeyResult.Error(R.string.err_key_not_ack)
}
val setIdCmd = "SET_ID:$newUuid\n".toByteArray(StandardCharsets.US_ASCII)
val w2 = connection.bulkTransfer(epOut, setIdCmd, setIdCmd.size, USB_BULK_TIMEOUT_MS)
Log.d(TAG, "Sent SET_ID ($w2 bytes)")
if (w2 < 0) {
- return KeyResult.Error("SET_ID schrijven mislukt")
+ return KeyResult.Error(R.string.err_set_id_write)
}
Thread.sleep(RESPONSE_WAIT_MS)
@@ -533,17 +534,17 @@ class SettingsActivity : AppCompatActivity() {
if (okUuidResponse.contains("OK")) {
return KeyResult.Success(newUuid, newKey)
} else {
- return KeyResult.Error("ESP bevestigde UUID niet")
+ return KeyResult.Error(R.string.err_uuid_not_ack)
}
}
Log.w(TAG, "Attempt $attempt: no KEY response, retrying")
}
- return KeyResult.Error("Geen sleutel gevonden na 5 pogingen")
+ return KeyResult.Error(R.string.err_key_retries)
} catch (e: Exception) {
Log.e(TAG, "Key exchange error", e)
- return KeyResult.Error("Fout: ${e.message}")
+ return KeyResult.Error(R.string.err_generic, e.message)
} finally {
try { connection.close() } catch (_: Exception) {}
}
@@ -692,7 +693,8 @@ class SettingsActivity : AppCompatActivity() {
finish()
}
is KeyResult.Error -> {
- Toast.makeText(this@SettingsActivity, result.message, Toast.LENGTH_LONG).show()
+ val msg = if (result.arg != null) getString(result.resId, result.arg!!) else getString(result.resId)
+ Toast.makeText(this@SettingsActivity, msg, Toast.LENGTH_LONG).show()
cleanupAction()
}
else -> {
@@ -707,20 +709,20 @@ class SettingsActivity : AppCompatActivity() {
private fun writeKeysToEsp(usbManager: UsbManager, device: UsbDevice,
appKey: String, appUuid: String): KeyResult? {
val intf = UsbUtils.findCdcDataInterface(device)
- ?: return KeyResult.Error("Geen CDC interface gevonden")
+ ?: return KeyResult.Error(R.string.err_no_cdc)
val connection = usbManager.openDevice(device)
- ?: return KeyResult.Error("Kan USB apparaat niet openen")
+ ?: return KeyResult.Error(R.string.err_usb_open)
if (!connection.claimInterface(intf, true)) {
connection.close()
- return KeyResult.Error("Kan USB interface niet claimen")
+ return KeyResult.Error(R.string.err_usb_claim)
}
val epOut = UsbUtils.findBulkEndpoint(intf, UsbConstants.USB_DIR_OUT)
- ?: run { connection.close(); return KeyResult.Error("Geen USB OUT endpoint gevonden") }
+ ?: run { connection.close(); return KeyResult.Error(R.string.err_no_out) }
val epIn = UsbUtils.findBulkEndpoint(intf, UsbConstants.USB_DIR_IN)
- ?: run { connection.close(); return KeyResult.Error("Geen USB IN endpoint gevonden") }
+ ?: run { connection.close(); return KeyResult.Error(R.string.err_no_in) }
try {
setupCdcControl(connection, device)
@@ -729,20 +731,20 @@ class SettingsActivity : AppCompatActivity() {
val setKeyCmd = "SET_KEY:$appKey\n".toByteArray(StandardCharsets.US_ASCII)
val w = connection.bulkTransfer(epOut, setKeyCmd, setKeyCmd.size, USB_BULK_TIMEOUT_MS)
- if (w < 0) return KeyResult.Error("SET_KEY schrijven mislukt")
+ if (w < 0) return KeyResult.Error(R.string.err_set_key_write)
Log.d(TAG, "Sent SET_KEY ($w bytes)")
Thread.sleep(RESPONSE_WAIT_MS)
val okKeyResponse = readResponse(connection, epIn, SET_KEY_TIMEOUT_MS)
- if (!okKeyResponse.contains("OK")) return KeyResult.Error("ESP bevestigde sleutel niet")
+ if (!okKeyResponse.contains("OK")) return KeyResult.Error(R.string.err_key_not_ack)
val setIdCmd = "SET_ID:$appUuid\n".toByteArray(StandardCharsets.US_ASCII)
val w2 = connection.bulkTransfer(epOut, setIdCmd, setIdCmd.size, USB_BULK_TIMEOUT_MS)
- if (w2 < 0) return KeyResult.Error("SET_ID schrijven mislukt")
+ if (w2 < 0) return KeyResult.Error(R.string.err_set_id_write)
Thread.sleep(RESPONSE_WAIT_MS)
val okUuidResponse = readResponse(connection, epIn, SET_KEY_TIMEOUT_MS)
- if (!okUuidResponse.contains("OK")) return KeyResult.Error("ESP bevestigde UUID niet")
+ if (!okUuidResponse.contains("OK")) return KeyResult.Error(R.string.err_uuid_not_ack)
val prefs = PrefsHelper.getPrefs(this)
prefs.edit()
@@ -752,7 +754,7 @@ class SettingsActivity : AppCompatActivity() {
return KeyResult.Success(appUuid, appKey)
} catch (e: Exception) {
Log.e(TAG, "Write keys error", e)
- return KeyResult.Error("Fout: ${e.message}")
+ return KeyResult.Error(R.string.err_generic, e.message)
} finally {
try { connection.close() } catch (_: Exception) {}
}
diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml
index 50ac9ab..b8b0040 100644
--- a/android/app/src/main/res/layout/activity_main.xml
+++ b/android/app/src/main/res/layout/activity_main.xml
@@ -11,7 +11,7 @@
android:layout_gravity="end|top"
android:src="@drawable/ic_settings"
android:background="?android:attr/selectableItemBackgroundBorderless"
- android:contentDescription="Instellingen" />
+ android:contentDescription="@string/cd_settings" />
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/activity_settings.xml b/android/app/src/main/res/layout/activity_settings.xml
index afa9538..8837376 100644
--- a/android/app/src/main/res/layout/activity_settings.xml
+++ b/android/app/src/main/res/layout/activity_settings.xml
@@ -8,7 +8,7 @@
@@ -16,7 +16,7 @@
android:id="@+id/exchangeKeyButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Sleutel Uitwisselen" />
+ android:text="@string/button_key_exchange" />
@@ -36,7 +36,7 @@
android:id="@+id/flashFirmwareButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Firmware Flashen" />
+ android:text="@string/button_firmware_flash" />
Instellingen
+ Disconnected
Verbonden, services zoeken…
Verbinden…
Connected
@@ -19,11 +20,16 @@
Opnieuw scannen
+ Instellingen
ON
HOLD
Instellingen
+ Sluit de ESP aan op USB en kies een actie.
+ Sleutel Uitwisselen
+ Zet de ESP in download-modus (houd reset 2s in tot de groene LED aangaat) en tik op Firmware Flashen.
+ Firmware Flashen
Buildnummer: %s
USB-toegang geweigerd
Beveiligingssleutel gelezen
@@ -55,6 +61,32 @@
Sluiten
Annuleren
+
+ Geen CDC interface gevonden
+ Kan USB apparaat niet openen
+ Kan USB interface niet claimen
+ Geen USB OUT endpoint gevonden
+ Geen USB IN endpoint gevonden
+ Ongeldig sleutel/UUID formaat
+ Geen sleutel gevonden na 5 pogingen
+ Fout: %s
+ SET_KEY schrijven mislukt
+ SET_ID schrijven mislukt
+ ESP bevestigde sleutel niet
+ ESP bevestigde UUID niet
+
+
+ Verbinden met ESP bootloader…
+ Bootloader
+ Partitie tabel
+ Boot app0
+ Firmware
+ Sync met bootloader…
+ Chip detecteren…
+ Chip: %s
+ Stub laden…
+ Firmware geflashed!
+
Knoptekst
Relay 1
diff --git a/docs/user-facing-text.md b/docs/user-facing-text.md
new file mode 100644
index 0000000..f40a153
--- /dev/null
+++ b/docs/user-facing-text.md
@@ -0,0 +1,111 @@
+# User-facing text — Atom Relay
+
+## MainActivity — statusbalk
+| Huidige tekst | strings.xml key |
+|---|---|
+| Connected | `status_connected` |
+| Verbinden… | `status_connecting` |
+| Servicefout | `status_service_error` |
+| Service not found | `status_service_not_found` |
+| Communicatiefout | `status_comm_error` |
+| Geen HMAC sleutel - open Instellingen | `status_no_key` |
+| Permissies geweigerd — open Instellingen | `status_permissions_denied` |
+| Bluetooth not enabled | `status_bluetooth_disabled` |
+| Scanning… | `status_scanning` |
+| Device not found | `status_device_not_found` |
+| Verkeerd apparaat — opnieuw scannen… | `status_wrong_device` |
+| Verbonden, services zoeken… | `status_discovering_services` |
+
+## MainActivity — knoppen
+| Huidige tekst | strings.xml key |
+|---|---|
+| Instellingen (contentDescription) | `cd_settings` |
+| Disconnected (placeholder) | `status_default` |
+| HOLD (fallback knoplabel) | `button_hold` |
+| Opnieuw scannen (fallback) | `button_retry_scan` |
+| User-defined labels 1 & 2 | PrefsHelper (user input) |
+
+## SettingsActivity — UI
+| Huidige tekst | strings.xml key |
+|---|---|
+| Sluit de ESP aan op USB en kies een actie. | `msg_settings_intro` |
+| Sleutel Uitwisselen | `button_key_exchange` |
+| Zet de ESP in download-modus (houd reset 2s in tot de groene LED aangaat) en tik op Firmware Flashen. | `msg_flash_mode_help` |
+| Firmware Flashen | `button_firmware_flash` |
+| Knoptekst | `section_labels` |
+| Relay 1 | `hint_label_relay1` |
+| Relay 2 | `hint_label_relay2` |
+| Weergave | `section_theme` |
+| Systeem | `theme_system` |
+| Licht | `theme_light` |
+| Donker | `theme_dark` |
+| Thema: %s | `theme_applied` |
+| Buildnummer: %s | `build_number` |
+
+## SettingsActivity — dialogs / toasts
+| Huidige tekst | strings.xml key |
+|---|---|
+| Sleutel Uitwisselen | `title_key_exchange` |
+| Sluit de ESP aan via USB. | `msg_connect_usb` |
+| Sleutel uitlezen… | `msg_reading_key` |
+| Beveiligingssleutel gelezen | `key_read_success` |
+| Sleutel uitwisselen mislukt - probeer opnieuw | `key_exchange_failed` |
+| USB-toegang geweigerd | `usb_access_denied` |
+| Annuleren | `button_cancel` |
+| Sluiten | `button_close` |
+| Firmware Flashen | `title_firmware_flash` |
+| Zet de ESP in download-modus (houd reset 2s in tot groene LED) en sluit aan via USB. | `msg_flash_download_mode` |
+| Initialiseren… | `msg_initializing` |
+| Firmware geflashed!\\n\\nOntkoppel de ESP en sluit opnieuw aan om de sleutel in te stellen. | `msg_flash_complete` |
+| Flashen mislukt: %s | `flash_failed` |
+| Sleutels komen niet overeen | `title_key_conflict` |
+| De ESP (SylDa_Server) heeft andere sleutels dan deze app. Wat wil je doen? | `msg_key_conflict` |
+| ESP sleutels overnemen | `button_keep_esp_keys` |
+| App sleutels overzetten | `button_overwrite_esp` |
+| ESP sleutels overschrijven? | `title_overwrite_confirm` |
+| Dit vervangt de sleutels van de ESP. De ESP kan daarna niet meer met de oorspronkelijke app communiceren. | `msg_overwrite_warning` |
+| Ik weet het zeker | `checkbox_confirm_overwrite` |
+| Bevestigen | `button_confirm` |
+| Sleutels overschrijven | `title_overwriting_keys` |
+| Sleutels schrijven naar ESP… | `msg_writing_keys` |
+
+## SettingsActivity — foutmeldingen (hardcoded in .kt)
+| Huidige tekst | strings.xml key |
+|---|---|
+| Geen CDC interface gevonden | `err_no_cdc` |
+| Kan USB apparaat niet openen | `err_usb_open` |
+| Kan USB interface niet claimen | `err_usb_claim` |
+| Geen USB OUT endpoint gevonden | `err_no_out` |
+| Geen USB IN endpoint gevonden | `err_no_in` |
+| Ongeldig sleutel/UUID formaat | `err_invalid_key` |
+| Geen sleutel gevonden na 5 pogingen | `err_key_retries` |
+| Fout: %s | `err_generic` |
+| SET_KEY schrijven mislukt | `err_set_key_write` |
+| SET_ID schrijven mislukt | `err_set_id_write` |
+| ESP bevestigde sleutel niet | `err_key_not_ack` |
+| ESP bevestigde UUID niet | `err_uuid_not_ack` |
+
+## EspFlasher — hardcoded in .kt
+| Huidige tekst | strings.xml key |
+|---|---|
+| Kan USB-apparaat niet openen | `err_usb_open` (reuse) |
+| Geen CDC data interface gevonden | `err_no_cdc` (reuse) |
+| Kan USB interface niet claimen | `err_usb_claim` (reuse) |
+| Geen USB OUT endpoint gevonden | `err_no_out` (reuse) |
+| Geen USB IN endpoint gevonden | `err_no_in` (reuse) |
+| Verbinden met ESP bootloader... | `flash_connecting` |
+| Bootloader | `flash_step_bootloader` |
+| Partitie tabel | `flash_step_partitions` |
+| Boot app0 | `flash_step_boot_app0` |
+| Firmware | `flash_step_firmware` |
+| Sync met bootloader... | `flash_sync` |
+| Chip detecteren... | `flash_detect` |
+| Chip: %s | `flash_chip` |
+| Stub laden... | `flash_stub` |
+| Firmware geflashed! | `flash_done` |
+
+## AndroidManifest
+| Huidige tekst | strings.xml key |
+|---|---|
+| Atom Relay | `app_name` |
+| Instellingen | `settings` |