mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
commit
2cda835f29
11 changed files with 189 additions and 82 deletions
|
|
@ -25,13 +25,14 @@ android {
|
|||
}
|
||||
} */
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "30.0.2" // Note: 30.0.0.2 doesn't yet work on Github actions CI
|
||||
// leave undefined to use version plugin wants
|
||||
// buildToolsVersion "30.0.2" // Note: 30.0.2 doesn't yet work on Github actions CI
|
||||
defaultConfig {
|
||||
applicationId "com.geeksville.mesh"
|
||||
minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works)
|
||||
targetSdkVersion 29
|
||||
versionCode 20122 // format is Mmmss (where M is 1+the numeric major number
|
||||
versionName "1.1.22"
|
||||
versionCode 20133 // format is Mmmss (where M is 1+the numeric major number
|
||||
versionName "1.1.33"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
|
|
@ -107,7 +108,7 @@ protobuf {
|
|||
|
||||
dependencies {
|
||||
|
||||
def room_version = "2.2.5"
|
||||
def room_version = '2.2.6'
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
|
|
|
|||
|
|
@ -144,6 +144,10 @@
|
|||
android:scheme="https"
|
||||
android:host="www.meshtastic.org"
|
||||
android:pathPrefix="/c/" />
|
||||
<data
|
||||
android:scheme="https"
|
||||
android:host="www.meshtastic.org"
|
||||
android:pathPrefix="/C/" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
|
|
|
|||
|
|
@ -27,20 +27,27 @@ class MeshUtilApplication : GeeksvilleApplication() {
|
|||
val pref = AppPrefs(this)
|
||||
crashlytics.setUserId(pref.getInstallId()) // be able to group all bugs per anonymous user
|
||||
|
||||
// We always send our log messages to the crashlytics lib, but they only get sent to the server if we report an exception
|
||||
// This makes log messages work properly if someone turns on analytics just before they click report bug.
|
||||
// send all log messages through crashyltics, so if we do crash we'll have those in the report
|
||||
val standardLogger = Logging.printlog
|
||||
Logging.printlog = { level, tag, message ->
|
||||
crashlytics.log("$tag: $message")
|
||||
standardLogger(level, tag, message)
|
||||
}
|
||||
|
||||
fun sendCrashReports() {
|
||||
if(isAnalyticsAllowed)
|
||||
crashlytics.sendUnsentReports()
|
||||
}
|
||||
|
||||
// Send any old reports if user approves
|
||||
sendCrashReports()
|
||||
|
||||
// Attach to our exception wrapper
|
||||
Exceptions.reporter = { exception, _, _ ->
|
||||
crashlytics.recordException(exception)
|
||||
crashlytics.sendUnsentReports()
|
||||
}
|
||||
|
||||
if (isAnalyticsAllowed) {
|
||||
val standardLogger = Logging.printlog
|
||||
|
||||
// send all log messages through crashyltics, so if we do crash we'll have those in the report
|
||||
Logging.printlog = { level, tag, message ->
|
||||
crashlytics.log("$tag: $message")
|
||||
standardLogger(level, tag, message)
|
||||
}
|
||||
sendCrashReports() // Send the new report
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ data class Channel(
|
|||
) {
|
||||
companion object {
|
||||
// Note: this string _SHOULD NOT BE LOCALIZED_ because it directly hashes to values used on the device for the default channel name.
|
||||
// FIXME - make this work with new channel name system
|
||||
val defaultChannelName = "Default"
|
||||
|
||||
// These bytes must match the well known and not secret bytes used the default channel AES128 key device code
|
||||
|
|
@ -32,17 +33,17 @@ data class Channel(
|
|||
MeshProtos.ChannelSettings.newBuilder().setName(defaultChannelName)
|
||||
.setModemConfig(MeshProtos.ChannelSettings.ModemConfig.Bw125Cr45Sf128).build()
|
||||
)
|
||||
|
||||
|
||||
const val prefix = "https://www.meshtastic.org/c/#"
|
||||
|
||||
private const val base64Flags = Base64.URL_SAFE + Base64.NO_WRAP
|
||||
private const val base64Flags = Base64.URL_SAFE + Base64.NO_WRAP + Base64.NO_PADDING
|
||||
|
||||
private fun urlToSettings(url: Uri): MeshProtos.ChannelSettings {
|
||||
val urlStr = url.toString()
|
||||
|
||||
// We no longer support the super old (about 0.8ish? verison of the URLs that don't use the # separator - I doubt
|
||||
// anyone is still using that old format
|
||||
val pathRegex = Regex("$prefix(.*)")
|
||||
val pathRegex = Regex("$prefix(.*)", RegexOption.IGNORE_CASE)
|
||||
val (base64) = pathRegex.find(urlStr)?.destructured
|
||||
?: throw MalformedURLException("Not a meshtastic URL: ${urlStr.take(40)}")
|
||||
val bytes = Base64.decode(base64, base64Flags)
|
||||
|
|
@ -54,24 +55,39 @@ data class Channel(
|
|||
constructor(url: Uri) : this(urlToSettings(url))
|
||||
|
||||
/// Return the name of our channel as a human readable string. If empty string, assume "Default" per mesh.proto spec
|
||||
val name: String get() = if(settings.name.isEmpty()) defaultChannelName else settings.name
|
||||
val name: String
|
||||
get() = if (settings.name.isEmpty()) {
|
||||
// We have a new style 'empty' channel name. Use the same logic from the device to convert that to a human readable name
|
||||
if (settings.bandwidth != 0)
|
||||
"Unset"
|
||||
else when (settings.modemConfig) {
|
||||
MeshProtos.ChannelSettings.ModemConfig.Bw125Cr45Sf128 -> "Medium"
|
||||
MeshProtos.ChannelSettings.ModemConfig.Bw500Cr45Sf128 -> "ShortFast"
|
||||
MeshProtos.ChannelSettings.ModemConfig.Bw31_25Cr48Sf512 -> "LongAlt"
|
||||
MeshProtos.ChannelSettings.ModemConfig.Bw125Cr48Sf4096 -> "LongSlow"
|
||||
else -> "Invalid"
|
||||
}
|
||||
} else
|
||||
settings.name
|
||||
|
||||
val modemConfig: MeshProtos.ChannelSettings.ModemConfig get() = settings.modemConfig
|
||||
|
||||
val psk get() = if(settings.psk.size() != 1)
|
||||
settings.psk // A standard PSK
|
||||
else {
|
||||
// One of our special 1 byte PSKs, see mesh.proto for docs.
|
||||
val pskIndex = settings.psk.byteAt(0).toInt()
|
||||
|
||||
if(pskIndex == 0)
|
||||
ByteString.EMPTY // Treat as an empty PSK (no encryption)
|
||||
val psk
|
||||
get() = if (settings.psk.size() != 1)
|
||||
settings.psk // A standard PSK
|
||||
else {
|
||||
// Treat an index of 1 as the old channelDefaultKey and work up from there
|
||||
val bytes = channelDefaultKey.clone()
|
||||
bytes[bytes.size - 1] = (0xff and (bytes[bytes.size - 1] + pskIndex - 1)).toByte()
|
||||
ByteString.copyFrom(bytes)
|
||||
// One of our special 1 byte PSKs, see mesh.proto for docs.
|
||||
val pskIndex = settings.psk.byteAt(0).toInt()
|
||||
|
||||
if (pskIndex == 0)
|
||||
ByteString.EMPTY // Treat as an empty PSK (no encryption)
|
||||
else {
|
||||
// Treat an index of 1 as the old channelDefaultKey and work up from there
|
||||
val bytes = channelDefaultKey.clone()
|
||||
bytes[bytes.size - 1] = (0xff and (bytes[bytes.size - 1] + pskIndex - 1)).toByte()
|
||||
ByteString.copyFrom(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a name that is formatted as #channename-suffix
|
||||
|
|
@ -80,28 +96,41 @@ data class Channel(
|
|||
*/
|
||||
val humanName: String
|
||||
get() {
|
||||
val code = settings.psk.fold(0, { acc, x -> acc xor (x.toInt() and 0xff) })
|
||||
return "#${settings.name}-${'A' + (code % 26)}"
|
||||
val suffix: Char = if (settings.psk.size() != 1) {
|
||||
// we have a full PSK, so hash it to generate the suffix
|
||||
val code = settings.psk.fold(0, { acc, x -> acc xor (x.toInt() and 0xff) })
|
||||
|
||||
'A' + (code % 26)
|
||||
} else
|
||||
'0' + settings.psk.byteAt(0).toInt()
|
||||
|
||||
return "#${name}-${suffix}"
|
||||
}
|
||||
|
||||
/// Can this channel be changed right now?
|
||||
var editable = false
|
||||
|
||||
/// Return an URL that represents the current channel values
|
||||
fun getChannelUrl(): Uri {
|
||||
/// @param upperCasePrefix - portions of the URL can be upper case to make for more efficient QR codes
|
||||
fun getChannelUrl(upperCasePrefix: Boolean = false): Uri {
|
||||
// If we have a valid radio config use it, othterwise use whatever we have saved in the prefs
|
||||
|
||||
val channelBytes = settings.toByteArray() ?: ByteArray(0) // if unset just use empty
|
||||
val enc = Base64.encodeToString(channelBytes, base64Flags)
|
||||
|
||||
return Uri.parse("$prefix$enc")
|
||||
val p = if(upperCasePrefix)
|
||||
prefix.toUpperCase()
|
||||
else
|
||||
prefix
|
||||
return Uri.parse("$p$enc")
|
||||
}
|
||||
|
||||
fun getChannelQR(): Bitmap {
|
||||
val multiFormatWriter = MultiFormatWriter()
|
||||
|
||||
// We encode as UPPER case for the QR code URL because QR codes are more efficient for that special case
|
||||
val bitMatrix =
|
||||
multiFormatWriter.encode(getChannelUrl().toString(), BarcodeFormat.QR_CODE, 192, 192);
|
||||
multiFormatWriter.encode(getChannelUrl(true).toString(), BarcodeFormat.QR_CODE, 192, 192);
|
||||
val barcodeEncoder = BarcodeEncoder()
|
||||
return barcodeEncoder.createBitmap(bitMatrix)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const val prefix = "com.geeksville.mesh"
|
|||
|
||||
// a bool true means now connected, false means not
|
||||
const val EXTRA_CONNECTED = "$prefix.Connected"
|
||||
const val EXTRA_PROGRESS = "$prefix.Progress"
|
||||
|
||||
/// a bool true means we expect this condition to continue until, false means device might come back
|
||||
const val EXTRA_PERMANENT = "$prefix.Permanent"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.geeksville.mesh.MeshProtos.ToRadio
|
|||
import com.geeksville.mesh.database.MeshtasticDatabase
|
||||
import com.geeksville.mesh.database.PacketRepository
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import com.geeksville.mesh.service.SoftwareUpdateService.Companion.ProgressNotStarted
|
||||
import com.geeksville.util.*
|
||||
import com.google.android.gms.common.api.ApiException
|
||||
import com.google.android.gms.common.api.ResolvableApiException
|
||||
|
|
@ -891,6 +892,7 @@ class MeshService : Service(), Logging {
|
|||
// Do our startup init
|
||||
try {
|
||||
connectTimeMsec = System.currentTimeMillis()
|
||||
SoftwareUpdateService.sendProgress(this, ProgressNotStarted) // Kinda crufty way of reiniting software update
|
||||
startConfig()
|
||||
|
||||
} catch (ex: InvalidProtocolBufferException) {
|
||||
|
|
@ -1097,10 +1099,10 @@ class MeshService : Service(), Logging {
|
|||
DataPair("dev_error_count", myInfo.errorCount)
|
||||
)
|
||||
|
||||
if (myInfo.errorCode != 0) {
|
||||
if (myInfo.errorCode.number != 0) {
|
||||
GeeksvilleApplication.analytics.track(
|
||||
"dev_error",
|
||||
DataPair("code", myInfo.errorCode),
|
||||
DataPair("code", myInfo.errorCode.number),
|
||||
DataPair("address", myInfo.errorAddress),
|
||||
|
||||
// We also include this info, because it is required to correctly decode address from the map file
|
||||
|
|
@ -1262,7 +1264,12 @@ class MeshService : Service(), Logging {
|
|||
destNum: Int = NODENUM_BROADCAST,
|
||||
wantResponse: Boolean = false
|
||||
) = serviceScope.handledLaunch {
|
||||
sendPosition(lat, lon, alt, destNum, wantResponse)
|
||||
try {
|
||||
sendPosition(lat, lon, alt, destNum, wantResponse)
|
||||
}
|
||||
catch(ex: RadioNotConnectedException) {
|
||||
warn("Ignoring disconnected radio during gps location update")
|
||||
}
|
||||
}
|
||||
|
||||
/** Send our current radio config to the device
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import androidx.work.OneTimeWorkRequestBuilder
|
|||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.geeksville.andlib.BuildConfig
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
|
|
@ -65,6 +66,6 @@ fun MeshService.Companion.startService(context: Context) {
|
|||
// Before binding we want to explicitly create - so the service stays alive forever (so it can keep
|
||||
// listening for the bluetooth packets arriving from the radio. And when they arrive forward them
|
||||
// to Signal or whatever.
|
||||
info("Trying to start service")
|
||||
info("Trying to start service debug=${BuildConfig.DEBUG}")
|
||||
requireNotNull(context.startMeshService()) { "Failed to start service" }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,6 +147,8 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
|
||||
const val ACTION_START_UPDATE = "$prefix.START_UPDATE"
|
||||
|
||||
const val ACTION_UPDATE_PROGRESS = "$prefix.UPDATE_PROGRESS"
|
||||
|
||||
const val EXTRA_MACADDR = "macaddr"
|
||||
|
||||
private const val SCAN_PERIOD: Long = 10000
|
||||
|
|
@ -170,10 +172,15 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
private val MANUFACTURE_CHARACTER = longBLEUUID("2a29")
|
||||
private val HW_VERSION_CHARACTER = longBLEUUID("2a27")
|
||||
|
||||
const val ProgressSuccess = -1
|
||||
const val ProgressUpdateFailed = -2
|
||||
const val ProgressBleException = -3
|
||||
const val ProgressNotStarted = -4
|
||||
|
||||
/**
|
||||
* % progress through the update
|
||||
*/
|
||||
var progress = 0
|
||||
var progress = ProgressNotStarted
|
||||
|
||||
/**
|
||||
* Convenience method for enqueuing work in to this service.
|
||||
|
|
@ -187,6 +194,17 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
}
|
||||
|
||||
|
||||
fun sendProgress(context: Context, p: Int) {
|
||||
if(progress != p) {
|
||||
progress = p
|
||||
|
||||
val intent = Intent(ACTION_UPDATE_PROGRESS).putExtra(
|
||||
EXTRA_PROGRESS,
|
||||
p
|
||||
)
|
||||
context.sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true if we thing the firmwarte shoulde be updated
|
||||
*
|
||||
|
|
@ -200,7 +218,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
val minVer =
|
||||
DeviceVersion("0.7.8") // The oldest device version with a working software update service
|
||||
|
||||
(curVer > deviceVersion) && (deviceVersion >= minVer)
|
||||
((curVer > deviceVersion) && (deviceVersion >= minVer))
|
||||
} catch (ex: Exception) {
|
||||
errormsg("Error finding swupdate info", ex)
|
||||
false // If we fail parsing our update info
|
||||
|
|
@ -262,7 +280,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
errormsg("Ignoring failure to update spiffs on old appload")
|
||||
}
|
||||
assets.appLoad?.let { doUpdate(context, sync, it, FLASH_REGION_APPLOAD) }
|
||||
progress = -1 // success
|
||||
sendProgress(context, ProgressSuccess)
|
||||
}
|
||||
|
||||
// writable region codes in the ESP32 update code
|
||||
|
|
@ -288,7 +306,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
|
||||
info("Starting firmware update for $assetName, flash region $flashRegion")
|
||||
|
||||
progress = 0
|
||||
sendProgress(context,0)
|
||||
val totalSizeDesc = getCharacteristic(SW_UPDATE_TOTALSIZE_CHARACTER)
|
||||
val dataDesc = getCharacteristic(SW_UPDATE_DATA_CHARACTER)
|
||||
val crc32Desc = getCharacteristic(SW_UPDATE_CRC32_CHARACTER)
|
||||
|
|
@ -336,7 +354,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
// yet
|
||||
val maxProgress = if(flashRegion != FLASH_REGION_APPLOAD)
|
||||
50 else 100
|
||||
progress = firmwareNumSent * maxProgress / firmwareSize
|
||||
sendProgress(context, firmwareNumSent * maxProgress / firmwareSize)
|
||||
debug("sending block ${progress}%")
|
||||
var blockSize = 512 - 3 // Max size MTU excluding framing
|
||||
|
||||
|
|
@ -366,7 +384,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
sync.readCharacteristic(updateResultDesc)
|
||||
.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0)
|
||||
if (updateResult != 0) {
|
||||
progress = -2
|
||||
sendProgress(context, ProgressUpdateFailed)
|
||||
throw Exception("Device update failed, reason=$updateResult")
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +395,7 @@ class SoftwareUpdateService : JobIntentService(), Logging {
|
|||
}
|
||||
}
|
||||
} catch (ex: BLEException) {
|
||||
progress = -3
|
||||
sendProgress(context, ProgressBleException)
|
||||
throw ex // Unexpected BLE exception
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import com.geeksville.android.GeeksvilleApplication
|
|||
import com.geeksville.android.Logging
|
||||
import com.geeksville.android.hideKeyboard
|
||||
import com.geeksville.android.isGooglePlayAvailable
|
||||
import com.geeksville.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.MainActivity
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.bluetoothManager
|
||||
|
|
@ -40,6 +39,9 @@ import com.geeksville.mesh.service.BluetoothInterface
|
|||
import com.geeksville.mesh.service.MeshService
|
||||
import com.geeksville.mesh.service.RadioInterfaceService
|
||||
import com.geeksville.mesh.service.SerialInterface
|
||||
import com.geeksville.mesh.service.SoftwareUpdateService.Companion.ACTION_UPDATE_PROGRESS
|
||||
import com.geeksville.mesh.service.SoftwareUpdateService.Companion.ProgressNotStarted
|
||||
import com.geeksville.mesh.service.SoftwareUpdateService.Companion.ProgressSuccess
|
||||
import com.geeksville.util.anonymize
|
||||
import com.geeksville.util.exceptionReporter
|
||||
import com.google.android.gms.location.LocationRequest
|
||||
|
|
@ -50,9 +52,9 @@ import com.hoho.android.usbserial.driver.UsbSerialDriver
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
object SLogging : Logging {}
|
||||
|
||||
/// Change to a new macaddr selection, updating GUI and radio
|
||||
|
|
@ -484,27 +486,13 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
private fun doFirmwareUpdate() {
|
||||
model.meshService?.let { service ->
|
||||
|
||||
mainScope.handledLaunch {
|
||||
debug("User started firmware update")
|
||||
binding.updateFirmwareButton.isEnabled = false // Disable until things complete
|
||||
binding.updateProgressBar.visibility = View.VISIBLE
|
||||
binding.updateProgressBar.progress = 0 // start from scratch
|
||||
debug("User started firmware update")
|
||||
binding.updateFirmwareButton.isEnabled = false // Disable until things complete
|
||||
binding.updateProgressBar.visibility = View.VISIBLE
|
||||
binding.updateProgressBar.progress = 0 // start from scratch
|
||||
|
||||
binding.scanStatusText.text = "Updating firmware, wait up to eight minutes..."
|
||||
try {
|
||||
service.startFirmwareUpdate()
|
||||
while (service.updateStatus >= 0) {
|
||||
binding.updateProgressBar.progress = service.updateStatus
|
||||
delay(2000) // Only check occasionally
|
||||
}
|
||||
} finally {
|
||||
val isSuccess = (service.updateStatus == -1)
|
||||
binding.scanStatusText.text =
|
||||
if (isSuccess) "Update successful" else "Update failed"
|
||||
binding.updateProgressBar.isEnabled = false
|
||||
binding.updateFirmwareButton.isEnabled = !isSuccess
|
||||
}
|
||||
}
|
||||
// We rely on our broadcast receiver to show progress as this progresses
|
||||
service.startFirmwareUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -516,20 +504,55 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
return binding.root
|
||||
}
|
||||
|
||||
private fun initNodeInfo() {
|
||||
val connected = model.isConnected.value
|
||||
|
||||
// If actively connected possibly let the user update firmware
|
||||
/// Set the correct update button configuration based on current progress
|
||||
private fun refreshUpdateButton() {
|
||||
debug("Reiniting the udpate button")
|
||||
val info = model.myNodeInfo.value
|
||||
if (connected == MeshService.ConnectionState.CONNECTED && info != null && info.shouldUpdate && info.couldUpdate) {
|
||||
val service = model.meshService
|
||||
if (model.isConnected.value == MeshService.ConnectionState.CONNECTED && info != null && info.shouldUpdate && info.couldUpdate && service != null) {
|
||||
binding.updateFirmwareButton.visibility = View.VISIBLE
|
||||
binding.updateFirmwareButton.isEnabled = true
|
||||
binding.updateFirmwareButton.text =
|
||||
getString(R.string.update_to).format(getString(R.string.cur_firmware_version))
|
||||
|
||||
val progress = service.updateStatus
|
||||
|
||||
binding.updateFirmwareButton.isEnabled =
|
||||
(progress < 0) // if currently doing an upgrade disable button
|
||||
|
||||
if (progress >= 0) {
|
||||
binding.updateProgressBar.progress = progress // update partial progress
|
||||
binding.scanStatusText.setText(R.string.updating_firmware)
|
||||
binding.updateProgressBar.visibility = View.VISIBLE
|
||||
} else
|
||||
when (progress) {
|
||||
ProgressSuccess -> {
|
||||
binding.scanStatusText.setText(R.string.update_successful)
|
||||
binding.updateProgressBar.visibility = View.GONE
|
||||
}
|
||||
ProgressNotStarted -> {
|
||||
// Do nothing - because we don't want to overwrite the status text in this case
|
||||
binding.updateProgressBar.visibility = View.GONE
|
||||
}
|
||||
else -> {
|
||||
binding.scanStatusText.setText(R.string.update_failed)
|
||||
binding.updateProgressBar.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
binding.updateProgressBar.isEnabled = false
|
||||
|
||||
} else {
|
||||
binding.updateFirmwareButton.visibility = View.GONE
|
||||
binding.updateProgressBar.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun initNodeInfo() {
|
||||
val connected = model.isConnected.value
|
||||
|
||||
refreshUpdateButton()
|
||||
|
||||
// If actively connected possibly let the user update firmware
|
||||
val info = model.myNodeInfo.value
|
||||
|
||||
when (connected) {
|
||||
MeshService.ConnectionState.CONNECTED -> {
|
||||
|
|
@ -784,11 +807,6 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
initClassicScan()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
scanModel.stopScan()
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user has not turned on location access throw up a toast warning
|
||||
*/
|
||||
|
|
@ -842,11 +860,29 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
|
|||
}
|
||||
}
|
||||
|
||||
private val updateProgressFilter = IntentFilter(ACTION_UPDATE_PROGRESS)
|
||||
|
||||
private val updateProgressReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
refreshUpdateButton()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
scanModel.stopScan()
|
||||
|
||||
requireActivity().unregisterReceiver(updateProgressReceiver)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
if (!hasCompanionDeviceApi)
|
||||
scanModel.startScan()
|
||||
|
||||
requireActivity().registerReceiver(updateProgressReceiver, updateProgressFilter)
|
||||
|
||||
// Keep reminding user BLE is still off
|
||||
val hasUSB = activity?.let { SerialInterface.findDrivers(it).isNotEmpty() } ?: true
|
||||
if (!hasUSB) {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 8729bad7f6cfa461be02e3ea65fbde29435b3fe3
|
||||
Subproject commit dfe7bc1217a00c23eecb9dfcf1d56fe95ebddc3b
|
||||
|
|
@ -77,4 +77,7 @@
|
|||
<string name="debug_panel">Debug Panel</string>
|
||||
<string name="debug_last_messages">500 last messages</string>
|
||||
<string name="clear_last_messages">Clear</string>
|
||||
<string name="updating_firmware">Updating firmware, wait up to eight minutes...</string>
|
||||
<string name="update_successful">Update successful</string>
|
||||
<string name="update_failed">Update failed</string>
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue