Compare commits
28 changed files with 166 additions and 2295 deletions
18
.gitignore
vendored
18
.gitignore
vendored
|
|
@ -1,7 +1,3 @@
|
||||||
# Generated from version.txt
|
|
||||||
src/version.h
|
|
||||||
android/app/src/main/java/nl/eddydevink/sylda/SylDaVersions.kt
|
|
||||||
|
|
||||||
# PlatformIO
|
# PlatformIO
|
||||||
.pio/
|
.pio/
|
||||||
.pioenvs/
|
.pioenvs/
|
||||||
|
|
@ -47,18 +43,4 @@ codereview.md
|
||||||
DC3-24V*.md
|
DC3-24V*.md
|
||||||
DC3-24V*.pdf
|
DC3-24V*.pdf
|
||||||
esplibalternatief.md
|
esplibalternatief.md
|
||||||
docs/handleiding.md
|
|
||||||
release/
|
release/
|
||||||
releases/
|
|
||||||
|
|
||||||
# Test/debug firmware files
|
|
||||||
src/blink.cpp
|
|
||||||
src/c1.cpp
|
|
||||||
src/get_mac.cpp
|
|
||||||
src/min_cli.cpp
|
|
||||||
src/min_srv.cpp
|
|
||||||
src/s1.cpp
|
|
||||||
src/test_*.cpp
|
|
||||||
|
|
||||||
# Python cache
|
|
||||||
scripts/__pycache__/
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ def hasSigningConfig = signingPropsFile.exists()
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'nl.eddydevink.sylda'
|
namespace 'nl.eddydevink.sylda'
|
||||||
|
// compileSdk/targetSdk stays on 34 (Android 14) by deliberate choice. The target audience
|
||||||
|
// consists of physically disabled users who are very reluctant to update their phones. Staying
|
||||||
|
// on API 34 maximises device compatibility. Re-evaluate when the user base has migrated.
|
||||||
compileSdk 34
|
compileSdk 34
|
||||||
|
|
||||||
if (hasSigningConfig) {
|
if (hasSigningConfig) {
|
||||||
|
|
@ -56,31 +59,6 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task generateSylDaVersions {
|
|
||||||
def versionFile = rootProject.file('../version.txt')
|
|
||||||
def outputFile = file('src/main/java/nl/eddydevink/sylda/SylDaVersions.kt')
|
|
||||||
|
|
||||||
inputs.file(versionFile)
|
|
||||||
outputs.file(outputFile)
|
|
||||||
|
|
||||||
doLast {
|
|
||||||
def versions = [:]
|
|
||||||
versionFile.eachLine { line ->
|
|
||||||
def parts = line.trim().split('=')
|
|
||||||
if (parts.length == 2) versions[parts[0].trim()] = parts[1].trim()
|
|
||||||
}
|
|
||||||
outputFile.text = """// Auto-generated from version.txt — DO NOT EDIT
|
|
||||||
package nl.eddydevink.sylda
|
|
||||||
|
|
||||||
object SylDaVersions {
|
|
||||||
const val SERVER = "${versions.server}"
|
|
||||||
const val CLIENT = "${versions.client}"
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
preBuild.dependsOn generateSylDaVersions
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.core:core-ktx:1.12.0'
|
implementation 'androidx.core:core-ktx:1.12.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
|
|
|
||||||
Binary file not shown.
BIN
android/app/src/main/assets/firmware/firmware.bin
Normal file
BIN
android/app/src/main/assets/firmware/firmware.bin
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -6,7 +6,6 @@ import android.annotation.SuppressLint
|
||||||
import android.bluetooth.BluetoothAdapter
|
import android.bluetooth.BluetoothAdapter
|
||||||
import android.bluetooth.BluetoothGatt
|
import android.bluetooth.BluetoothGatt
|
||||||
import android.bluetooth.BluetoothGattCharacteristic
|
import android.bluetooth.BluetoothGattCharacteristic
|
||||||
import android.bluetooth.BluetoothGattDescriptor
|
|
||||||
import android.bluetooth.BluetoothProfile
|
import android.bluetooth.BluetoothProfile
|
||||||
import android.bluetooth.le.ScanCallback
|
import android.bluetooth.le.ScanCallback
|
||||||
import android.bluetooth.le.ScanFilter
|
import android.bluetooth.le.ScanFilter
|
||||||
|
|
@ -38,7 +37,6 @@ class BleRelayManager(
|
||||||
private val UUID_CHAR_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d9")
|
private val UUID_CHAR_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26d9")
|
||||||
private val VERSION_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26db")
|
private val VERSION_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26db")
|
||||||
private val OTA_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26da")
|
private val OTA_UUID: UUID = UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361f26da")
|
||||||
private val OTA_CCCD_UUID: UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
|
|
||||||
|
|
||||||
private const val SCAN_TIMEOUT_MS = 30000L
|
private const val SCAN_TIMEOUT_MS = 30000L
|
||||||
private const val RECONNECT_DELAY_MS = 2000L
|
private const val RECONNECT_DELAY_MS = 2000L
|
||||||
|
|
@ -208,18 +206,7 @@ class BleRelayManager(
|
||||||
fun enableOtaNotifications(): Boolean {
|
fun enableOtaNotifications(): Boolean {
|
||||||
val char = otaCharacteristic ?: return false
|
val char = otaCharacteristic ?: return false
|
||||||
val gatt = bluetoothGatt ?: return false
|
val gatt = bluetoothGatt ?: return false
|
||||||
if (!gatt.setCharacteristicNotification(char, true)) {
|
return gatt.setCharacteristicNotification(char, true)
|
||||||
Log.e(TAG, "setCharacteristicNotification returned false")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val desc = char.getDescriptor(OTA_CCCD_UUID)
|
|
||||||
if (desc != null) {
|
|
||||||
desc.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
|
|
||||||
if (!gatt.writeDescriptor(desc)) {
|
|
||||||
Log.w(TAG, "writeDescriptor for CCCD returned false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasOtaCharacteristic(): Boolean {
|
fun hasOtaCharacteristic(): Boolean {
|
||||||
|
|
@ -272,7 +259,6 @@ class BleRelayManager(
|
||||||
bluetoothGatt = null
|
bluetoothGatt = null
|
||||||
relayCharacteristic = null
|
relayCharacteristic = null
|
||||||
seq = 1
|
seq = 1
|
||||||
triedAddresses.clear()
|
|
||||||
handler.postDelayed(scanRunnable, RECONNECT_DELAY_MS)
|
handler.postDelayed(scanRunnable, RECONNECT_DELAY_MS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -383,8 +369,7 @@ class BleRelayManager(
|
||||||
val prefs = PrefsHelper.getPrefs(context)
|
val prefs = PrefsHelper.getPrefs(context)
|
||||||
val hexKey = prefs.getString(PrefsHelper.PREF_KEY, null)
|
val hexKey = prefs.getString(PrefsHelper.PREF_KEY, null)
|
||||||
hmacKey = hexKey?.let { hexStringToBytes(it) }
|
hmacKey = hexKey?.let { hexStringToBytes(it) }
|
||||||
val hexUuid = PrefsHelper.getServerUuid(context)
|
val hexUuid = prefs.getString(PrefsHelper.PREF_UUID, null)
|
||||||
?: prefs.getString(PrefsHelper.PREF_UUID, null)
|
|
||||||
deviceUuid = hexUuid?.let { hexStringToBytes(it) }
|
deviceUuid = hexUuid?.let { hexStringToBytes(it) }
|
||||||
seq = prefs.getInt(PrefsHelper.PREF_SEQ, 1).coerceAtMost(PrefsHelper.SEQ_MAX)
|
seq = prefs.getInt(PrefsHelper.PREF_SEQ, 1).coerceAtMost(PrefsHelper.SEQ_MAX)
|
||||||
Log.d(TAG, "HMAC key loaded: ${hmacKey?.size ?: "none"} bytes, UUID: ${deviceUuid?.size ?: "none"} bytes, seq=$seq")
|
Log.d(TAG, "HMAC key loaded: ${hmacKey?.size ?: "none"} bytes, UUID: ${deviceUuid?.size ?: "none"} bytes, seq=$seq")
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,6 @@ class EspFlasher(private val context: Context) {
|
||||||
fun onProgress(percent: Int, message: String)
|
fun onProgress(percent: Int, message: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assetPath(baseName: String): String {
|
|
||||||
val prefix = if (PrefsHelper.getDeviceMode(context) == "client") "client/" else "server/"
|
|
||||||
return "firmware/$prefix$baseName"
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun flashDevice(usbManager: UsbManager, device: UsbDevice, callback: FlashCallback) {
|
suspend fun flashDevice(usbManager: UsbManager, device: UsbDevice, callback: FlashCallback) {
|
||||||
try {
|
try {
|
||||||
withContext(kotlinx.coroutines.Dispatchers.IO) {
|
withContext(kotlinx.coroutines.Dispatchers.IO) {
|
||||||
|
|
@ -82,10 +77,10 @@ class EspFlasher(private val context: Context) {
|
||||||
coroutineContext.ensureActive()
|
coroutineContext.ensureActive()
|
||||||
|
|
||||||
val assets = context.assets
|
val assets = context.assets
|
||||||
val bootloader = assets.open(assetPath("bootloader.bin")).readBytes()
|
val bootloader = assets.open("firmware/bootloader.bin").readBytes()
|
||||||
val partitions = assets.open(assetPath("partitions.bin")).readBytes()
|
val partitions = assets.open("firmware/partitions.bin").readBytes()
|
||||||
val bootApp0 = assets.open(assetPath("boot_app0.bin")).readBytes()
|
val bootApp0 = assets.open("firmware/boot_app0.bin").readBytes()
|
||||||
val firmware = assets.open(assetPath("firmware.bin")).readBytes()
|
val firmware = assets.open("firmware/firmware.bin").readBytes()
|
||||||
|
|
||||||
val steps = listOf(
|
val steps = listOf(
|
||||||
FlashStep(R.string.flash_step_bootloader, 0x0, bootloader, 30, 10, false),
|
FlashStep(R.string.flash_step_bootloader, 0x0, bootloader, 30, 10, false),
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ class MainActivity : AppCompatActivity(), BleCallbacks {
|
||||||
private const val REQUEST_SETTINGS = 1
|
private const val REQUEST_SETTINGS = 1
|
||||||
private const val POST_SETTINGS_RESCAN_MS = 500L
|
private const val POST_SETTINGS_RESCAN_MS = 500L
|
||||||
private const val ON_RESUME_SCAN_DELAY_MS = 300L
|
private const val ON_RESUME_SCAN_DELAY_MS = 300L
|
||||||
|
private const val BUNDLED_FIRMWARE_VERSION = "6"
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var manager: BleRelayManager
|
private lateinit var manager: BleRelayManager
|
||||||
|
|
@ -40,9 +41,8 @@ class MainActivity : AppCompatActivity(), BleCallbacks {
|
||||||
statusText.text = getString(R.string.status_connected)
|
statusText.text = getString(R.string.status_connected)
|
||||||
setupRelayButton()
|
setupRelayButton()
|
||||||
setupRelay2Button()
|
setupRelay2Button()
|
||||||
val isServer = PrefsHelper.getDeviceMode(this@MainActivity) == "server"
|
relay1Button.isEnabled = true
|
||||||
relay1Button.isEnabled = isServer
|
relay2Button.isEnabled = true
|
||||||
relay2Button.isEnabled = isServer
|
|
||||||
}
|
}
|
||||||
BleStatus.Disconnected -> {
|
BleStatus.Disconnected -> {
|
||||||
statusText.text = getString(R.string.status_connecting)
|
statusText.text = getString(R.string.status_connecting)
|
||||||
|
|
@ -92,7 +92,7 @@ class MainActivity : AppCompatActivity(), BleCallbacks {
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
manager = BleRelayManager(this, this)
|
manager = BleRelayManager(this, this)
|
||||||
manager.setAppVersion(SylDaVersions.SERVER)
|
manager.setAppVersion(BUNDLED_FIRMWARE_VERSION)
|
||||||
|
|
||||||
statusText = findViewById(R.id.statusText)
|
statusText = findViewById(R.id.statusText)
|
||||||
relay1Button = findViewById(R.id.relay1Button)
|
relay1Button = findViewById(R.id.relay1Button)
|
||||||
|
|
@ -107,9 +107,6 @@ class MainActivity : AppCompatActivity(), BleCallbacks {
|
||||||
}
|
}
|
||||||
setupRelayButton()
|
setupRelayButton()
|
||||||
setupRelay2Button()
|
setupRelay2Button()
|
||||||
val isServer = PrefsHelper.getDeviceMode(this) == "server"
|
|
||||||
relay1Button.isEnabled = isServer
|
|
||||||
relay2Button.isEnabled = isServer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Deprecated in Java")
|
@Deprecated("Deprecated in Java")
|
||||||
|
|
@ -230,9 +227,11 @@ class MainActivity : AppCompatActivity(), BleCallbacks {
|
||||||
override fun onFirmwareVersion(espVersion: String, appVersion: String) {
|
override fun onFirmwareVersion(espVersion: String, appVersion: String) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (isFinishing || isDestroyed || !lifecycle.currentState.isAtLeast(androidx.lifecycle.Lifecycle.State.RESUMED)) return@runOnUiThread
|
if (isFinishing || isDestroyed || !lifecycle.currentState.isAtLeast(androidx.lifecycle.Lifecycle.State.RESUMED)) return@runOnUiThread
|
||||||
if (espVersion.isNotEmpty() && espVersion != appVersion) {
|
if (espVersion.isEmpty() || espVersion != appVersion) {
|
||||||
Toast.makeText(this, getString(R.string.ota_update_available), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getString(R.string.ota_update_available), Toast.LENGTH_SHORT).show()
|
||||||
startActivityForResult(Intent(this, SettingsActivity::class.java), REQUEST_SETTINGS)
|
startActivityForResult(Intent(this, SettingsActivity::class.java), REQUEST_SETTINGS)
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, getString(R.string.ota_up_to_date), Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,13 @@ class OtaManager(
|
||||||
return minOf(maxOf(mtu - 3 - 4, 20), OTA_ESP_MAX_CHUNK)
|
return minOf(maxOf(mtu - 3 - 4, 20), OTA_ESP_MAX_CHUNK)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadFirmware(assetPath: String = "firmware/firmware.bin"): Boolean {
|
fun loadFirmware(): Boolean {
|
||||||
return try {
|
return try {
|
||||||
firmwareData = context.assets.open(assetPath).readBytes()
|
firmwareData = context.assets.open("firmware/firmware.bin").readBytes()
|
||||||
chunkDataSize = calculateChunkSize(relayManager.getNegotiatedMtu())
|
chunkDataSize = calculateChunkSize(relayManager.getNegotiatedMtu())
|
||||||
Log.d(TAG, "Loaded firmware from $assetPath: ${firmwareData!!.size} bytes, chunkDataSize=$chunkDataSize")
|
|
||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Failed to load firmware from $assetPath", e)
|
Log.e(TAG, "Failed to load firmware", e)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -81,21 +80,7 @@ class OtaManager(
|
||||||
val startPacket = ByteBuffer.allocate(5).order(ByteOrder.BIG_ENDIAN)
|
val startPacket = ByteBuffer.allocate(5).order(ByteOrder.BIG_ENDIAN)
|
||||||
startPacket.put(OTA_CMD_START)
|
startPacket.put(OTA_CMD_START)
|
||||||
startPacket.putInt(fw.size)
|
startPacket.putInt(fw.size)
|
||||||
var startSent = false
|
relayManager.sendOtaData(startPacket.array())
|
||||||
for (attempt in 1..MAX_RETRIES) {
|
|
||||||
if (relayManager.sendOtaData(startPacket.array())) {
|
|
||||||
startSent = true
|
|
||||||
Log.d(TAG, "OTA start packet sent on attempt $attempt")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
Log.w(TAG, "OTA start packet failed attempt $attempt, retrying...")
|
|
||||||
delay(500)
|
|
||||||
}
|
|
||||||
if (!startSent) {
|
|
||||||
relayManager.setOtaNotifyCallback(null)
|
|
||||||
withContext(Dispatchers.Main) { callback.onError("OTA mislukt: start packet niet verzonden") }
|
|
||||||
return@withContext false
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
withTimeout(OTA_START_TIMEOUT_MS) {
|
withTimeout(OTA_START_TIMEOUT_MS) {
|
||||||
|
|
|
||||||
|
|
@ -15,20 +15,8 @@ object PrefsHelper {
|
||||||
const val PREF_THEME = "theme"
|
const val PREF_THEME = "theme"
|
||||||
const val PREF_LABEL1 = "label_relay1"
|
const val PREF_LABEL1 = "label_relay1"
|
||||||
const val PREF_LABEL2 = "label_relay2"
|
const val PREF_LABEL2 = "label_relay2"
|
||||||
const val KEY_DEVICE_MODE = "deviceMode"
|
|
||||||
const val KEY_SERVER_UUID = "serverUuid"
|
|
||||||
const val KEY_CLIENT_UUID = "clientUuid"
|
|
||||||
const val SEQ_MAX = 1_000_000
|
const val SEQ_MAX = 1_000_000
|
||||||
|
|
||||||
fun getDeviceMode(context: Context): String = getPrefs(context).getString(KEY_DEVICE_MODE, "server") ?: "server"
|
|
||||||
fun setDeviceMode(context: Context, mode: String) = getPrefs(context).edit().putString(KEY_DEVICE_MODE, mode).apply()
|
|
||||||
fun getTransportMode(context: Context): String = getPrefs(context).getString("transportMode", "ble") ?: "ble"
|
|
||||||
fun setTransportMode(context: Context, mode: String) = getPrefs(context).edit().putString("transportMode", mode).apply()
|
|
||||||
fun getServerUuid(context: Context): String? = getPrefs(context).getString(KEY_SERVER_UUID, null)
|
|
||||||
fun setServerUuid(context: Context, uuid: String) = getPrefs(context).edit().putString(KEY_SERVER_UUID, uuid).apply()
|
|
||||||
fun getClientUuid(context: Context): String? = getPrefs(context).getString(KEY_CLIENT_UUID, null)
|
|
||||||
fun setClientUuid(context: Context, uuid: String) = getPrefs(context).edit().putString(KEY_CLIENT_UUID, uuid).apply()
|
|
||||||
|
|
||||||
fun applyTheme(context: Context) {
|
fun applyTheme(context: Context) {
|
||||||
val prefs = getPrefs(context)
|
val prefs = getPrefs(context)
|
||||||
val mode = when (prefs.getString(PREF_THEME, "system")) {
|
val mode = when (prefs.getString(PREF_THEME, "system")) {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import android.util.Log
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
|
import android.widget.CheckBox
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.RadioGroup
|
import android.widget.RadioGroup
|
||||||
|
|
@ -31,7 +32,6 @@ import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
|
@ -64,6 +64,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
private const val REBOOT_WAIT_MS = 5000L
|
private const val REBOOT_WAIT_MS = 5000L
|
||||||
private const val REBOOT_POLL_MS = 500L
|
private const val REBOOT_POLL_MS = 500L
|
||||||
private const val REBOOT_MAX_WAIT_MS = 15000L
|
private const val REBOOT_MAX_WAIT_MS = 15000L
|
||||||
|
private const val EXPECTED_FIRMWARE_VERSION = "6"
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class UsbAction { NONE, EXCHANGE_KEY, FLASH }
|
private enum class UsbAction { NONE, EXCHANGE_KEY, FLASH }
|
||||||
|
|
@ -130,10 +131,6 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExpectedFirmwareVersion(): String {
|
|
||||||
return if (PrefsHelper.getDeviceMode(this) == "client") SylDaVersions.CLIENT else SylDaVersions.SERVER
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
lastTheme = PrefsHelper.getPrefs(this).getString(PrefsHelper.PREF_THEME, "system") ?: "system"
|
lastTheme = PrefsHelper.getPrefs(this).getString(PrefsHelper.PREF_THEME, "system") ?: "system"
|
||||||
PrefsHelper.applyTheme(this)
|
PrefsHelper.applyTheme(this)
|
||||||
|
|
@ -143,29 +140,8 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.title = getString(R.string.title_settings)
|
supportActionBar?.title = getString(R.string.title_settings)
|
||||||
|
|
||||||
val radioDeviceMode = findViewById<RadioGroup>(R.id.radioDeviceMode)
|
|
||||||
val radioTransport = findViewById<RadioGroup>(R.id.radioTransport)
|
|
||||||
|
|
||||||
radioDeviceMode.check(
|
|
||||||
if (PrefsHelper.getDeviceMode(this) == "client") R.id.radioClient else R.id.radioServer
|
|
||||||
)
|
|
||||||
radioTransport.check(
|
|
||||||
if (PrefsHelper.getTransportMode(this) == "usb") R.id.radioUsb else R.id.radioBle
|
|
||||||
)
|
|
||||||
|
|
||||||
radioDeviceMode.setOnCheckedChangeListener { _, checkedId ->
|
|
||||||
val mode = if (checkedId == R.id.radioClient) "client" else "server"
|
|
||||||
PrefsHelper.setDeviceMode(this, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
radioTransport.setOnCheckedChangeListener { _, checkedId ->
|
|
||||||
val mode = if (checkedId == R.id.radioUsb) "usb" else "ble"
|
|
||||||
PrefsHelper.setTransportMode(this, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
findViewById<Button>(R.id.updateModuleButton).setOnClickListener {
|
findViewById<Button>(R.id.updateModuleButton).setOnClickListener {
|
||||||
if (PrefsHelper.getDeviceMode(this) == "client" ||
|
if (findViewById<RadioGroup>(R.id.flashMethodGroup).checkedRadioButtonId == R.id.methodUsb) {
|
||||||
findViewById<RadioGroup>(R.id.radioTransport).checkedRadioButtonId == R.id.radioUsb) {
|
|
||||||
startFlashing()
|
startFlashing()
|
||||||
} else {
|
} else {
|
||||||
startOtaUpdate()
|
startOtaUpdate()
|
||||||
|
|
@ -246,7 +222,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val espVersion = manager.getEspVersion()
|
val espVersion = manager.getEspVersion()
|
||||||
if (espVersion != null && espVersion == getExpectedFirmwareVersion()) {
|
if (espVersion != null && espVersion == EXPECTED_FIRMWARE_VERSION) {
|
||||||
Toast.makeText(this, getString(R.string.ota_up_to_date), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getString(R.string.ota_up_to_date), Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -255,9 +231,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
otaManager = OtaManager(this, manager)
|
otaManager = OtaManager(this, manager)
|
||||||
|
|
||||||
val mode = PrefsHelper.getDeviceMode(this)
|
if (otaManager?.loadFirmware() != true) {
|
||||||
val fwPath = "firmware/${if (mode == "client") "client" else "server"}/firmware.bin"
|
|
||||||
if (otaManager?.loadFirmware(fwPath) != true) {
|
|
||||||
progressDialog?.dismiss()
|
progressDialog?.dismiss()
|
||||||
progressDialog = null
|
progressDialog = null
|
||||||
Toast.makeText(this, getString(R.string.ota_load_failed), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getString(R.string.ota_load_failed), Toast.LENGTH_SHORT).show()
|
||||||
|
|
@ -267,7 +241,6 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
manager.enableOtaNotifications()
|
manager.enableOtaNotifications()
|
||||||
|
|
||||||
otaJob = lifecycleScope.launch {
|
otaJob = lifecycleScope.launch {
|
||||||
delay(300)
|
|
||||||
val result = otaManager!!.performOta(object : OtaManager.OtaCallback {
|
val result = otaManager!!.performOta(object : OtaManager.OtaCallback {
|
||||||
override fun onProgress(percent: Int, message: String) {
|
override fun onProgress(percent: Int, message: String) {
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
|
@ -388,11 +361,6 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
.putString(PrefsHelper.PREF_UUID, result.deviceUuid)
|
.putString(PrefsHelper.PREF_UUID, result.deviceUuid)
|
||||||
.putString(PrefsHelper.PREF_KEY, result.hexKey)
|
.putString(PrefsHelper.PREF_KEY, result.hexKey)
|
||||||
.apply()
|
.apply()
|
||||||
if (PrefsHelper.getDeviceMode(this@SettingsActivity) == "server") {
|
|
||||||
PrefsHelper.setServerUuid(this@SettingsActivity, result.deviceUuid)
|
|
||||||
} else {
|
|
||||||
PrefsHelper.setClientUuid(this@SettingsActivity, result.deviceUuid)
|
|
||||||
}
|
|
||||||
cleanupAction()
|
cleanupAction()
|
||||||
setResult(RESULT_SCAN)
|
setResult(RESULT_SCAN)
|
||||||
finish()
|
finish()
|
||||||
|
|
@ -441,7 +409,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val version = readEspVersion(usbManager, device)
|
val version = readEspVersion(usbManager, device)
|
||||||
if (version == getExpectedFirmwareVersion() && !expectingReboot) {
|
if (version == EXPECTED_FIRMWARE_VERSION && !expectingReboot) {
|
||||||
Log.d(TAG, "Firmware version $version matches, up to date")
|
Log.d(TAG, "Firmware version $version matches, up to date")
|
||||||
progressDialog?.dismiss()
|
progressDialog?.dismiss()
|
||||||
progressDialog = null
|
progressDialog = null
|
||||||
|
|
@ -655,7 +623,8 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
hexUuid.length == UUID_HEX_LEN && 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 != espUuidUpper || appKey != espKeyUpper) {
|
if (appUuid != null && appKey != null &&
|
||||||
|
(appUuid != espUuidUpper || appKey != espKeyUpper)) {
|
||||||
return KeyResult.Conflict(espUuidUpper, espKeyUpper)
|
return KeyResult.Conflict(espUuidUpper, espKeyUpper)
|
||||||
}
|
}
|
||||||
return KeyResult.Success(espUuidUpper, espKeyUpper)
|
return KeyResult.Success(espUuidUpper, espKeyUpper)
|
||||||
|
|
@ -665,14 +634,10 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
} else if (noKey) {
|
} else if (noKey) {
|
||||||
val newKey: String
|
val newKey: String
|
||||||
val newUuid: String
|
val newUuid: String
|
||||||
if (appKey != null) {
|
if (appUuid != null && appKey != null) {
|
||||||
Log.d(TAG, "ESP has no keys, using existing HMAC key")
|
Log.d(TAG, "ESP has no keys, using existing app keys")
|
||||||
newKey = appKey
|
newKey = appKey
|
||||||
newUuid = if (PrefsHelper.getDeviceMode(this) == "server") {
|
newUuid = appUuid
|
||||||
generateRandomHex(UUID_BYTE_COUNT)
|
|
||||||
} else {
|
|
||||||
generateRandomHex(UUID_BYTE_COUNT)
|
|
||||||
}
|
|
||||||
} 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()
|
||||||
|
|
@ -706,21 +671,6 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
Log.d(TAG, "SET_ID response: ${okUuidResponse.take(200)}")
|
Log.d(TAG, "SET_ID response: ${okUuidResponse.take(200)}")
|
||||||
|
|
||||||
if (okUuidResponse.contains("OK")) {
|
if (okUuidResponse.contains("OK")) {
|
||||||
if (PrefsHelper.getDeviceMode(this) == "client") {
|
|
||||||
val srvUuid = PrefsHelper.getServerUuid(this)
|
|
||||||
if (srvUuid != null) {
|
|
||||||
val setServerCmd = "SET_SERVER:$srvUuid\n".toByteArray(StandardCharsets.US_ASCII)
|
|
||||||
connection.bulkTransfer(epOut, setServerCmd, setServerCmd.size, USB_BULK_TIMEOUT_MS)
|
|
||||||
Thread.sleep(RESPONSE_WAIT_MS)
|
|
||||||
val okServerResp = readResponse(connection, epIn, SET_KEY_TIMEOUT_MS)
|
|
||||||
Log.d(TAG, "SET_SERVER response: ${okServerResp.take(200)}")
|
|
||||||
if (!okServerResp.contains("OK")) {
|
|
||||||
return KeyResult.Error(R.string.err_server_not_ack)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return KeyResult.Error(R.string.err_no_server_uuid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return KeyResult.Success(newUuid, newKey)
|
return KeyResult.Success(newUuid, newKey)
|
||||||
} else {
|
} else {
|
||||||
return KeyResult.Error(R.string.err_uuid_not_ack)
|
return KeyResult.Error(R.string.err_uuid_not_ack)
|
||||||
|
|
@ -817,23 +767,52 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
private fun showConflictDialog(usbManager: UsbManager, device: UsbDevice,
|
private fun showConflictDialog(usbManager: UsbManager, device: UsbDevice,
|
||||||
espUuid: String, espKey: String, appKey: String) {
|
espUuid: String, espKey: String, appKey: String) {
|
||||||
val prefs = PrefsHelper.getPrefs(this)
|
val prefs = PrefsHelper.getPrefs(this)
|
||||||
val appUuid = prefs.getString(PrefsHelper.PREF_UUID, null)
|
val appUuid = prefs.getString(PrefsHelper.PREF_UUID, "") ?: ""
|
||||||
val newAppKey = appKey.ifEmpty { generateRandomHexKey() }
|
|
||||||
val newAppUuid = appUuid ?: generateRandomHex(UUID_BYTE_COUNT)
|
|
||||||
|
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setTitle(getString(R.string.title_key_conflict))
|
.setTitle(getString(R.string.title_key_conflict))
|
||||||
.setMessage(getString(R.string.msg_pair_device))
|
.setMessage(getString(R.string.msg_key_conflict))
|
||||||
.setPositiveButton(getString(R.string.button_pair)) { _, _ ->
|
.setPositiveButton(getString(R.string.button_keep_esp_keys)) { _, _ ->
|
||||||
doOverwriteEspKeys(usbManager, device, newAppKey, newAppUuid)
|
prefs.edit()
|
||||||
|
.putString(PrefsHelper.PREF_UUID, espUuid)
|
||||||
|
.putString(PrefsHelper.PREF_KEY, espKey)
|
||||||
|
.apply()
|
||||||
|
cleanupAction()
|
||||||
|
setResult(RESULT_SCAN)
|
||||||
|
finish()
|
||||||
}
|
}
|
||||||
.setNegativeButton(getString(R.string.button_cancel)) { _, _ ->
|
.setNegativeButton(getString(R.string.button_overwrite_esp)) { _, _ ->
|
||||||
|
showOverwriteConfirmDialog(usbManager, device, espUuid, espKey, appKey, appUuid)
|
||||||
|
}
|
||||||
|
.setNeutralButton(getString(R.string.button_cancel)) { _, _ ->
|
||||||
cleanupAction()
|
cleanupAction()
|
||||||
}
|
}
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showOverwriteConfirmDialog(usbManager: UsbManager, device: UsbDevice,
|
||||||
|
espUuid: String, espKey: String,
|
||||||
|
appKey: String, appUuid: String) {
|
||||||
|
val view = layoutInflater.inflate(R.layout.dialog_overwrite_confirm, null)
|
||||||
|
val checkBox = view.findViewById<CheckBox>(R.id.confirmCheckBox)
|
||||||
|
val dialog = AlertDialog.Builder(this)
|
||||||
|
.setTitle(getString(R.string.title_overwrite_confirm))
|
||||||
|
.setView(view)
|
||||||
|
.setPositiveButton(getString(R.string.button_confirm)) { _, _ ->
|
||||||
|
if (checkBox.isChecked) {
|
||||||
|
doOverwriteEspKeys(usbManager, device, appKey, appUuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setNegativeButton(getString(R.string.button_cancel)) { _, _ -> }
|
||||||
|
.setCancelable(true)
|
||||||
|
.show()
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
|
||||||
|
checkBox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun doOverwriteEspKeys(usbManager: UsbManager, device: UsbDevice,
|
private fun doOverwriteEspKeys(usbManager: UsbManager, device: UsbDevice,
|
||||||
appKey: String, appUuid: String) {
|
appKey: String, appUuid: String) {
|
||||||
showProgressDialog(getString(R.string.title_overwriting_keys), getString(R.string.msg_writing_keys), STYLE_SPINNER)
|
showProgressDialog(getString(R.string.title_overwriting_keys), getString(R.string.msg_writing_keys), STYLE_SPINNER)
|
||||||
|
|
@ -887,12 +866,6 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
Thread.sleep(KEY_SETTLE_MS)
|
Thread.sleep(KEY_SETTLE_MS)
|
||||||
flushReadBuffer(connection, epIn)
|
flushReadBuffer(connection, epIn)
|
||||||
|
|
||||||
val newUuid = if (PrefsHelper.getDeviceMode(this) == "client") {
|
|
||||||
generateRandomHex(UUID_BYTE_COUNT)
|
|
||||||
} else {
|
|
||||||
appUuid
|
|
||||||
}
|
|
||||||
|
|
||||||
val setKeyCmd = "SET_KEY:$appKey\n".toByteArray(StandardCharsets.US_ASCII)
|
val setKeyCmd = "SET_KEY:$appKey\n".toByteArray(StandardCharsets.US_ASCII)
|
||||||
val w = connection.bulkTransfer(epOut, setKeyCmd, setKeyCmd.size, USB_BULK_TIMEOUT_MS)
|
val w = connection.bulkTransfer(epOut, setKeyCmd, setKeyCmd.size, USB_BULK_TIMEOUT_MS)
|
||||||
if (w < 0) return KeyResult.Error(R.string.err_set_key_write)
|
if (w < 0) return KeyResult.Error(R.string.err_set_key_write)
|
||||||
|
|
@ -902,7 +875,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
val okKeyResponse = readResponse(connection, epIn, SET_KEY_TIMEOUT_MS)
|
val okKeyResponse = readResponse(connection, epIn, SET_KEY_TIMEOUT_MS)
|
||||||
if (!okKeyResponse.contains("OK")) return KeyResult.Error(R.string.err_key_not_ack)
|
if (!okKeyResponse.contains("OK")) return KeyResult.Error(R.string.err_key_not_ack)
|
||||||
|
|
||||||
val setIdCmd = "SET_ID:$newUuid\n".toByteArray(StandardCharsets.US_ASCII)
|
val setIdCmd = "SET_ID:$appUuid\n".toByteArray(StandardCharsets.US_ASCII)
|
||||||
val w2 = connection.bulkTransfer(epOut, setIdCmd, setIdCmd.size, USB_BULK_TIMEOUT_MS)
|
val w2 = connection.bulkTransfer(epOut, setIdCmd, setIdCmd.size, USB_BULK_TIMEOUT_MS)
|
||||||
if (w2 < 0) return KeyResult.Error(R.string.err_set_id_write)
|
if (w2 < 0) return KeyResult.Error(R.string.err_set_id_write)
|
||||||
|
|
||||||
|
|
@ -910,31 +883,12 @@ class SettingsActivity : AppCompatActivity() {
|
||||||
val okUuidResponse = readResponse(connection, epIn, SET_KEY_TIMEOUT_MS)
|
val okUuidResponse = readResponse(connection, epIn, SET_KEY_TIMEOUT_MS)
|
||||||
if (!okUuidResponse.contains("OK")) return KeyResult.Error(R.string.err_uuid_not_ack)
|
if (!okUuidResponse.contains("OK")) return KeyResult.Error(R.string.err_uuid_not_ack)
|
||||||
|
|
||||||
if (PrefsHelper.getDeviceMode(this) == "client") {
|
|
||||||
val srvUuid = PrefsHelper.getServerUuid(this)
|
|
||||||
if (srvUuid != null) {
|
|
||||||
val setServerCmd = "SET_SERVER:$srvUuid\n".toByteArray(StandardCharsets.US_ASCII)
|
|
||||||
connection.bulkTransfer(epOut, setServerCmd, setServerCmd.size, USB_BULK_TIMEOUT_MS)
|
|
||||||
Thread.sleep(RESPONSE_WAIT_MS)
|
|
||||||
val okServerResp = readResponse(connection, epIn, SET_KEY_TIMEOUT_MS)
|
|
||||||
Log.d(TAG, "SET_SERVER response: ${okServerResp.take(200)}")
|
|
||||||
if (!okServerResp.contains("OK")) return KeyResult.Error(R.string.err_server_not_ack)
|
|
||||||
} else {
|
|
||||||
return KeyResult.Error(R.string.err_no_server_uuid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val prefs = PrefsHelper.getPrefs(this)
|
val prefs = PrefsHelper.getPrefs(this)
|
||||||
prefs.edit()
|
prefs.edit()
|
||||||
.putString(PrefsHelper.PREF_UUID, newUuid)
|
.putString(PrefsHelper.PREF_UUID, appUuid)
|
||||||
.putString(PrefsHelper.PREF_KEY, appKey)
|
.putString(PrefsHelper.PREF_KEY, appKey)
|
||||||
.apply()
|
.apply()
|
||||||
if (PrefsHelper.getDeviceMode(this) == "server") {
|
return KeyResult.Success(appUuid, appKey)
|
||||||
PrefsHelper.setServerUuid(this, newUuid)
|
|
||||||
} else {
|
|
||||||
PrefsHelper.setClientUuid(this, newUuid)
|
|
||||||
}
|
|
||||||
return KeyResult.Success(newUuid, appKey)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Write keys error", e)
|
Log.e(TAG, "Write keys error", e)
|
||||||
return KeyResult.Error(R.string.err_generic, e.message)
|
return KeyResult.Error(R.string.err_generic, e.message)
|
||||||
|
|
|
||||||
|
|
@ -6,58 +6,30 @@
|
||||||
android:padding="24dp">
|
android:padding="24dp">
|
||||||
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
android:id="@+id/radioDeviceMode"
|
android:id="@+id/flashMethodGroup"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:checkedButton="@+id/radioServer">
|
android:checkedButton="@id/methodBluetooth">
|
||||||
|
|
||||||
<RadioButton
|
<RadioButton
|
||||||
android:id="@+id/radioServer"
|
android:id="@+id/methodBluetooth"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="Server" />
|
android:gravity="center"
|
||||||
|
android:text="@string/method_bluetooth" />
|
||||||
|
|
||||||
<RadioButton
|
<RadioButton
|
||||||
android:id="@+id/radioClient"
|
android:id="@+id/methodUsb"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="Client" />
|
android:gravity="center"
|
||||||
|
android:text="@string/method_usb" />
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
<RadioGroup
|
|
||||||
android:id="@+id/radioTransport"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:checkedButton="@+id/radioBle">
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/radioBle"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="BLE" />
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/radioUsb"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="USB" />
|
|
||||||
</RadioGroup>
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:background="?android:attr/listDivider"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginBottom="8dp" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/updateModuleButton"
|
android:id="@+id/updateModuleButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
||||||
21
android/app/src/main/res/layout/dialog_overwrite_confirm.xml
Normal file
21
android/app/src/main/res/layout/dialog_overwrite_confirm.xml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_overwrite_warning"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:paddingBottom="16dp" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/confirmCheckBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/checkbox_confirm_overwrite" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -27,6 +27,8 @@
|
||||||
|
|
||||||
<!-- SettingsActivity -->
|
<!-- SettingsActivity -->
|
||||||
<string name="title_settings">Instellingen</string>
|
<string name="title_settings">Instellingen</string>
|
||||||
|
<string name="method_bluetooth">Bluetooth</string>
|
||||||
|
<string name="method_usb">USB</string>
|
||||||
<string name="button_update_module">Module bijwerken</string>
|
<string name="button_update_module">Module bijwerken</string>
|
||||||
<string name="usb_access_denied">USB-toegang geweigerd</string>
|
<string name="usb_access_denied">USB-toegang geweigerd</string>
|
||||||
<string name="key_read_success">Beveiligingssleutel gelezen</string>
|
<string name="key_read_success">Beveiligingssleutel gelezen</string>
|
||||||
|
|
@ -34,10 +36,16 @@
|
||||||
<string name="flash_failed">Flashen mislukt: %s</string>
|
<string name="flash_failed">Flashen mislukt: %s</string>
|
||||||
|
|
||||||
<!-- Key conflict dialog -->
|
<!-- Key conflict dialog -->
|
||||||
<string name="title_key_conflict">Onbekende module</string>
|
<string name="title_key_conflict">Sleutels komen niet overeen</string>
|
||||||
<string name="msg_pair_device">Deze module koppelen aan deze telefoon?</string>
|
<string name="msg_key_conflict">De ESP (SylDa_Server) heeft andere sleutels dan deze app. Wat wil je doen?</string>
|
||||||
<string name="button_pair">Koppelen</string>
|
<string name="button_keep_esp_keys">ESP sleutels overnemen</string>
|
||||||
|
<string name="button_overwrite_esp">App sleutels overzetten</string>
|
||||||
|
<string name="button_confirm">Bevestigen</string>
|
||||||
|
|
||||||
|
<!-- Overwrite confirmation dialog -->
|
||||||
|
<string name="title_overwrite_confirm">ESP sleutels overschrijven?</string>
|
||||||
|
<string name="msg_overwrite_warning">Dit vervangt de sleutels van de ESP. De ESP kan daarna niet meer met de oorspronkelijke app communiceren.</string>
|
||||||
|
<string name="checkbox_confirm_overwrite">Ik weet het zeker</string>
|
||||||
<string name="title_overwriting_keys">Sleutels overschrijven</string>
|
<string name="title_overwriting_keys">Sleutels overschrijven</string>
|
||||||
<string name="msg_writing_keys">Sleutels schrijven naar ESP…</string>
|
<string name="msg_writing_keys">Sleutels schrijven naar ESP…</string>
|
||||||
|
|
||||||
|
|
@ -67,8 +75,6 @@
|
||||||
<string name="err_set_id_write">SET_ID schrijven mislukt</string>
|
<string name="err_set_id_write">SET_ID schrijven mislukt</string>
|
||||||
<string name="err_key_not_ack">ESP bevestigde sleutel niet</string>
|
<string name="err_key_not_ack">ESP bevestigde sleutel niet</string>
|
||||||
<string name="err_uuid_not_ack">ESP bevestigde UUID niet</string>
|
<string name="err_uuid_not_ack">ESP bevestigde UUID niet</string>
|
||||||
<string name="err_server_not_ack">ESP bevestigde server UUID niet</string>
|
|
||||||
<string name="err_no_server_uuid">Server UUID niet beschikbaar — provisioneer eerst de server</string>
|
|
||||||
|
|
||||||
<!-- EspFlasher -->
|
<!-- EspFlasher -->
|
||||||
<string name="flash_connecting">Verbinden met ESP bootloader…</string>
|
<string name="flash_connecting">Verbinden met ESP bootloader…</string>
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue