mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-04 22:23:47 -04:00
feat(logging): Replace Timber with Kermit for multiplatform logging (#4083)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
@@ -232,7 +232,7 @@ dependencies {
|
||||
implementation(libs.usb.serial.android)
|
||||
implementation(libs.androidx.work.runtime.ktx)
|
||||
implementation(libs.accompanist.permissions)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
|
||||
implementation(libs.nordic)
|
||||
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
<ID>CommentSpacing:BLEException.kt$BLEConnectionClosing$/// Our interface is being shut down</ID>
|
||||
<ID>CommentSpacing:Constants.kt$/// a bool true means we expect this condition to continue until, false means device might come back</ID>
|
||||
<ID>CommentSpacing:Coroutines.kt$/// Wrap launch with an exception handler, FIXME, move into a utility lib</ID>
|
||||
<ID>ComposableParamOrder:Channel.kt$ChannelScreen</ID>
|
||||
<ID>ComposableParamOrder:Channel.kt$EditChannelUrl</ID>
|
||||
<ID>ComposableParamOrder:ConnectionsNavIcon.kt$ConnectionsNavIcon</ID>
|
||||
<ID>ComposableParamOrder:EmptyStateContent.kt$EmptyStateContent</ID>
|
||||
<ID>ComposableParamOrder:Share.kt$ShareScreen</ID>
|
||||
<ID>CyclomaticComplexMethod:BleError.kt$BleError.Companion$fun from(exception: Throwable): BleError</ID>
|
||||
<ID>CyclomaticComplexMethod:MeshService.kt$MeshService$private fun handleReceivedData(packet: MeshPacket)</ID>
|
||||
<ID>CyclomaticComplexMethod:SettingsNavigation.kt$@Suppress("LongMethod") fun NavGraphBuilder.settingsGraph(navController: NavHostController)</ID>
|
||||
@@ -32,10 +27,9 @@
|
||||
<ID>FinalNewline:SerialInterfaceFactory.kt$com.geeksville.mesh.repository.radio.SerialInterfaceFactory.kt</ID>
|
||||
<ID>FinalNewline:TCPInterfaceFactory.kt$com.geeksville.mesh.repository.radio.TCPInterfaceFactory.kt</ID>
|
||||
<ID>FinalNewline:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt</ID>
|
||||
<ID>LambdaParameterEventTrailing:Channel.kt$onConfirm</ID>
|
||||
<ID>LambdaParameterInRestartableEffect:Channel.kt$onConfirm</ID>
|
||||
<ID>LargeClass:MeshService.kt$MeshService : Service</ID>
|
||||
<ID>LongMethod:MeshService.kt$MeshService$private fun handleReceivedData(packet: MeshPacket)</ID>
|
||||
<ID>LongMethod:TCPInterface.kt$TCPInterface$private suspend fun startConnect()</ID>
|
||||
<ID>MagicNumber:Contacts.kt$7</ID>
|
||||
<ID>MagicNumber:Contacts.kt$8</ID>
|
||||
<ID>MagicNumber:MQTTRepository.kt$MQTTRepository$512</ID>
|
||||
@@ -60,27 +54,15 @@
|
||||
<ID>MagicNumber:StreamInterface.kt$StreamInterface$4</ID>
|
||||
<ID>MagicNumber:StreamInterface.kt$StreamInterface$8</ID>
|
||||
<ID>MagicNumber:TCPInterface.kt$TCPInterface$1000</ID>
|
||||
<ID>MagicNumber:TCPInterface.kt$TCPInterface$180</ID>
|
||||
<ID>MagicNumber:TCPInterface.kt$TCPInterface$500</ID>
|
||||
<ID>MagicNumber:UIState.kt$4</ID>
|
||||
<ID>MaxLineLength:MeshService.kt$MeshService$"Config complete id mismatch: received=$configCompleteId expected one of [$configOnlyNonce,$nodeInfoNonce]"</ID>
|
||||
<ID>MaxLineLength:MeshService.kt$MeshService$"Neighbor info response filtered: ToUs=$isAddressedToUs, isRecentRequest=$isRecentRequest"</ID>
|
||||
<ID>MaxLineLength:MeshService.kt$MeshService$"setOwner Id: $id longName: ${longName.anonymize} shortName: $shortName isLicensed: $isLicensed isUnmessagable: $isUnmessagable"</ID>
|
||||
<ID>MaxLineLength:MeshService.kt$MeshService.<no name provided>$"sendData dest=${p.to}, id=${p.id} <- ${bytes.size} bytes (connectionState=${connectionStateHolder.connectionState.value})"</ID>
|
||||
<ID>MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found fromNum: ${fromNumCharacteristic?.uuid}, ${fromNumCharacteristic?.instanceId}"</ID>
|
||||
<ID>MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found fromRadio: ${fromRadioCharacteristic?.uuid}, ${fromRadioCharacteristic?.instanceId}"</ID>
|
||||
<ID>MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found logRadio: ${logRadioCharacteristic?.uuid}, ${logRadioCharacteristic?.instanceId}"</ID>
|
||||
<ID>MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found toRadio: ${toRadioCharacteristic?.uuid}, ${toRadioCharacteristic?.instanceId}"</ID>
|
||||
<ID>ModifierClickableOrder:Channel.kt$clickable(onClick = onClick)</ID>
|
||||
<ID>ModifierMissing:BLEDevices.kt$BLEDevices</ID>
|
||||
<ID>ModifierMissing:Channel.kt$ChannelScreen</ID>
|
||||
<ID>ModifierMissing:Contacts.kt$ContactListView</ID>
|
||||
<ID>ModifierMissing:Contacts.kt$ContactsScreen</ID>
|
||||
<ID>ModifierMissing:Contacts.kt$SelectionToolbar</ID>
|
||||
<ID>ModifierMissing:EmptyStateContent.kt$EmptyStateContent</ID>
|
||||
<ID>ModifierMissing:Main.kt$MainScreen</ID>
|
||||
<ID>ModifierMissing:NetworkDevices.kt$NetworkDevices</ID>
|
||||
<ID>ModifierMissing:Share.kt$ShareScreen</ID>
|
||||
<ID>MutableStateAutoboxing:Contacts.kt$mutableStateOf(2)</ID>
|
||||
<ID>NestedBlockDepth:MeshService.kt$MeshService$private fun handleReceivedAdmin(fromNodeNum: Int, a: AdminProtos.AdminMessage)</ID>
|
||||
<ID>NestedBlockDepth:MeshService.kt$MeshService$private fun handleReceivedData(packet: MeshPacket)</ID>
|
||||
<ID>NewLineAtEndOfFile:BLEException.kt$com.geeksville.mesh.service.BLEException.kt</ID>
|
||||
@@ -106,11 +88,6 @@
|
||||
<ID>NoEmptyClassBody:DebugLogFile.kt$BinaryLogFile${ }</ID>
|
||||
<ID>NoSemicolons:DateUtils.kt$DateUtils$;</ID>
|
||||
<ID>OptionalAbstractKeyword:SyncContinuation.kt$Continuation$abstract</ID>
|
||||
<ID>ParameterNaming:Contacts.kt$onDeleteSelected</ID>
|
||||
<ID>ParameterNaming:Contacts.kt$onMuteSelected</ID>
|
||||
<ID>ParameterNaming:UsbDevices.kt$onDeviceSelected</ID>
|
||||
<ID>PreviewPublic:Channel.kt$ModemPresetInfoPreview</ID>
|
||||
<ID>PreviewPublic:EmptyStateContent.kt$EmptyStateContentPreview</ID>
|
||||
<ID>RethrowCaughtException:SyncContinuation.kt$Continuation$throw ex</ID>
|
||||
<ID>SwallowedException:Exceptions.kt$ex: Throwable</ID>
|
||||
<ID>SwallowedException:NsdManager.kt$ex: IllegalArgumentException</ID>
|
||||
@@ -138,6 +115,5 @@
|
||||
<ID>TooManyFunctions:UIState.kt$UIViewModel : ViewModel</ID>
|
||||
<ID>TopLevelPropertyNaming:Constants.kt$const val prefix = "com.geeksville.mesh"</ID>
|
||||
<ID>UtilityClassWithPublicConstructor:NetworkRepositoryModule.kt$NetworkRepositoryModule</ID>
|
||||
<ID>ViewModelForwarding:Main.kt$VersionChecks(uIViewModel)</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
||||
@@ -40,6 +40,7 @@ import androidx.core.net.toUri
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.MainScreen
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@@ -53,7 +54,6 @@ import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.core.ui.theme.MODE_DYNAMIC
|
||||
import org.meshtastic.core.ui.util.showToast
|
||||
import org.meshtastic.feature.intro.AppIntroductionScreen
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -118,27 +118,27 @@ class MainActivity : AppCompatActivity() {
|
||||
when (appLinkAction) {
|
||||
Intent.ACTION_VIEW -> {
|
||||
appLinkData?.let {
|
||||
Timber.d("App link data: $it")
|
||||
Logger.d { "App link data: $it" }
|
||||
if (it.path?.startsWith("/e/") == true || it.path?.startsWith("/E/") == true) {
|
||||
Timber.d("App link data is a channel set")
|
||||
Logger.d { "App link data is a channel set" }
|
||||
model.requestChannelUrl(
|
||||
url = it,
|
||||
onFailure = { lifecycleScope.launch { showToast(Res.string.channel_invalid) } },
|
||||
)
|
||||
} else if (it.path?.startsWith("/v/") == true || it.path?.startsWith("/V/") == true) {
|
||||
Timber.d("App link data is a shared contact")
|
||||
Logger.d { "App link data is a shared contact" }
|
||||
model.setSharedContactRequested(
|
||||
url = it,
|
||||
onFailure = { lifecycleScope.launch { showToast(Res.string.contact_invalid) } },
|
||||
)
|
||||
} else {
|
||||
Timber.d("App link data is not a channel set")
|
||||
Logger.d { "App link data is not a channel set" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
|
||||
Timber.d("USB device attached")
|
||||
Logger.d { "USB device attached" }
|
||||
showSettingsPage()
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
else -> {
|
||||
Timber.w("Unexpected action $appLinkAction")
|
||||
Logger.w { "Unexpected action $appLinkAction" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.android.BindFailedException
|
||||
import com.geeksville.mesh.android.ServiceClient
|
||||
import com.geeksville.mesh.concurrent.SequentialJob
|
||||
@@ -32,7 +33,6 @@ import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import org.meshtastic.core.service.IMeshService
|
||||
import org.meshtastic.core.service.ServiceRepository
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/** A Activity-lifecycle-aware [ServiceClient] that binds [MeshService] once the Activity is started. */
|
||||
@@ -49,7 +49,7 @@ constructor(
|
||||
private val lifecycleOwner: LifecycleOwner = context as LifecycleOwner
|
||||
|
||||
init {
|
||||
Timber.d("Adding self as LifecycleObserver for $lifecycleOwner")
|
||||
Logger.d { "Adding self as LifecycleObserver for $lifecycleOwner" }
|
||||
lifecycleOwner.lifecycle.addObserver(this)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ constructor(
|
||||
override fun onConnected(service: IMeshService) {
|
||||
serviceSetupJob.launch(lifecycleOwner.lifecycleScope) {
|
||||
serviceRepository.setMeshService(service)
|
||||
Timber.d("connected to mesh service, connectionState=${serviceRepository.connectionState.value}")
|
||||
Logger.d { "connected to mesh service, connectionState=${serviceRepository.connectionState.value}" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,32 +73,32 @@ constructor(
|
||||
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
super.onStart(owner)
|
||||
Timber.d("Lifecycle: ON_START")
|
||||
Logger.d { "Lifecycle: ON_START" }
|
||||
|
||||
try {
|
||||
bindMeshService()
|
||||
} catch (ex: BindFailedException) {
|
||||
Timber.e("Bind of MeshService failed: ${ex.message}")
|
||||
Logger.e { "Bind of MeshService failed: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
super.onDestroy(owner)
|
||||
Timber.d("Lifecycle: ON_DESTROY")
|
||||
Logger.d { "Lifecycle: ON_DESTROY" }
|
||||
|
||||
owner.lifecycle.removeObserver(this)
|
||||
Timber.d("Removed self as LifecycleObserver to $lifecycleOwner")
|
||||
Logger.d { "Removed self as LifecycleObserver to $lifecycleOwner" }
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
private fun bindMeshService() {
|
||||
Timber.d("Binding to mesh service!")
|
||||
Logger.d { "Binding to mesh service!" }
|
||||
try {
|
||||
MeshService.startService(context)
|
||||
} catch (ex: Exception) {
|
||||
Timber.e("Failed to start service from activity - but ignoring because bind will work: ${ex.message}")
|
||||
Logger.e { "Failed to start service from activity - but ignoring because bind will work: ${ex.message}" }
|
||||
}
|
||||
|
||||
connect(context, MeshService.createIntent(context), BIND_AUTO_CREATE + BIND_ABOVE_CLIENT)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.geeksville.mesh
|
||||
|
||||
import android.app.Application
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
@@ -28,7 +29,6 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.prefs.mesh.MeshPrefs
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* The main application class for Meshtastic.
|
||||
@@ -61,7 +61,7 @@ interface AppEntryPoint {
|
||||
fun logAssert(executeReliableWrite: Boolean) {
|
||||
if (!executeReliableWrite) {
|
||||
val ex = AssertionError("Assertion failed")
|
||||
Timber.e(ex)
|
||||
Logger.e(ex) { "logAssert" }
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,9 @@ import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import android.os.IInterface
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.util.exceptionReporter
|
||||
import timber.log.Timber
|
||||
import java.io.Closeable
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
@@ -73,14 +72,14 @@ open class ServiceClient<T : IInterface>(private val stubFactory: (IBinder) -> T
|
||||
// Some phones seem to ahve a race where if you unbind and quickly rebind bindService returns false.
|
||||
// Try
|
||||
// a short sleep to see if that helps
|
||||
Timber.e("Needed to use the second bind attempt hack")
|
||||
Logger.e { "Needed to use the second bind attempt hack" }
|
||||
Thread.sleep(500) // was 200ms, but received an autobug from a Galaxy Note4, android 6.0.1
|
||||
if (!c.bindService(intent, connection, flags)) {
|
||||
throw BindFailedException()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Timber.w("Ignoring rebind attempt for service")
|
||||
Logger.w { "Ignoring rebind attempt for service" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +89,7 @@ open class ServiceClient<T : IInterface>(private val stubFactory: (IBinder) -> T
|
||||
context?.unbindService(connection)
|
||||
} catch (ex: IllegalArgumentException) {
|
||||
// Autobugs show this can generate an illegal arg exception for "service not registered" during reinstall?
|
||||
Timber.w("Ignoring error in ServiceClient.close, probably harmless")
|
||||
Logger.w { "Ignoring error in ServiceClient.close, probably harmless" }
|
||||
}
|
||||
serviceP = null
|
||||
context = null
|
||||
@@ -116,7 +115,7 @@ open class ServiceClient<T : IInterface>(private val stubFactory: (IBinder) -> T
|
||||
// If we start to close a service, it seems that there is a possibility a onServiceConnected event
|
||||
// is the queue
|
||||
// for us. Be careful not to process that stale event
|
||||
Timber.w("A service connected while we were closing it, ignoring")
|
||||
Logger.w { "A service connected while we were closing it, ignoring" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package com.geeksville.mesh.concurrent
|
||||
|
||||
import timber.log.Timber
|
||||
import co.touchlab.kermit.Logger
|
||||
|
||||
/**
|
||||
* Sometimes when starting services we face situations where messages come in that require computation but we can't do
|
||||
@@ -36,7 +36,7 @@ class DeferredExecution {
|
||||
|
||||
// / run all work in the queue and clear it to be ready to accept new work
|
||||
fun run() {
|
||||
Timber.d("Running deferred execution numjobs=${queue.size}")
|
||||
Logger.d { "Running deferred execution numjobs=${queue.size}" }
|
||||
queue.forEach { it() }
|
||||
queue.clear()
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ interface Continuation<in T> {
|
||||
fun resumeWithException(ex: Throwable) = try {
|
||||
resume(Result.failure(ex))
|
||||
} catch (ex: Throwable) {
|
||||
// Timber.e("Ignoring $ex while resuming, because we are the ones who threw it")
|
||||
// Logger.e { "Ignoring $ex while resuming because we are the ones who threw it" }
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.os.RemoteException
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
|
||||
import com.geeksville.mesh.repository.network.NetworkRepository
|
||||
import com.geeksville.mesh.repository.network.NetworkRepository.Companion.toAddressString
|
||||
@@ -49,7 +50,6 @@ import org.meshtastic.core.service.ServiceRepository
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.meshtastic
|
||||
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
// ... (DeviceListEntry sealed class remains the same) ...
|
||||
@@ -164,13 +164,13 @@ constructor(
|
||||
|
||||
init {
|
||||
serviceRepository.statusMessage.onEach { errorText.value = it }.launchIn(viewModelScope)
|
||||
Timber.d("BTScanModel created")
|
||||
Logger.d { "BTScanModel created" }
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
bluetoothRepository.stopScan()
|
||||
Timber.d("BTScanModel cleared")
|
||||
Logger.d { "BTScanModel cleared" }
|
||||
}
|
||||
|
||||
fun setErrorText(text: String) {
|
||||
@@ -178,7 +178,7 @@ constructor(
|
||||
}
|
||||
|
||||
fun stopScan() {
|
||||
Timber.d("stopping scan")
|
||||
Logger.d { "stopping scan" }
|
||||
bluetoothRepository.stopScan()
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ constructor(
|
||||
}
|
||||
|
||||
fun startScan() {
|
||||
Timber.d("starting ble scan")
|
||||
Logger.d { "starting ble scan" }
|
||||
bluetoothRepository.startScan()
|
||||
}
|
||||
|
||||
@@ -195,24 +195,24 @@ constructor(
|
||||
try {
|
||||
serviceRepository.meshService?.let { service -> MeshService.changeDeviceAddress(context, service, address) }
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "changeDeviceSelection failed, probably it is shutting down")
|
||||
Logger.e(ex) { "changeDeviceSelection failed, probably it is shutting down" }
|
||||
}
|
||||
}
|
||||
|
||||
/** Initiates the bonding process and connects to the device upon success. */
|
||||
private fun requestBonding(entry: DeviceListEntry.Ble) {
|
||||
Timber.i("Starting bonding for ${entry.peripheral.address.anonymize}")
|
||||
Logger.i { "Starting bonding for ${entry.peripheral.address.anonymize}" }
|
||||
viewModelScope.launch {
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
try {
|
||||
bluetoothRepository.bond(entry.peripheral)
|
||||
Timber.i("Bonding complete for ${entry.peripheral.address.anonymize}, selecting device...")
|
||||
Logger.i { "Bonding complete for ${entry.peripheral.address.anonymize}, selecting device..." }
|
||||
changeDeviceAddress(entry.fullAddress)
|
||||
} catch (ex: SecurityException) {
|
||||
Timber.e(ex, "Bonding failed for ${entry.peripheral.address.anonymize} Permissions not granted")
|
||||
Logger.e(ex) { "Bonding failed for ${entry.peripheral.address.anonymize} Permissions not granted" }
|
||||
serviceRepository.setErrorMessage("Bonding failed: ${ex.message} Permissions not granted")
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Bonding failed for ${entry.peripheral.address.anonymize}")
|
||||
Logger.e(ex) { "Bonding failed for ${entry.peripheral.address.anonymize}" }
|
||||
serviceRepository.setErrorMessage("Bonding failed: ${ex.message}")
|
||||
}
|
||||
}
|
||||
@@ -223,10 +223,10 @@ constructor(
|
||||
.requestPermission(it.driver.device)
|
||||
.onEach { granted ->
|
||||
if (granted) {
|
||||
Timber.i("User approved USB access")
|
||||
Logger.i { "User approved USB access" }
|
||||
changeDeviceAddress(it.fullAddress)
|
||||
} else {
|
||||
Timber.e("USB permission denied for device ${it.address}")
|
||||
Logger.e { "USB permission denied for device ${it.address}" }
|
||||
}
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
@@ -25,6 +25,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavHostController
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.repository.radio.MeshActivity
|
||||
import com.geeksville.mesh.repository.radio.RadioInterfaceService
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
@@ -67,7 +68,6 @@ import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
|
||||
import org.meshtastic.proto.AdminProtos
|
||||
import org.meshtastic.proto.AppOnlyProtos
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
// Given a human name, strip out the first letter of the first three words and return that as the
|
||||
@@ -212,7 +212,7 @@ constructor(
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
Timber.d("ViewModel created")
|
||||
Logger.d { "ViewModel created" }
|
||||
}
|
||||
|
||||
private val _sharedContactRequested: MutableStateFlow<AdminProtos.SharedContact?> = MutableStateFlow(null)
|
||||
@@ -222,7 +222,7 @@ constructor(
|
||||
fun setSharedContactRequested(url: Uri, onFailure: () -> Unit) {
|
||||
runCatching { _sharedContactRequested.value = url.toSharedContact() }
|
||||
.onFailure { ex ->
|
||||
Timber.e(ex, "Shared contact error")
|
||||
Logger.e(ex) { "Shared contact error" }
|
||||
onFailure()
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ constructor(
|
||||
fun requestChannelUrl(url: Uri, onFailure: () -> Unit) =
|
||||
runCatching { _requestChannelSet.value = url.toChannelSet() }
|
||||
.onFailure { ex ->
|
||||
Timber.e(ex, "Channel url error")
|
||||
Logger.e(ex) { "Channel url error" }
|
||||
onFailure()
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ constructor(
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
Timber.d("ViewModel cleared")
|
||||
Logger.d { "ViewModel cleared" }
|
||||
}
|
||||
|
||||
val tracerouteResponse: LiveData<TracerouteResponse?>
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.app.Application
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.repository.radio.BleConstants.BLE_NAME_PATTERN
|
||||
import com.geeksville.mesh.repository.radio.BleConstants.BTM_SERVICE_UUID
|
||||
import com.geeksville.mesh.util.registerReceiverCompat
|
||||
@@ -42,7 +43,6 @@ import no.nordicsemi.kotlin.ble.core.Manager
|
||||
import org.meshtastic.core.common.hasBluetoothPermission
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import org.meshtastic.core.di.ProcessLifecycle
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
@@ -112,7 +112,7 @@ constructor(
|
||||
.onStart { _isScanning.value = true }
|
||||
.onCompletion { _isScanning.value = false }
|
||||
.catch { ex ->
|
||||
Timber.w(ex, "Bluetooth scan failed")
|
||||
Logger.w(ex) { "Bluetooth scan failed" }
|
||||
_isScanning.value = false
|
||||
}
|
||||
.collect { peripheral ->
|
||||
@@ -158,7 +158,7 @@ constructor(
|
||||
)
|
||||
|
||||
_state.emit(newState)
|
||||
Timber.d("Detected our bluetooth access=$newState")
|
||||
Logger.d { "Detected our bluetooth access=$newState" }
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package com.geeksville.mesh.repository.network
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.util.ignoreException
|
||||
import com.google.protobuf.ByteString
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
@@ -36,7 +37,6 @@ import org.meshtastic.core.data.repository.RadioConfigRepository
|
||||
import org.meshtastic.core.model.util.subscribeList
|
||||
import org.meshtastic.proto.MeshProtos.MqttClientProxyMessage
|
||||
import org.meshtastic.proto.mqttClientProxyMessage
|
||||
import timber.log.Timber
|
||||
import java.net.URI
|
||||
import java.security.SecureRandom
|
||||
import javax.inject.Inject
|
||||
@@ -70,7 +70,7 @@ constructor(
|
||||
private var mqttClient: MqttAsyncClient? = null
|
||||
|
||||
fun disconnect() {
|
||||
Timber.i("MQTT Disconnected")
|
||||
Logger.i { "MQTT Disconnected" }
|
||||
mqttClient?.apply {
|
||||
ignoreException { disconnect() }
|
||||
close(true)
|
||||
@@ -110,7 +110,7 @@ constructor(
|
||||
val callback =
|
||||
object : MqttCallbackExtended {
|
||||
override fun connectComplete(reconnect: Boolean, serverURI: String) {
|
||||
Timber.i("MQTT connectComplete: $serverURI reconnect: $reconnect")
|
||||
Logger.i { "MQTT connectComplete: $serverURI reconnect: $reconnect" }
|
||||
channelSet.subscribeList
|
||||
.ifEmpty {
|
||||
return
|
||||
@@ -123,7 +123,7 @@ constructor(
|
||||
}
|
||||
|
||||
override fun connectionLost(cause: Throwable) {
|
||||
Timber.i("MQTT connectionLost cause: $cause")
|
||||
Logger.i { "MQTT connectionLost cause: $cause" }
|
||||
if (cause is IllegalArgumentException) close(cause)
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ constructor(
|
||||
}
|
||||
|
||||
override fun deliveryComplete(token: IMqttDeliveryToken?) {
|
||||
Timber.i("MQTT deliveryComplete messageId: ${token?.messageId}")
|
||||
Logger.i { "MQTT deliveryComplete messageId: ${token?.messageId}" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,15 +161,15 @@ constructor(
|
||||
|
||||
private fun subscribe(topic: String) {
|
||||
mqttClient?.subscribe(topic, DEFAULT_QOS)
|
||||
Timber.i("MQTT Subscribed to topic: $topic")
|
||||
Logger.i { "MQTT Subscribed to topic: $topic" }
|
||||
}
|
||||
|
||||
fun publish(topic: String, data: ByteArray, retained: Boolean) {
|
||||
try {
|
||||
val token = mqttClient?.publish(topic, data, DEFAULT_QOS, retained)
|
||||
Timber.i("MQTT Publish messageId: ${token?.messageId}")
|
||||
Logger.i { "MQTT Publish messageId: ${token?.messageId}" }
|
||||
} catch (ex: Exception) {
|
||||
Timber.e("MQTT Publish error: ${ex.message}")
|
||||
Logger.e { "MQTT Publish error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.SuppressLint
|
||||
import android.net.nsd.NsdManager
|
||||
import android.net.nsd.NsdServiceInfo
|
||||
import android.os.Build
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.asExecutor
|
||||
@@ -30,7 +31,6 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@@ -54,22 +54,22 @@ private fun NsdManager.discoverServices(
|
||||
}
|
||||
|
||||
override fun onDiscoveryStarted(serviceType: String) {
|
||||
Timber.d("NSD Service discovery started")
|
||||
Logger.d { "NSD Service discovery started" }
|
||||
}
|
||||
|
||||
override fun onDiscoveryStopped(serviceType: String) {
|
||||
Timber.d("NSD Service discovery stopped")
|
||||
Logger.d { "NSD Service discovery stopped" }
|
||||
close()
|
||||
}
|
||||
|
||||
override fun onServiceFound(serviceInfo: NsdServiceInfo) {
|
||||
Timber.d("NSD Service found: $serviceInfo")
|
||||
Logger.d { "NSD Service found: $serviceInfo" }
|
||||
serviceList += serviceInfo
|
||||
trySend(serviceList)
|
||||
}
|
||||
|
||||
override fun onServiceLost(serviceInfo: NsdServiceInfo) {
|
||||
Timber.d("NSD Service lost: $serviceInfo")
|
||||
Logger.d { "NSD Service lost: $serviceInfo" }
|
||||
serviceList.removeAll { it.serviceName == serviceInfo.serviceName }
|
||||
trySend(serviceList)
|
||||
}
|
||||
@@ -102,7 +102,7 @@ private suspend fun NsdManager.resolveService(serviceInfo: NsdServiceInfo): NsdS
|
||||
try {
|
||||
unregisterServiceInfoCallback(this)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.w(e, "Already unregistered")
|
||||
Logger.w(e) { "Already unregistered" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ private suspend fun NsdManager.resolveService(serviceInfo: NsdServiceInfo): NsdS
|
||||
try {
|
||||
unregisterServiceInfoCallback(this)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.w(e, "Already unregistered")
|
||||
Logger.w(e) { "Already unregistered" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ private suspend fun NsdManager.resolveService(serviceInfo: NsdServiceInfo): NsdS
|
||||
try {
|
||||
unregisterServiceInfoCallback(callback)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.w(e, "Already unregistered")
|
||||
Logger.w(e) { "Already unregistered" }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package com.geeksville.mesh.repository.radio
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.model.getInitials
|
||||
import com.google.protobuf.ByteString
|
||||
@@ -38,7 +39,6 @@ import org.meshtastic.proto.config
|
||||
import org.meshtastic.proto.deviceMetadata
|
||||
import org.meshtastic.proto.fromRadio
|
||||
import org.meshtastic.proto.queueStatus
|
||||
import timber.log.Timber
|
||||
import kotlin.random.Random
|
||||
|
||||
private val defaultLoRaConfig =
|
||||
@@ -71,7 +71,7 @@ constructor(
|
||||
private val packetIdSequence = generateSequence { currentPacketId++ }.iterator()
|
||||
|
||||
init {
|
||||
Timber.i("Starting the mock interface")
|
||||
Logger.i { "Starting the mock interface" }
|
||||
service.onConnect() // Tell clients they can use the API
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ constructor(
|
||||
data != null && data.portnum == Portnums.PortNum.ADMIN_APP ->
|
||||
handleAdminPacket(pr, AdminProtos.AdminMessage.parseFrom(data.payload))
|
||||
pr.hasPacket() && pr.packet.wantAck -> sendFakeAck(pr)
|
||||
else -> Timber.i("Ignoring data sent to mock interface $pr")
|
||||
else -> Logger.i { "Ignoring data sent to mock interface $pr" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,12 +108,12 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
else -> Timber.i("Ignoring admin sent to mock interface $d")
|
||||
else -> Logger.i { "Ignoring admin sent to mock interface $d" }
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
Timber.i("Closing the mock interface")
|
||||
Logger.i { "Closing the mock interface" }
|
||||
}
|
||||
|
||||
// / Generate a fake text message from a node
|
||||
@@ -297,7 +297,7 @@ constructor(
|
||||
}
|
||||
|
||||
private fun sendConfigResponse(configId: Int) {
|
||||
Timber.d("Sending mock config response")
|
||||
Logger.d { "Sending mock config response" }
|
||||
|
||||
// / Generate a fake node info entry
|
||||
@Suppress("MagicNumber")
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.geeksville.mesh.repository.radio
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.repository.radio.BleConstants.BTM_FROMNUM_CHARACTER
|
||||
import com.geeksville.mesh.repository.radio.BleConstants.BTM_FROMRADIO_CHARACTER
|
||||
import com.geeksville.mesh.repository.radio.BleConstants.BTM_LOGRADIO_CHARACTER
|
||||
@@ -52,7 +53,6 @@ import no.nordicsemi.kotlin.ble.client.android.Peripheral
|
||||
import no.nordicsemi.kotlin.ble.core.CharacteristicProperty
|
||||
import no.nordicsemi.kotlin.ble.core.ConnectionState
|
||||
import no.nordicsemi.kotlin.ble.core.WriteType
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
import kotlin.uuid.toKotlinUuid
|
||||
@@ -79,12 +79,12 @@ constructor(
|
||||
) : IRadioInterface {
|
||||
|
||||
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
||||
Timber.e(throwable, "[$address] Uncaught exception in connectionScope")
|
||||
Logger.e(throwable) { "[$address] Uncaught exception in connectionScope" }
|
||||
serviceScope.launch {
|
||||
try {
|
||||
peripheral?.disconnect()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "[$address] Failed to disconnect in exception handler")
|
||||
Logger.e(e) { "[$address] Failed to disconnect in exception handler" }
|
||||
}
|
||||
}
|
||||
service.onDisconnect(BleError.from(throwable))
|
||||
@@ -118,7 +118,7 @@ constructor(
|
||||
val packet =
|
||||
fromRadioCharacteristic?.read()?.takeIf { it.isNotEmpty() }
|
||||
?: run {
|
||||
Timber.d("[$address] fromRadio queue drain complete (read empty/null)")
|
||||
Logger.d { "[$address] fromRadio queue drain complete (read empty/null)" }
|
||||
break
|
||||
}
|
||||
send(packet)
|
||||
@@ -128,14 +128,14 @@ constructor(
|
||||
private fun dispatchPacket(packet: ByteArray) {
|
||||
packetsReceived++
|
||||
bytesReceived += packet.size
|
||||
Timber.d(
|
||||
Logger.d {
|
||||
"[$address] Dispatching packet to service.handleFromRadio() - " +
|
||||
"Packet #$packetsReceived, ${packet.size} bytes (Total: $bytesReceived bytes)",
|
||||
)
|
||||
"Packet #$packetsReceived, ${packet.size} bytes (Total: $bytesReceived bytes)"
|
||||
}
|
||||
try {
|
||||
service.handleFromRadio(p = packet)
|
||||
} catch (t: Throwable) {
|
||||
Timber.e(t, "[$address] Failed to execute service.handleFromRadio()")
|
||||
Logger.e(t) { "[$address] Failed to execute service.handleFromRadio()" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,13 +145,13 @@ constructor(
|
||||
fromRadioPacketFlow()
|
||||
.onEach { packet ->
|
||||
drainedCount++
|
||||
Timber.d("[$address] Read packet from queue (${packet.size} bytes)")
|
||||
Logger.d { "[$address] Read packet from queue (${packet.size} bytes)" }
|
||||
dispatchPacket(packet)
|
||||
}
|
||||
.catch { ex -> Timber.w(ex, "[$address] Exception while draining packet queue") }
|
||||
.catch { ex -> Logger.w(ex) { "[$address] Exception while draining packet queue" } }
|
||||
.onCompletion {
|
||||
if (drainedCount > 0) {
|
||||
Timber.d("[$address] Drained $drainedCount packets from packet queue")
|
||||
Logger.d { "[$address] Drained $drainedCount packets from packet queue" }
|
||||
}
|
||||
}
|
||||
.collect()
|
||||
@@ -168,19 +168,19 @@ constructor(
|
||||
connectionScope.launch {
|
||||
try {
|
||||
connectionStartTime = System.currentTimeMillis()
|
||||
Timber.i("[$address] BLE connection attempt started at $connectionStartTime")
|
||||
Logger.i { "[$address] BLE connection attempt started at $connectionStartTime" }
|
||||
|
||||
peripheral = retryCall { findAndConnectPeripheral() }
|
||||
peripheral?.let {
|
||||
val connectionTime = System.currentTimeMillis() - connectionStartTime
|
||||
Timber.i("[$address] BLE peripheral connected in ${connectionTime}ms")
|
||||
Logger.i { "[$address] BLE peripheral connected in ${connectionTime}ms" }
|
||||
onConnected()
|
||||
observePeripheralChanges()
|
||||
discoverServicesAndSetupCharacteristics(it)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
val failureTime = System.currentTimeMillis() - connectionStartTime
|
||||
Timber.e(e, "[$address] Failed to connect to peripheral after ${failureTime}ms")
|
||||
Logger.e(e) { "[$address] Failed to connect to peripheral after ${failureTime}ms" }
|
||||
service.onDisconnect(BleError.from(e))
|
||||
}
|
||||
}
|
||||
@@ -200,27 +200,27 @@ constructor(
|
||||
try {
|
||||
peripheral?.let { p ->
|
||||
val rssi = retryCall { p.readRssi() }
|
||||
Timber.d("[$address] Connection established. RSSI: $rssi dBm")
|
||||
Logger.d { "[$address] Connection established. RSSI: $rssi dBm" }
|
||||
|
||||
val phyInUse = retryCall { p.readPhy() }
|
||||
Timber.d("[$address] PHY in use: $phyInUse")
|
||||
Logger.d { "[$address] PHY in use: $phyInUse" }
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "[$address] Failed to read initial connection properties")
|
||||
Logger.w(e) { "[$address] Failed to read initial connection properties" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun observePeripheralChanges() {
|
||||
peripheral?.let { p ->
|
||||
p.phy.onEach { phy -> Timber.i("[$address] BLE PHY changed to $phy") }.launchIn(connectionScope)
|
||||
p.phy.onEach { phy -> Logger.i { "[$address] BLE PHY changed to $phy" } }.launchIn(connectionScope)
|
||||
|
||||
p.connectionParameters
|
||||
.onEach { params -> Timber.i("[$address] BLE connection parameters changed to $params") }
|
||||
.onEach { params -> Logger.i { "[$address] BLE connection parameters changed to $params" } }
|
||||
.launchIn(connectionScope)
|
||||
|
||||
p.state
|
||||
.onEach { state ->
|
||||
Timber.i("[$address] BLE connection state changed to $state")
|
||||
Logger.i { "[$address] BLE connection state changed to $state" }
|
||||
if (state is ConnectionState.Disconnected) {
|
||||
val uptime =
|
||||
if (connectionStartTime > 0) {
|
||||
@@ -228,19 +228,19 @@ constructor(
|
||||
} else {
|
||||
0
|
||||
}
|
||||
Timber.w(
|
||||
Logger.w {
|
||||
"[$address] BLE disconnected - Reason: ${state.reason}, " +
|
||||
"Uptime: ${uptime}ms, " +
|
||||
"Packets RX: $packetsReceived ($bytesReceived bytes), " +
|
||||
"Packets TX: $packetsSent ($bytesSent bytes)",
|
||||
)
|
||||
"Packets TX: $packetsSent ($bytesSent bytes)"
|
||||
}
|
||||
service.onDisconnect(BleError.Disconnected(reason = state.reason))
|
||||
}
|
||||
}
|
||||
.launchIn(connectionScope)
|
||||
}
|
||||
centralManager.state
|
||||
.onEach { state -> Timber.i("[$address] CentralManager state changed to $state") }
|
||||
.onEach { state -> Logger.i { "[$address] CentralManager state changed to $state" } }
|
||||
.launchIn(connectionScope)
|
||||
}
|
||||
|
||||
@@ -268,35 +268,35 @@ constructor(
|
||||
it != null
|
||||
}
|
||||
) {
|
||||
Timber.d(
|
||||
"[$address] Found toRadio: ${toRadioCharacteristic?.uuid}, ${toRadioCharacteristic?.instanceId}",
|
||||
)
|
||||
Timber.d(
|
||||
"[$address] Found fromNum: ${fromNumCharacteristic?.uuid}, ${fromNumCharacteristic?.instanceId}",
|
||||
)
|
||||
Timber.d(
|
||||
"[$address] Found fromRadio: ${fromRadioCharacteristic?.uuid}, ${fromRadioCharacteristic?.instanceId}",
|
||||
)
|
||||
Timber.d(
|
||||
"[$address] Found logRadio: ${logRadioCharacteristic?.uuid}, ${logRadioCharacteristic?.instanceId}",
|
||||
)
|
||||
Logger.d {
|
||||
"[$address] Found toRadio: ${toRadioCharacteristic?.uuid}, ${toRadioCharacteristic?.instanceId}"
|
||||
}
|
||||
Logger.d {
|
||||
"[$address] Found fromNum: ${fromNumCharacteristic?.uuid}, ${fromNumCharacteristic?.instanceId}"
|
||||
}
|
||||
Logger.d {
|
||||
"[$address] Found fromRadio: ${fromRadioCharacteristic?.uuid}, ${fromRadioCharacteristic?.instanceId}"
|
||||
}
|
||||
Logger.d {
|
||||
"[$address] Found logRadio: ${logRadioCharacteristic?.uuid}, ${logRadioCharacteristic?.instanceId}"
|
||||
}
|
||||
setupNotifications()
|
||||
service.onConnect()
|
||||
} else {
|
||||
Timber.w("[$address] Discovery failed: missing required characteristics")
|
||||
Logger.w { "[$address] Discovery failed: missing required characteristics" }
|
||||
service.onDisconnect(BleError.DiscoveryFailed("One or more characteristics not found"))
|
||||
}
|
||||
} else {
|
||||
Timber.w("[$address] Discovery failed: Meshtastic service not found")
|
||||
Logger.w { "[$address] Discovery failed: Meshtastic service not found" }
|
||||
service.onDisconnect(BleError.DiscoveryFailed("Meshtastic service not found"))
|
||||
}
|
||||
}
|
||||
.catch { e ->
|
||||
Timber.e(e, "[$address] Service discovery failed")
|
||||
Logger.e(e) { "[$address] Service discovery failed" }
|
||||
try {
|
||||
peripheral.disconnect()
|
||||
} catch (e2: Exception) {
|
||||
Timber.e(e2, "[$address] Failed to disconnect in discovery catch")
|
||||
Logger.e(e2) { "[$address] Failed to disconnect in discovery catch" }
|
||||
}
|
||||
service.onDisconnect(BleError.from(e))
|
||||
}
|
||||
@@ -309,29 +309,29 @@ constructor(
|
||||
@OptIn(ExperimentalUuidApi::class)
|
||||
private suspend fun setupNotifications() {
|
||||
retryCall { fromNumCharacteristic?.subscribe() }
|
||||
?.onStart { Timber.d("[$address] Subscribing to fromNumCharacteristic") }
|
||||
?.onStart { Logger.d { "[$address] Subscribing to fromNumCharacteristic" } }
|
||||
?.onEach { notifyBytes ->
|
||||
Timber.d("[$address] FromNum Notification (${notifyBytes.size} bytes), draining queue")
|
||||
Logger.d { "[$address] FromNum Notification (${notifyBytes.size} bytes), draining queue" }
|
||||
connectionScope.launch { drainPacketQueueAndDispatch() }
|
||||
}
|
||||
?.catch { e ->
|
||||
Timber.e(e, "[$address] Error subscribing to fromNumCharacteristic")
|
||||
Logger.e(e) { "[$address] Error subscribing to fromNumCharacteristic" }
|
||||
service.onDisconnect(BleError.from(e))
|
||||
}
|
||||
?.onCompletion { cause -> Timber.d("[$address] fromNum sub flow completed, cause=$cause") }
|
||||
?.onCompletion { cause -> Logger.d { "[$address] fromNum sub flow completed, cause=$cause" } }
|
||||
?.launchIn(scope = connectionScope)
|
||||
|
||||
retryCall { logRadioCharacteristic?.subscribe() }
|
||||
?.onStart { Timber.d("[$address] Subscribing to logRadioCharacteristic") }
|
||||
?.onStart { Logger.d { "[$address] Subscribing to logRadioCharacteristic" } }
|
||||
?.onEach { notifyBytes ->
|
||||
Timber.d("[$address] LogRadio Notification (${notifyBytes.size} bytes), dispatching packet")
|
||||
Logger.d { "[$address] LogRadio Notification (${notifyBytes.size} bytes), dispatching packet" }
|
||||
dispatchPacket(notifyBytes)
|
||||
}
|
||||
?.catch { e ->
|
||||
Timber.e(e, "[$address] Error subscribing to logRadioCharacteristic")
|
||||
Logger.e(e) { "[$address] Error subscribing to logRadioCharacteristic" }
|
||||
service.onDisconnect(BleError.from(e))
|
||||
}
|
||||
?.onCompletion { cause -> Timber.d("[$address] logRadio sub flow completed, cause=$cause") }
|
||||
?.onCompletion { cause -> Logger.d { "[$address] logRadio sub flow completed, cause=$cause" } }
|
||||
?.launchIn(scope = connectionScope)
|
||||
}
|
||||
|
||||
@@ -345,14 +345,13 @@ constructor(
|
||||
} catch (e: Exception) {
|
||||
currentAttempt++
|
||||
if (currentAttempt >= RETRY_COUNT) {
|
||||
Timber.e(e, "[$address] BLE operation failed after $RETRY_COUNT attempts, giving up")
|
||||
Logger.e(e) { "[$address] BLE operation failed after $RETRY_COUNT attempts, giving up" }
|
||||
throw e
|
||||
}
|
||||
Timber.w(
|
||||
e,
|
||||
Logger.w(e) {
|
||||
"[$address] BLE operation failed (attempt $currentAttempt/$RETRY_COUNT), " +
|
||||
"retrying in ${RETRY_DELAY_MS}ms...",
|
||||
)
|
||||
"retrying in ${RETRY_DELAY_MS}ms..."
|
||||
}
|
||||
delay(RETRY_DELAY_MS)
|
||||
}
|
||||
}
|
||||
@@ -368,7 +367,7 @@ constructor(
|
||||
override fun handleSendToRadio(p: ByteArray) {
|
||||
toRadioCharacteristic?.let { characteristic ->
|
||||
if (peripheral == null) {
|
||||
Timber.w("[$address] BLE peripheral is null, cannot send packet")
|
||||
Logger.w { "[$address] BLE peripheral is null, cannot send packet" }
|
||||
return@let
|
||||
}
|
||||
connectionScope.launch {
|
||||
@@ -383,24 +382,23 @@ constructor(
|
||||
retryCall {
|
||||
packetsSent++
|
||||
bytesSent += p.size
|
||||
Timber.d(
|
||||
Logger.d {
|
||||
"[$address] Writing packet #$packetsSent to toRadioCharacteristic with $writeType - " +
|
||||
"${p.size} bytes (Total TX: $bytesSent bytes)",
|
||||
)
|
||||
"${p.size} bytes (Total TX: $bytesSent bytes)"
|
||||
}
|
||||
characteristic.write(p, writeType = writeType)
|
||||
}
|
||||
drainPacketQueueAndDispatch()
|
||||
} catch (e: Exception) {
|
||||
Timber.e(
|
||||
e,
|
||||
Logger.e(e) {
|
||||
"[$address] Failed to write packet to toRadioCharacteristic after " +
|
||||
"$packetsSent successful writes",
|
||||
)
|
||||
"$packetsSent successful writes"
|
||||
}
|
||||
service.onDisconnect(BleError.from(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: Timber.w("[$address] toRadio characteristic unavailable, can't send data")
|
||||
} ?: Logger.w { "[$address] toRadio characteristic unavailable, can't send data" }
|
||||
}
|
||||
|
||||
/** Closes the connection to the device. */
|
||||
@@ -412,12 +410,12 @@ constructor(
|
||||
} else {
|
||||
0
|
||||
}
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[$address] BLE close() called - " +
|
||||
"Uptime: ${uptime}ms, " +
|
||||
"Packets RX: $packetsReceived ($bytesReceived bytes), " +
|
||||
"Packets TX: $packetsSent ($bytesSent bytes)",
|
||||
)
|
||||
"Packets TX: $packetsSent ($bytesSent bytes)"
|
||||
}
|
||||
connectionScope.cancel()
|
||||
peripheral?.disconnect()
|
||||
service.onDisconnect(true)
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
package com.geeksville.mesh.repository.radio
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
|
||||
import org.meshtastic.core.model.util.anonymize
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/** Bluetooth backend implementation. */
|
||||
@@ -35,7 +35,7 @@ constructor(
|
||||
override fun addressValid(rest: String): Boolean {
|
||||
val allPaired = bluetoothRepository.state.value.bondedDevices.map { it.address }.toSet()
|
||||
return if (!allPaired.contains(rest)) {
|
||||
Timber.w("Ignoring stale bond to ${rest.anonymize}")
|
||||
Logger.w { "Ignoring stale bond to ${rest.anonymize}" }
|
||||
false
|
||||
} else {
|
||||
true
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.app.Application
|
||||
import android.provider.Settings
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.BuildConfig
|
||||
import com.geeksville.mesh.android.BinaryLogFile
|
||||
import com.geeksville.mesh.android.BuildUtils
|
||||
@@ -50,7 +51,6 @@ import org.meshtastic.core.model.util.anonymize
|
||||
import org.meshtastic.core.prefs.radio.RadioPrefs
|
||||
import org.meshtastic.core.service.ConnectionState
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -141,7 +141,7 @@ constructor(
|
||||
fun keepAlive(now: Long = System.currentTimeMillis()) {
|
||||
if (now - lastHeartbeatMillis > HEARTBEAT_INTERVAL_MILLIS) {
|
||||
if (radioIf is SerialInterface) {
|
||||
Timber.i("Sending ToRadio heartbeat")
|
||||
Logger.i { "Sending ToRadio heartbeat" }
|
||||
val heartbeat =
|
||||
MeshProtos.ToRadio.newBuilder().setHeartbeat(MeshProtos.Heartbeat.getDefaultInstance()).build()
|
||||
handleSendToRadio(heartbeat.toByteArray())
|
||||
@@ -206,7 +206,7 @@ constructor(
|
||||
}
|
||||
|
||||
private fun broadcastConnectionChanged(newState: ConnectionState) {
|
||||
Timber.d("Broadcasting connection state change to $newState")
|
||||
Logger.d { "Broadcasting connection state change to $newState" }
|
||||
processLifecycle.coroutineScope.launch(dispatchers.default) { _connectionState.emit(newState) }
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ constructor(
|
||||
receivedPacketsLog.write(p)
|
||||
receivedPacketsLog.flush()
|
||||
} catch (t: Throwable) {
|
||||
Timber.w(t, "Failed to write receive log in handleFromRadio")
|
||||
Logger.w(t) { "Failed to write receive log in handleFromRadio" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,13 +231,13 @@ constructor(
|
||||
keepAlive(System.currentTimeMillis())
|
||||
}
|
||||
|
||||
// ignoreException { Timber.d("FromRadio: ${MeshProtos.FromRadio.parseFrom(p)}") }
|
||||
// ignoreException { Logger.d { "FromRadio: ${MeshProtos.FromRadio.parseFrom(p }}" } }
|
||||
|
||||
try {
|
||||
processLifecycle.coroutineScope.launch(dispatchers.io) { _receivedData.emit(p) }
|
||||
emitReceiveActivity()
|
||||
} catch (t: Throwable) {
|
||||
Timber.e(t, "RadioInterfaceService.handleFromRadio failed while emitting data")
|
||||
Logger.e(t) { "RadioInterfaceService.handleFromRadio failed while emitting data" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,13 +262,13 @@ constructor(
|
||||
/** Start our configured interface (if it isn't already running) */
|
||||
private fun startInterface() {
|
||||
if (radioIf !is NopInterface) {
|
||||
Timber.w("Can't start interface - $radioIf is already running")
|
||||
Logger.w { "Can't start interface - $radioIf is already running" }
|
||||
} else {
|
||||
val address = getBondedDeviceAddress()
|
||||
if (address == null) {
|
||||
Timber.w("No bonded mesh radio, can't start interface")
|
||||
Logger.w { "No bonded mesh radio, can't start interface" }
|
||||
} else {
|
||||
Timber.i("Starting radio ${address.anonymize}")
|
||||
Logger.i { "Starting radio ${address.anonymize}" }
|
||||
isStarted = true
|
||||
|
||||
if (logSends) {
|
||||
@@ -285,7 +285,7 @@ constructor(
|
||||
|
||||
private fun stopInterface() {
|
||||
val r = radioIf
|
||||
Timber.i("stopping interface $r")
|
||||
Logger.i { "stopping interface $r" }
|
||||
isStarted = false
|
||||
radioIf = interfaceFactory.nopInterface
|
||||
r.close()
|
||||
@@ -314,7 +314,7 @@ constructor(
|
||||
*/
|
||||
private fun setBondedDeviceAddress(address: String?): Boolean =
|
||||
if (getBondedDeviceAddress() == address && isStarted && _connectionState.value == ConnectionState.Connected) {
|
||||
Timber.w("Ignoring setBondedDevice ${address.anonymize}, because we are already using that device")
|
||||
Logger.w { "Ignoring setBondedDevice ${address.anonymize}, because we are already using that device" }
|
||||
false
|
||||
} else {
|
||||
// Record that this use has configured a new radio
|
||||
@@ -325,7 +325,7 @@ constructor(
|
||||
|
||||
// The device address "n" can be used to mean none
|
||||
|
||||
Timber.d("Setting bonded device to ${address.anonymize}")
|
||||
Logger.d { "Setting bonded device to ${address.anonymize}" }
|
||||
|
||||
// Stores the address if non-null, otherwise removes the pref
|
||||
radioPrefs.devAddr = address
|
||||
@@ -366,14 +366,14 @@ constructor(
|
||||
// Use tryEmit for SharedFlow as it's non-blocking
|
||||
val emitted = _meshActivity.tryEmit(MeshActivity.Send)
|
||||
if (!emitted) {
|
||||
Timber.d("MeshActivity.Send event was not emitted due to buffer overflow or no collectors")
|
||||
Logger.d { "MeshActivity.Send event was not emitted due to buffer overflow or no collectors" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun emitReceiveActivity() {
|
||||
val emitted = _meshActivity.tryEmit(MeshActivity.Receive)
|
||||
if (!emitted) {
|
||||
Timber.d("MeshActivity.Receive event was not emitted due to buffer overflow or no collectors")
|
||||
Logger.d { "MeshActivity.Receive event was not emitted due to buffer overflow or no collectors" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
|
||||
package com.geeksville.mesh.repository.radio
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.repository.usb.SerialConnection
|
||||
import com.geeksville.mesh.repository.usb.SerialConnectionListener
|
||||
import com.geeksville.mesh.repository.usb.UsbRepository
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
/** An interface that assumes we are talking to a meshtastic device via USB serial */
|
||||
@@ -48,10 +48,10 @@ constructor(
|
||||
override fun connect() {
|
||||
val device = serialInterfaceSpec.findSerial(address)
|
||||
if (device == null) {
|
||||
Timber.e("[$address] Serial device not found at address")
|
||||
Logger.e { "[$address] Serial device not found at address" }
|
||||
} else {
|
||||
val connectStart = System.currentTimeMillis()
|
||||
Timber.i("[$address] Opening serial device: $device")
|
||||
Logger.i { "[$address] Opening serial device: $device" }
|
||||
|
||||
var packetsReceived = 0
|
||||
var bytesReceived = 0L
|
||||
@@ -60,7 +60,7 @@ constructor(
|
||||
val onConnect: () -> Unit = {
|
||||
connectionStartTime = System.currentTimeMillis()
|
||||
val connectionTime = connectionStartTime - connectStart
|
||||
Timber.i("[$address] Serial device connected in ${connectionTime}ms")
|
||||
Logger.i { "[$address] Serial device connected in ${connectionTime}ms" }
|
||||
super.connect()
|
||||
}
|
||||
|
||||
@@ -69,9 +69,9 @@ constructor(
|
||||
device,
|
||||
object : SerialConnectionListener {
|
||||
override fun onMissingPermission() {
|
||||
Timber.e(
|
||||
"[$address] Serial connection failed - missing USB permissions for device: $device",
|
||||
)
|
||||
Logger.e {
|
||||
"[$address] Serial connection failed - missing USB permissions for device: $device"
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConnected() {
|
||||
@@ -81,10 +81,10 @@ constructor(
|
||||
override fun onDataReceived(bytes: ByteArray) {
|
||||
packetsReceived++
|
||||
bytesReceived += bytes.size
|
||||
Timber.d(
|
||||
Logger.d {
|
||||
"[$address] Serial received packet #$packetsReceived - " +
|
||||
"${bytes.size} byte(s) (Total RX: $bytesReceived bytes)",
|
||||
)
|
||||
"${bytes.size} byte(s) (Total RX: $bytesReceived bytes)"
|
||||
}
|
||||
bytes.forEach(::readChar)
|
||||
}
|
||||
|
||||
@@ -95,13 +95,15 @@ constructor(
|
||||
} else {
|
||||
0
|
||||
}
|
||||
thrown?.let { e -> Timber.e(e, "[$address] Serial error after ${uptime}ms: ${e.message}") }
|
||||
Timber.w(
|
||||
thrown?.let { e ->
|
||||
Logger.e(e) { "[$address] Serial error after ${uptime}ms: ${e.message}" }
|
||||
}
|
||||
Logger.w {
|
||||
"[$address] Serial device disconnected - " +
|
||||
"Device: $device, " +
|
||||
"Uptime: ${uptime}ms, " +
|
||||
"Packets RX: $packetsReceived ($bytesReceived bytes)",
|
||||
)
|
||||
"Packets RX: $packetsReceived ($bytesReceived bytes)"
|
||||
}
|
||||
onDeviceDisconnect(false)
|
||||
}
|
||||
},
|
||||
@@ -116,10 +118,10 @@ constructor(
|
||||
override fun sendBytes(p: ByteArray) {
|
||||
val conn = connRef.get()
|
||||
if (conn != null) {
|
||||
Timber.d("[$address] Serial sending ${p.size} bytes")
|
||||
Logger.d { "[$address] Serial sending ${p.size} bytes" }
|
||||
conn.sendBytes(p)
|
||||
} else {
|
||||
Timber.w("[$address] Serial connection not available, cannot send ${p.size} bytes")
|
||||
Logger.w { "[$address] Serial connection not available, cannot send ${p.size} bytes" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package com.geeksville.mesh.repository.radio
|
||||
|
||||
import timber.log.Timber
|
||||
import co.touchlab.kermit.Logger
|
||||
|
||||
/**
|
||||
* An interface that assumes we are talking to a meshtastic device over some sort of stream connection (serial or TCP
|
||||
@@ -41,7 +41,7 @@ abstract class StreamInterface(protected val service: RadioInterfaceService) : I
|
||||
private var packetLen = 0
|
||||
|
||||
override fun close() {
|
||||
Timber.d("Closing stream for good")
|
||||
Logger.d { "Closing stream for good" }
|
||||
onDeviceDisconnect(true)
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ abstract class StreamInterface(protected val service: RadioInterfaceService) : I
|
||||
when (val c = b.toInt().toChar()) {
|
||||
'\r' -> {} // ignore
|
||||
'\n' -> {
|
||||
Timber.d("DeviceLog: $debugLineBuf")
|
||||
Logger.d { "DeviceLog: $debugLineBuf" }
|
||||
debugLineBuf.clear()
|
||||
}
|
||||
else -> debugLineBuf.append(c)
|
||||
@@ -104,7 +104,7 @@ abstract class StreamInterface(protected val service: RadioInterfaceService) : I
|
||||
var nextPtr = ptr + 1
|
||||
|
||||
fun lostSync() {
|
||||
Timber.e("Lost protocol sync")
|
||||
Logger.e { "Lost protocol sync" }
|
||||
nextPtr = 0
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package com.geeksville.mesh.repository.radio
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.repository.network.NetworkRepository
|
||||
import com.geeksville.mesh.util.Exceptions
|
||||
@@ -25,7 +26,6 @@ import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.IOException
|
||||
@@ -67,12 +67,12 @@ class TCPInterface @AssistedInject constructor(service: RadioInterfaceService, @
|
||||
override fun sendBytes(p: ByteArray) {
|
||||
packetsSent++
|
||||
bytesSent += p.size
|
||||
Timber.d("[$address] TCP sending packet #$packetsSent - ${p.size} bytes (Total TX: $bytesSent bytes)")
|
||||
Logger.d { "[$address] TCP sending packet #$packetsSent - ${p.size} bytes (Total TX: $bytesSent bytes)" }
|
||||
outStream.write(p)
|
||||
}
|
||||
|
||||
override fun flushBytes() {
|
||||
Timber.d("[$address] TCP flushing output stream")
|
||||
Logger.d { "[$address] TCP flushing output stream" }
|
||||
outStream.flush()
|
||||
}
|
||||
|
||||
@@ -85,13 +85,13 @@ class TCPInterface @AssistedInject constructor(service: RadioInterfaceService, @
|
||||
} else {
|
||||
0
|
||||
}
|
||||
Timber.w(
|
||||
Logger.w {
|
||||
"[$address] TCP disconnecting - " +
|
||||
"Uptime: ${uptime}ms, " +
|
||||
"Packets RX: $packetsReceived ($bytesReceived bytes), " +
|
||||
"Packets TX: $packetsSent ($bytesSent bytes), " +
|
||||
"Timeout events: $timeoutEvents",
|
||||
)
|
||||
"Timeout events: $timeoutEvents"
|
||||
}
|
||||
s.close()
|
||||
socket = null
|
||||
}
|
||||
@@ -110,7 +110,7 @@ class TCPInterface @AssistedInject constructor(service: RadioInterfaceService, @
|
||||
} else {
|
||||
0
|
||||
}
|
||||
Timber.e(ex, "[$address] TCP IOException after ${uptime}ms - ${ex.message}")
|
||||
Logger.e(ex) { "[$address] TCP IOException after ${uptime}ms - ${ex.message}" }
|
||||
onDeviceDisconnect(false)
|
||||
} catch (ex: Throwable) {
|
||||
val uptime =
|
||||
@@ -119,38 +119,38 @@ class TCPInterface @AssistedInject constructor(service: RadioInterfaceService, @
|
||||
} else {
|
||||
0
|
||||
}
|
||||
Timber.e(ex, "[$address] TCP exception after ${uptime}ms - ${ex.message}")
|
||||
Logger.e(ex) { "[$address] TCP exception after ${uptime}ms - ${ex.message}" }
|
||||
Exceptions.report(ex, "Exception in TCP reader")
|
||||
onDeviceDisconnect(false)
|
||||
}
|
||||
|
||||
if (retryCount > MAX_RETRIES_ALLOWED) {
|
||||
Timber.e("[$address] TCP max retries ($MAX_RETRIES_ALLOWED) exceeded, giving up")
|
||||
Logger.e { "[$address] TCP max retries ($MAX_RETRIES_ALLOWED) exceeded, giving up" }
|
||||
break
|
||||
}
|
||||
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[$address] TCP reconnect attempt #$retryCount in ${backoffDelay / 1000}s " +
|
||||
"(backoff: ${backoffDelay}ms)",
|
||||
)
|
||||
"(backoff: ${backoffDelay}ms)"
|
||||
}
|
||||
delay(backoffDelay)
|
||||
|
||||
retryCount++
|
||||
backoffDelay = minOf(backoffDelay * 2, MAX_BACKOFF_MILLIS)
|
||||
}
|
||||
Timber.i("[$address] TCP reader exiting")
|
||||
Logger.i { "[$address] TCP reader exiting" }
|
||||
}
|
||||
}
|
||||
|
||||
// Create a socket to make the connection with the server
|
||||
private suspend fun startConnect() = withContext(Dispatchers.IO) {
|
||||
val attemptStart = System.currentTimeMillis()
|
||||
Timber.i("[$address] TCP connection attempt starting...")
|
||||
Logger.i { "[$address] TCP connection attempt starting..." }
|
||||
|
||||
val (host, port) =
|
||||
address.split(":", limit = 2).let { it[0] to (it.getOrNull(1)?.toIntOrNull() ?: SERVICE_PORT) }
|
||||
|
||||
Timber.d("[$address] Resolving host '$host' and connecting to port $port...")
|
||||
Logger.d { "[$address] Resolving host '$host' and connecting to port $port..." }
|
||||
|
||||
Socket(InetAddress.getByName(host), port).use { socket ->
|
||||
socket.tcpNoDelay = true
|
||||
@@ -159,10 +159,10 @@ class TCPInterface @AssistedInject constructor(service: RadioInterfaceService, @
|
||||
|
||||
val connectTime = System.currentTimeMillis() - attemptStart
|
||||
connectionStartTime = System.currentTimeMillis()
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[$address] TCP socket connected in ${connectTime}ms - " +
|
||||
"Local: ${socket.localSocketAddress}, Remote: ${socket.remoteSocketAddress}",
|
||||
)
|
||||
"Local: ${socket.localSocketAddress}, Remote: ${socket.remoteSocketAddress}"
|
||||
}
|
||||
|
||||
BufferedOutputStream(socket.getOutputStream()).use { outputStream ->
|
||||
outStream = outputStream
|
||||
@@ -178,7 +178,9 @@ class TCPInterface @AssistedInject constructor(service: RadioInterfaceService, @
|
||||
try { // close after 90s of inactivity
|
||||
val c = inputStream.read()
|
||||
if (c == -1) {
|
||||
Timber.w("[$address] TCP got EOF on stream after $packetsReceived packets received")
|
||||
Logger.w {
|
||||
"[$address] TCP got EOF on stream after $packetsReceived packets received"
|
||||
}
|
||||
break
|
||||
} else {
|
||||
timeoutCount = 0
|
||||
@@ -190,20 +192,20 @@ class TCPInterface @AssistedInject constructor(service: RadioInterfaceService, @
|
||||
timeoutCount++
|
||||
timeoutEvents++
|
||||
if (timeoutCount % TIMEOUT_LOG_INTERVAL == 0) {
|
||||
Timber.d(
|
||||
Logger.d {
|
||||
"[$address] TCP socket timeout count: $timeoutCount/$SOCKET_RETRIES " +
|
||||
"(total timeouts: $timeoutEvents)",
|
||||
)
|
||||
"(total timeouts: $timeoutEvents)"
|
||||
}
|
||||
}
|
||||
// Ignore and start another read
|
||||
}
|
||||
}
|
||||
if (timeoutCount >= SOCKET_RETRIES) {
|
||||
val inactivityMs = SOCKET_RETRIES * SOCKET_TIMEOUT
|
||||
Timber.w(
|
||||
Logger.w {
|
||||
"[$address] TCP closing connection due to $SOCKET_RETRIES consecutive timeouts " +
|
||||
"(${inactivityMs}ms of inactivity)",
|
||||
)
|
||||
"(${inactivityMs}ms of inactivity)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
package com.geeksville.mesh.repository.usb
|
||||
|
||||
import android.hardware.usb.UsbManager
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.util.ignoreException
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver
|
||||
import com.hoho.android.usbserial.driver.UsbSerialPort
|
||||
import com.hoho.android.usbserial.util.SerialInputOutputManager
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
@@ -40,7 +40,7 @@ internal class SerialConnectionImpl(
|
||||
|
||||
override fun sendBytes(bytes: ByteArray) {
|
||||
ioRef.get()?.let {
|
||||
Timber.d("writing ${bytes.size} byte(s)")
|
||||
Logger.d { "writing ${bytes.size} byte(s }" }
|
||||
it.writeAsync(bytes)
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ internal class SerialConnectionImpl(
|
||||
|
||||
// Allow a short amount of time for the manager to quit (so the port can be cleanly closed)
|
||||
if (waitForStopped) {
|
||||
Timber.d("Waiting for USB manager to stop...")
|
||||
Logger.d { "Waiting for USB manager to stop..." }
|
||||
closedLatch.await(1, TimeUnit.SECONDS)
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ internal class SerialConnectionImpl(
|
||||
port.dtr = true
|
||||
port.rts = true
|
||||
|
||||
Timber.d("Starting serial reader thread")
|
||||
Logger.d { "Starting serial reader thread" }
|
||||
val io =
|
||||
SerialInputOutputManager(
|
||||
port,
|
||||
|
||||
@@ -23,9 +23,9 @@ import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.hardware.usb.UsbDevice
|
||||
import android.hardware.usb.UsbManager
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.util.exceptionReporter
|
||||
import com.geeksville.mesh.util.getParcelableExtraCompat
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/** A helper class to call onChanged when bluetooth is enabled or disabled or when permissions are changed. */
|
||||
@@ -44,15 +44,15 @@ class UsbBroadcastReceiver @Inject constructor(private val usbRepository: UsbRep
|
||||
|
||||
when (intent.action) {
|
||||
UsbManager.ACTION_USB_DEVICE_DETACHED -> {
|
||||
Timber.d("USB device '$deviceName' was detached")
|
||||
Logger.d { "USB device '$deviceName' was detached" }
|
||||
usbRepository.refreshState()
|
||||
}
|
||||
UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
|
||||
Timber.d("USB device '$deviceName' was attached")
|
||||
Logger.d { "USB device '$deviceName' was attached" }
|
||||
usbRepository.refreshState()
|
||||
}
|
||||
UsbManager.EXTRA_PERMISSION_GRANTED -> {
|
||||
Timber.d("USB device '$deviceName' was granted permission")
|
||||
Logger.d { "USB device '$deviceName' was granted permission" }
|
||||
usbRepository.refreshState()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.util.Log
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.app.ServiceCompat
|
||||
import androidx.core.location.LocationCompat
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.BuildConfig
|
||||
import com.geeksville.mesh.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.model.NO_DEVICE_SELECTED
|
||||
@@ -127,7 +128,6 @@ import org.meshtastic.proto.fromRadio
|
||||
import org.meshtastic.proto.position
|
||||
import org.meshtastic.proto.telemetry
|
||||
import org.meshtastic.proto.user
|
||||
import timber.log.Timber
|
||||
import java.util.ArrayDeque
|
||||
import java.util.Locale
|
||||
import java.util.UUID
|
||||
@@ -280,12 +280,20 @@ class MeshService : Service() {
|
||||
crossinline message: () -> String,
|
||||
) {
|
||||
if (!BuildConfig.DEBUG) return
|
||||
val timber = Timber.tag(HISTORY_TAG)
|
||||
val severity =
|
||||
when (priority) {
|
||||
Log.VERBOSE -> co.touchlab.kermit.Severity.Verbose
|
||||
Log.DEBUG -> co.touchlab.kermit.Severity.Debug
|
||||
Log.INFO -> co.touchlab.kermit.Severity.Info
|
||||
Log.WARN -> co.touchlab.kermit.Severity.Warn
|
||||
Log.ERROR -> co.touchlab.kermit.Severity.Error
|
||||
else -> co.touchlab.kermit.Severity.Info
|
||||
}
|
||||
val msg = message()
|
||||
if (throwable != null) {
|
||||
timber.log(priority, throwable, msg)
|
||||
Logger.log(severity, HISTORY_TAG, throwable, msg)
|
||||
} else {
|
||||
timber.log(priority, msg)
|
||||
Logger.log(severity, HISTORY_TAG, null, msg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +358,7 @@ class MeshService : Service() {
|
||||
|
||||
private fun stopLocationRequests() {
|
||||
if (locationFlow?.isActive == true) {
|
||||
Timber.i("Stopping location requests")
|
||||
Logger.i { "Stopping location requests" }
|
||||
locationFlow?.cancel()
|
||||
locationFlow = null
|
||||
}
|
||||
@@ -391,7 +399,7 @@ class MeshService : Service() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Timber.i("Creating mesh service")
|
||||
Logger.i { "Creating mesh service" }
|
||||
serviceNotifications.initChannels()
|
||||
// Switch to the IO thread
|
||||
serviceScope.handledLaunch { radioInterfaceService.connect() }
|
||||
@@ -408,7 +416,7 @@ class MeshService : Service() {
|
||||
.onEach(::onReceiveFromRadio)
|
||||
.launchIn(serviceScope)
|
||||
radioInterfaceService.connectionError
|
||||
.onEach { error -> Timber.e("BLE Connection Error: ${error.message}") }
|
||||
.onEach { error -> Logger.e { "BLE Connection Error: ${error.message}" } }
|
||||
.launchIn(serviceScope)
|
||||
radioConfigRepository.localConfigFlow.onEach { localConfig = it }.launchIn(serviceScope)
|
||||
radioConfigRepository.moduleConfigFlow.onEach { moduleConfig = it }.launchIn(serviceScope)
|
||||
@@ -461,7 +469,7 @@ class MeshService : Service() {
|
||||
val a = radioInterfaceService.getBondedDeviceAddress()
|
||||
val wantForeground = a != null && a != NO_DEVICE_SELECTED
|
||||
|
||||
Timber.i("Requesting foreground service=$wantForeground")
|
||||
Logger.i { "Requesting foreground service=$wantForeground" }
|
||||
val notification = updateServiceStatusNotification()
|
||||
|
||||
try {
|
||||
@@ -480,7 +488,7 @@ class MeshService : Service() {
|
||||
},
|
||||
)
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Error starting foreground service")
|
||||
Logger.e(ex) { "Error starting foreground service" }
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
return if (!wantForeground) {
|
||||
@@ -492,7 +500,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Timber.i("Destroying mesh service")
|
||||
Logger.i { "Destroying mesh service" }
|
||||
|
||||
// Make sure we aren't using the notification first
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
@@ -510,7 +518,7 @@ class MeshService : Service() {
|
||||
|
||||
/** discard entire node db & message state - used when downloading a new db from the device */
|
||||
private fun discardNodeDB() {
|
||||
Timber.d("Discarding NodeDB")
|
||||
Logger.d { "Discarding NodeDB" }
|
||||
myNodeInfo = null
|
||||
nodeDBbyNodeNum.clear()
|
||||
isNodeDbReady = false
|
||||
@@ -836,7 +844,7 @@ class MeshService : Service() {
|
||||
// We ignore most messages that we sent
|
||||
val fromUs = myInfo.myNodeNum == packet.from
|
||||
|
||||
Timber.d("Received data from $fromId, portnum=${data.portnum} ${bytes.size} bytes")
|
||||
Logger.d { "Received data from $fromId, portnum=${data.portnum} ${bytes.size} bytes" }
|
||||
|
||||
dataPacket.status = MessageStatus.RECEIVED
|
||||
|
||||
@@ -851,24 +859,24 @@ class MeshService : Service() {
|
||||
when (data.portnumValue) {
|
||||
Portnums.PortNum.TEXT_MESSAGE_APP_VALUE -> {
|
||||
if (data.replyId != 0 && data.emoji == 0) {
|
||||
Timber.d("Received REPLY from $fromId")
|
||||
Logger.d { "Received REPLY from $fromId" }
|
||||
rememberDataPacket(dataPacket)
|
||||
} else if (data.replyId != 0 && data.emoji != 0) {
|
||||
Timber.d("Received EMOJI from $fromId")
|
||||
Logger.d { "Received EMOJI from $fromId" }
|
||||
rememberReaction(packet)
|
||||
} else {
|
||||
Timber.d("Received CLEAR_TEXT from $fromId")
|
||||
Logger.d { "Received CLEAR_TEXT from $fromId" }
|
||||
rememberDataPacket(dataPacket)
|
||||
}
|
||||
}
|
||||
|
||||
Portnums.PortNum.ALERT_APP_VALUE -> {
|
||||
Timber.d("Received ALERT_APP from $fromId")
|
||||
Logger.d { "Received ALERT_APP from $fromId" }
|
||||
rememberDataPacket(dataPacket)
|
||||
}
|
||||
|
||||
Portnums.PortNum.WAYPOINT_APP_VALUE -> {
|
||||
Timber.d("Received WAYPOINT_APP from $fromId")
|
||||
Logger.d { "Received WAYPOINT_APP from $fromId" }
|
||||
val u = MeshProtos.Waypoint.parseFrom(data.payload)
|
||||
// Validate locked Waypoints from the original sender
|
||||
if (u.lockedTo != 0 && u.lockedTo != packet.from) return
|
||||
@@ -876,10 +884,10 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
Portnums.PortNum.POSITION_APP_VALUE -> {
|
||||
Timber.d("Received POSITION_APP from $fromId")
|
||||
Logger.d { "Received POSITION_APP from $fromId" }
|
||||
val u = MeshProtos.Position.parseFrom(data.payload)
|
||||
if (data.wantResponse && u.latitudeI == 0 && u.longitudeI == 0) {
|
||||
Timber.d("Ignoring nop position update from position request")
|
||||
Logger.d { "Ignoring nop position update from position request" }
|
||||
} else {
|
||||
handleReceivedPosition(packet.from, u, dataPacket.time)
|
||||
}
|
||||
@@ -887,7 +895,7 @@ class MeshService : Service() {
|
||||
|
||||
Portnums.PortNum.NODEINFO_APP_VALUE ->
|
||||
if (!fromUs) {
|
||||
Timber.d("Received NODEINFO_APP from $fromId")
|
||||
Logger.d { "Received NODEINFO_APP from $fromId" }
|
||||
val u =
|
||||
MeshProtos.User.parseFrom(data.payload).copy {
|
||||
if (isLicensed) clearPublicKey()
|
||||
@@ -898,7 +906,7 @@ class MeshService : Service() {
|
||||
|
||||
// Handle new telemetry info
|
||||
Portnums.PortNum.TELEMETRY_APP_VALUE -> {
|
||||
Timber.d("Received TELEMETRY_APP from $fromId")
|
||||
Logger.d { "Received TELEMETRY_APP from $fromId" }
|
||||
val u =
|
||||
TelemetryProtos.Telemetry.parseFrom(data.payload).copy {
|
||||
if (time == 0) time = (dataPacket.time / 1000L).toInt()
|
||||
@@ -907,7 +915,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
Portnums.PortNum.ROUTING_APP_VALUE -> {
|
||||
Timber.d("Received ROUTING_APP from $fromId")
|
||||
Logger.d { "Received ROUTING_APP from $fromId" }
|
||||
// We always send ACKs to other apps, because they might care about the
|
||||
// messages they sent
|
||||
shouldBroadcast = true
|
||||
@@ -922,41 +930,41 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
Portnums.PortNum.ADMIN_APP_VALUE -> {
|
||||
Timber.d("Received ADMIN_APP from $fromId")
|
||||
Logger.d { "Received ADMIN_APP from $fromId" }
|
||||
val u = AdminProtos.AdminMessage.parseFrom(data.payload)
|
||||
handleReceivedAdmin(packet.from, u)
|
||||
shouldBroadcast = false
|
||||
}
|
||||
|
||||
Portnums.PortNum.PAXCOUNTER_APP_VALUE -> {
|
||||
Timber.d("Received PAXCOUNTER_APP from $fromId")
|
||||
Logger.d { "Received PAXCOUNTER_APP from $fromId" }
|
||||
val p = PaxcountProtos.Paxcount.parseFrom(data.payload)
|
||||
handleReceivedPaxcounter(packet.from, p)
|
||||
shouldBroadcast = false
|
||||
}
|
||||
|
||||
Portnums.PortNum.STORE_FORWARD_APP_VALUE -> {
|
||||
Timber.d("Received STORE_FORWARD_APP from $fromId")
|
||||
Logger.d { "Received STORE_FORWARD_APP from $fromId" }
|
||||
val u = StoreAndForwardProtos.StoreAndForward.parseFrom(data.payload)
|
||||
handleReceivedStoreAndForward(dataPacket, u)
|
||||
shouldBroadcast = false
|
||||
}
|
||||
|
||||
Portnums.PortNum.RANGE_TEST_APP_VALUE -> {
|
||||
Timber.d("Received RANGE_TEST_APP from $fromId")
|
||||
Logger.d { "Received RANGE_TEST_APP from $fromId" }
|
||||
if (!moduleConfig.rangeTest.enabled) return
|
||||
val u = dataPacket.copy(dataType = Portnums.PortNum.TEXT_MESSAGE_APP_VALUE)
|
||||
rememberDataPacket(u)
|
||||
}
|
||||
|
||||
Portnums.PortNum.DETECTION_SENSOR_APP_VALUE -> {
|
||||
Timber.d("Received DETECTION_SENSOR_APP from $fromId")
|
||||
Logger.d { "Received DETECTION_SENSOR_APP from $fromId" }
|
||||
val u = dataPacket.copy(dataType = Portnums.PortNum.TEXT_MESSAGE_APP_VALUE)
|
||||
rememberDataPacket(u)
|
||||
}
|
||||
|
||||
Portnums.PortNum.TRACEROUTE_APP_VALUE -> {
|
||||
Timber.d("Received TRACEROUTE_APP from $fromId")
|
||||
Logger.d { "Received TRACEROUTE_APP from $fromId" }
|
||||
val routeDiscovery = packet.fullRouteDiscovery
|
||||
val full = packet.getFullTracerouteResponse(::getUserName)
|
||||
if (full != null) {
|
||||
@@ -988,7 +996,7 @@ class MeshService : Service() {
|
||||
if (start != null) {
|
||||
val elapsedMs = System.currentTimeMillis() - start
|
||||
val seconds = elapsedMs / 1000.0
|
||||
Timber.i("Traceroute $requestId complete in $seconds s")
|
||||
Logger.i { "Traceroute $requestId complete in $seconds s" }
|
||||
"$full\n\nDuration: ${"%.1f".format(seconds)} s"
|
||||
} else {
|
||||
full
|
||||
@@ -1012,9 +1020,9 @@ class MeshService : Service() {
|
||||
|
||||
Portnums.PortNum.NEIGHBORINFO_APP_VALUE -> {
|
||||
val requestId = packet.decoded.requestId
|
||||
Timber.d("Processing NEIGHBORINFO_APP packet with requestId: $requestId")
|
||||
Logger.d { "Processing NEIGHBORINFO_APP packet with requestId: $requestId" }
|
||||
val start = neighborInfoStartTimes.remove(requestId)
|
||||
Timber.d("Found start time for requestId $requestId: $start")
|
||||
Logger.d { "Found start time for requestId $requestId: $start" }
|
||||
|
||||
val info =
|
||||
runCatching { MeshProtos.NeighborInfo.parseFrom(data.payload.toByteArray()) }.getOrNull()
|
||||
@@ -1022,7 +1030,7 @@ class MeshService : Service() {
|
||||
// Store the last neighbor info from our connected radio
|
||||
if (info != null && packet.from == myInfo.myNodeNum) {
|
||||
lastNeighborInfo = info
|
||||
Timber.d("Stored last neighbor info from connected radio")
|
||||
Logger.d { "Stored last neighbor info from connected radio" }
|
||||
}
|
||||
|
||||
// Only show response if packet is addressed to us and we sent a request in the last 3 minutes
|
||||
@@ -1060,27 +1068,25 @@ class MeshService : Service() {
|
||||
if (start != null) {
|
||||
val elapsedMs = System.currentTimeMillis() - start
|
||||
val seconds = elapsedMs / 1000.0
|
||||
Timber.i("Neighbor info $requestId complete in $seconds s")
|
||||
Logger.i { "Neighbor info $requestId complete in $seconds s" }
|
||||
"$formatted\n\nDuration: ${"%.1f".format(seconds)} s"
|
||||
} else {
|
||||
Timber.w("No start time found for neighbor info requestId: $requestId")
|
||||
Logger.w { "No start time found for neighbor info requestId: $requestId" }
|
||||
formatted
|
||||
}
|
||||
serviceRepository.setNeighborInfoResponse(response)
|
||||
} else {
|
||||
Timber.d(
|
||||
"Neighbor info response filtered: ToUs=%s, isRecentRequest=%s",
|
||||
isAddressedToUs,
|
||||
isRecentRequest,
|
||||
)
|
||||
Logger.d {
|
||||
"Neighbor info response filtered: ToUs=$isAddressedToUs, isRecentRequest=$isRecentRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Portnums.PortNum.NEIGHBORINFO_APP_VALUE -> {
|
||||
val requestId = packet.decoded.requestId
|
||||
Timber.d("Processing NEIGHBORINFO_APP packet with requestId: $requestId")
|
||||
Logger.d { "Processing NEIGHBORINFO_APP packet with requestId: $requestId" }
|
||||
val start = neighborInfoStartTimes.remove(requestId)
|
||||
Timber.d("Found start time for requestId $requestId: $start")
|
||||
Logger.d { "Found start time for requestId $requestId: $start" }
|
||||
|
||||
val info =
|
||||
runCatching { MeshProtos.NeighborInfo.parseFrom(data.payload.toByteArray()) }.getOrNull()
|
||||
@@ -1088,7 +1094,7 @@ class MeshService : Service() {
|
||||
// Store the last neighbor info from our connected radio
|
||||
if (info != null && packet.from == myInfo.myNodeNum) {
|
||||
lastNeighborInfo = info
|
||||
Timber.d("Stored last neighbor info from connected radio")
|
||||
Logger.d { "Stored last neighbor info from connected radio" }
|
||||
}
|
||||
|
||||
// Only show response if packet is addressed to us and we sent a request in the last 3 minutes
|
||||
@@ -1126,23 +1132,21 @@ class MeshService : Service() {
|
||||
if (start != null) {
|
||||
val elapsedMs = System.currentTimeMillis() - start
|
||||
val seconds = elapsedMs / 1000.0
|
||||
Timber.i("Neighbor info $requestId complete in $seconds s")
|
||||
Logger.i { "Neighbor info $requestId complete in $seconds s" }
|
||||
"$formatted\n\nDuration: ${"%.1f".format(seconds)} s"
|
||||
} else {
|
||||
Timber.w("No start time found for neighbor info requestId: $requestId")
|
||||
Logger.w { "No start time found for neighbor info requestId: $requestId" }
|
||||
formatted
|
||||
}
|
||||
serviceRepository.setNeighborInfoResponse(response)
|
||||
} else {
|
||||
Timber.d(
|
||||
"Neighbor info response filtered: isToUs=%s, isRecent=%s",
|
||||
isAddressedToUs,
|
||||
isRecentRequest,
|
||||
)
|
||||
Logger.d {
|
||||
"Neighbor info response filtered: isToUs=$isAddressedToUs, isRecent=$isRecentRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> Timber.d("No custom processing needed for ${data.portnumValue} from $fromId")
|
||||
else -> Logger.d { "No custom processing needed for ${data.portnumValue} from $fromId" }
|
||||
}
|
||||
|
||||
// We always tell other apps when new data packets arrive
|
||||
@@ -1162,7 +1166,7 @@ class MeshService : Service() {
|
||||
AdminProtos.AdminMessage.PayloadVariantCase.GET_CONFIG_RESPONSE -> {
|
||||
if (fromNodeNum == myNodeNum) {
|
||||
val response = a.getConfigResponse
|
||||
Timber.d("Admin: received config ${response.payloadVariantCase}")
|
||||
Logger.d { "Admin: received config ${response.payloadVariantCase}" }
|
||||
setLocalConfig(response)
|
||||
}
|
||||
}
|
||||
@@ -1172,7 +1176,7 @@ class MeshService : Service() {
|
||||
val mi = myNodeInfo
|
||||
if (mi != null) {
|
||||
val ch = a.getChannelResponse
|
||||
Timber.d("Admin: Received channel ${ch.index}")
|
||||
Logger.d { "Admin: Received channel ${ch.index}" }
|
||||
|
||||
if (ch.index + 1 < mi.maxChannels) {
|
||||
handleChannel(ch)
|
||||
@@ -1182,15 +1186,15 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
AdminProtos.AdminMessage.PayloadVariantCase.GET_DEVICE_METADATA_RESPONSE -> {
|
||||
Timber.d("Admin: received DeviceMetadata from $fromNodeNum")
|
||||
Logger.d { "Admin: received DeviceMetadata from $fromNodeNum" }
|
||||
serviceScope.handledLaunch {
|
||||
nodeRepository.insertMetadata(MetadataEntity(fromNodeNum, a.getDeviceMetadataResponse))
|
||||
}
|
||||
}
|
||||
|
||||
else -> Timber.w("No special processing needed for ${a.payloadVariantCase}")
|
||||
else -> Logger.w { "No special processing needed for ${a.payloadVariantCase}" }
|
||||
}
|
||||
Timber.d("Admin: Received session_passkey from $fromNodeNum")
|
||||
Logger.d { "Admin: Received session_passkey from $fromNodeNum" }
|
||||
sessionPasskey = a.sessionPasskey
|
||||
}
|
||||
|
||||
@@ -1224,11 +1228,11 @@ class MeshService : Service() {
|
||||
|
||||
if (shouldPreserve) {
|
||||
// Firmware sent us a placeholder - keep all our existing user data
|
||||
Timber.d(
|
||||
Logger.d {
|
||||
"Preserving existing user data for node $fromNum: " +
|
||||
"kept='${it.user.longName}' (hwModel=${it.user.hwModel}), " +
|
||||
"skipped default='${p.longName}' (hwModel=UNSET)",
|
||||
)
|
||||
"skipped default='${p.longName}' (hwModel=UNSET)"
|
||||
}
|
||||
// Ensure denormalized columns are updated from preserved user data
|
||||
it.longName = it.user.longName
|
||||
it.shortName = it.user.shortName
|
||||
@@ -1242,7 +1246,7 @@ class MeshService : Service() {
|
||||
p
|
||||
} else {
|
||||
p.copy {
|
||||
Timber.w("Public key mismatch from $longName ($shortName)")
|
||||
Logger.w { "Public key mismatch from $longName ($shortName)" }
|
||||
publicKey = NodeEntity.ERROR_BYTE_STRING
|
||||
}
|
||||
}
|
||||
@@ -1275,7 +1279,7 @@ class MeshService : Service() {
|
||||
// (only)
|
||||
// we don't record these nop position updates
|
||||
if (myNodeNum == fromNum && p.latitudeI == 0 && p.longitudeI == 0) {
|
||||
Timber.d("Ignoring nop position update for the local node")
|
||||
Logger.d { "Ignoring nop position update for the local node" }
|
||||
} else {
|
||||
updateNodeInfo(fromNum) { it.setPosition(p, (defaultTime / 1000L).toInt()) }
|
||||
}
|
||||
@@ -1407,7 +1411,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleReceivedStoreAndForward(dataPacket: DataPacket, s: StoreAndForwardProtos.StoreAndForward) {
|
||||
Timber.d("StoreAndForward: ${s.variantCase} ${s.rr} from ${dataPacket.from}")
|
||||
Logger.d { "StoreAndForward: ${s.variantCase} ${s.rr} from ${dataPacket.from}" }
|
||||
val transport = currentTransport()
|
||||
val lastRequest =
|
||||
if (s.variantCase == StoreAndForwardProtos.StoreAndForward.VariantCase.HISTORY) {
|
||||
@@ -1481,7 +1485,7 @@ class MeshService : Service() {
|
||||
if (packet.rxTime == 0) setRxTime(currentSecond())
|
||||
}
|
||||
.build()
|
||||
Timber.d("[packet]: ${packet.toOneLineString()}")
|
||||
Logger.d { "[packet]: ${packet.toOneLineString()}" }
|
||||
if (isNodeDbReady) {
|
||||
processReceivedMeshPacket(preparedPacket)
|
||||
return
|
||||
@@ -1524,7 +1528,7 @@ class MeshService : Service() {
|
||||
private fun sendNow(p: DataPacket) {
|
||||
val packet = toMeshPacket(p)
|
||||
p.time = System.currentTimeMillis() // update time to the actual time we started sending
|
||||
// Timber.d("Sending to radio: ${packet.toPIIString()}")
|
||||
// Logger.d { "Sending to radio: ${packet.toPIIString()}" }
|
||||
packetHandler.sendToRadio(packet)
|
||||
}
|
||||
|
||||
@@ -1535,7 +1539,7 @@ class MeshService : Service() {
|
||||
sendNow(p)
|
||||
sentPackets.add(p)
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Error sending queued message:")
|
||||
Logger.e(ex) { "Error sending queued message:" }
|
||||
}
|
||||
}
|
||||
offlineSentPackets.removeAll(sentPackets)
|
||||
@@ -1669,7 +1673,7 @@ class MeshService : Service() {
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun onConnectionChanged(c: ConnectionState) {
|
||||
if (connectionStateHolder.connectionState.value == c && c !is ConnectionState.Connected) return
|
||||
Timber.d("onConnectionChanged: ${connectionStateHolder.connectionState.value} -> $c")
|
||||
Logger.d { "onConnectionChanged: ${connectionStateHolder.connectionState.value} -> $c" }
|
||||
|
||||
// Cancel any existing timeouts
|
||||
sleepTimeout?.cancel()
|
||||
@@ -1697,7 +1701,7 @@ class MeshService : Service() {
|
||||
|
||||
private fun handleDisconnected() {
|
||||
connectionStateHolder.setState(ConnectionState.Disconnected)
|
||||
Timber.d("Starting disconnect")
|
||||
Logger.d { "Starting disconnect" }
|
||||
packetHandler.stopPacketQueue()
|
||||
stopLocationRequests()
|
||||
stopMqttClientProxy()
|
||||
@@ -1730,12 +1734,12 @@ class MeshService : Service() {
|
||||
// wait 30 seconds
|
||||
val timeout = (localConfig.power?.lsSecs ?: 0) + 30
|
||||
|
||||
Timber.d("Waiting for sleeping device, timeout=$timeout secs")
|
||||
Logger.d { "Waiting for sleeping device, timeout=$timeout secs" }
|
||||
delay(timeout * 1000L)
|
||||
Timber.w("Device timeout out, setting disconnected")
|
||||
Logger.w { "Device timeout out, setting disconnected" }
|
||||
onConnectionChanged(ConnectionState.Disconnected)
|
||||
} catch (_: CancellationException) {
|
||||
Timber.d("device sleep timeout cancelled")
|
||||
Logger.d { "device sleep timeout cancelled" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1746,7 +1750,7 @@ class MeshService : Service() {
|
||||
private fun handleConnected() {
|
||||
connectionStateHolder.setState(ConnectionState.Connecting)
|
||||
serviceBroadcasts.broadcastConnection()
|
||||
Timber.d("Starting connect")
|
||||
Logger.d { "Starting connect" }
|
||||
historyLog {
|
||||
val address = meshPrefs.deviceAddress ?: "null"
|
||||
"onReconnect transport=${currentTransport()} node=$address"
|
||||
@@ -1755,9 +1759,9 @@ class MeshService : Service() {
|
||||
connectTimeMsec = System.currentTimeMillis()
|
||||
startConfigOnly()
|
||||
} catch (ex: InvalidProtocolBufferException) {
|
||||
Timber.e(ex, "Invalid protocol buffer sent by device - update device software and try again")
|
||||
Logger.e(ex) { "Invalid protocol buffer sent by device - update device software and try again" }
|
||||
} catch (ex: RadioNotConnectedException) {
|
||||
Timber.e("Lost connection to radio during init - waiting for reconnect ${ex.message}")
|
||||
Logger.e { "Lost connection to radio during init - waiting for reconnect ${ex.message}" }
|
||||
} catch (ex: RemoteException) {
|
||||
onConnectionChanged(ConnectionState.DeviceSleep)
|
||||
throw ex
|
||||
@@ -1852,7 +1856,9 @@ class MeshService : Service() {
|
||||
|
||||
// Explicitly handle default/unwanted cases to satisfy the exhaustive `when`
|
||||
PayloadVariantCase.PAYLOADVARIANT_NOT_SET -> { proto ->
|
||||
Timber.d("Received variant PayloadVariantUnset: Full FromRadio proto: ${proto.toPIIString()}")
|
||||
Logger.d {
|
||||
"Received variant PayloadVariantUnset: Full FromRadio proto: ${proto.toPIIString()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1872,9 +1878,9 @@ class MeshService : Service() {
|
||||
runCatching { MeshProtos.FromRadio.parseFrom(bytes) }
|
||||
.onSuccess { proto ->
|
||||
if (proto.payloadVariantCase == PayloadVariantCase.PAYLOADVARIANT_NOT_SET) {
|
||||
Timber.w(
|
||||
"Received FromRadio with PAYLOADVARIANT_NOT_SET. rawBytes=${bytes.toHexString()} proto=$proto",
|
||||
)
|
||||
Logger.w {
|
||||
"Received FromRadio with PAYLOADVARIANT_NOT_SET. rawBytes=${bytes.toHexString()} proto=$proto"
|
||||
}
|
||||
}
|
||||
proto.route()
|
||||
}
|
||||
@@ -1884,11 +1890,10 @@ class MeshService : Service() {
|
||||
handleLogRecord(logRecord)
|
||||
}
|
||||
.onFailure { _ ->
|
||||
Timber.e(
|
||||
primaryException,
|
||||
Logger.e(primaryException) {
|
||||
"Failed to parse radio packet (len=${bytes.size} contents=${bytes.toHexString()}). " +
|
||||
"Not a valid FromRadio or LogRecord.",
|
||||
)
|
||||
"Not a valid FromRadio or LogRecord."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1908,7 +1913,7 @@ class MeshService : Service() {
|
||||
private var nodeInfoNonce: Int = DEFAULT_NODE_INFO_NONCE
|
||||
|
||||
private fun handleDeviceConfig(config: ConfigProtos.Config) {
|
||||
Timber.d("[deviceConfig] ${config.toPIIString()}")
|
||||
Logger.d { "[deviceConfig] ${config.toPIIString()}" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -1924,7 +1929,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleModuleConfig(config: ModuleConfigProtos.ModuleConfig) {
|
||||
Timber.d("[moduleConfig] ${config.toPIIString()}")
|
||||
Logger.d { "[moduleConfig] ${config.toPIIString()}" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -1940,7 +1945,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleChannel(ch: ChannelProtos.Channel) {
|
||||
Timber.d("[channel] ${ch.toPIIString()}")
|
||||
Logger.d { "[channel] ${ch.toPIIString()}" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -1965,11 +1970,11 @@ class MeshService : Service() {
|
||||
|
||||
if (shouldPreserve) {
|
||||
// Firmware sent us a placeholder - keep all our existing user data
|
||||
Timber.d(
|
||||
Logger.d {
|
||||
"Preserving existing user data for node ${info.num}: " +
|
||||
"kept='${it.user.longName}' (hwModel=${it.user.hwModel}), " +
|
||||
"skipped default='${info.user.longName}' (hwModel=UNSET)",
|
||||
)
|
||||
"skipped default='${info.user.longName}' (hwModel=UNSET)"
|
||||
}
|
||||
// Ensure denormalized columns are updated from preserved user data
|
||||
it.longName = it.user.longName
|
||||
it.shortName = it.user.shortName
|
||||
@@ -2013,7 +2018,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleNodeInfo(info: MeshProtos.NodeInfo) {
|
||||
Timber.d("[nodeInfo] ${info.toPIIString()}")
|
||||
Logger.d { "[nodeInfo] ${info.toPIIString()}" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -2029,7 +2034,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleNodeInfoComplete() {
|
||||
Timber.d("NodeInfo complete for nonce $nodeInfoNonce")
|
||||
Logger.d { "NodeInfo complete for nonce $nodeInfoNonce" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -2040,7 +2045,7 @@ class MeshService : Service() {
|
||||
)
|
||||
insertMeshLog(packetToSave)
|
||||
if (newNodes.isEmpty()) {
|
||||
Timber.e("Did not receive a valid node info")
|
||||
Logger.e { "Did not receive a valid node info" }
|
||||
} else {
|
||||
// Batch update: Update in-memory models first without triggering individual DB writes
|
||||
val entities =
|
||||
@@ -2074,13 +2079,13 @@ class MeshService : Service() {
|
||||
private fun regenMyNodeInfo(metadata: MeshProtos.DeviceMetadata? = MeshProtos.DeviceMetadata.getDefaultInstance()) {
|
||||
val myInfo = rawMyNodeInfo
|
||||
val hasMetadata = metadata != null && metadata != MeshProtos.DeviceMetadata.getDefaultInstance()
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[MYNODE_REGEN] Called - " +
|
||||
"rawMyNodeInfo: ${if (myInfo != null) "present" else "null"}, " +
|
||||
"metadata: ${if (hasMetadata) "present" else "null/default"}, " +
|
||||
"firmwareVersion: ${metadata?.firmwareVersion ?: "null"}, " +
|
||||
"hasWifi: ${metadata?.hasWifi}",
|
||||
)
|
||||
"hasWifi: ${metadata?.hasWifi}"
|
||||
}
|
||||
|
||||
if (myInfo != null) {
|
||||
val mi =
|
||||
@@ -2107,21 +2112,21 @@ class MeshService : Service() {
|
||||
)
|
||||
}
|
||||
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[MYNODE_REGEN] Created MyNodeEntity - " +
|
||||
"nodeNum: ${mi.myNodeNum}, " +
|
||||
"model: ${mi.model}, " +
|
||||
"firmwareVersion: ${mi.firmwareVersion}, " +
|
||||
"hasWifi: ${mi.hasWifi}",
|
||||
)
|
||||
"hasWifi: ${mi.hasWifi}"
|
||||
}
|
||||
|
||||
if (metadata != null && metadata != MeshProtos.DeviceMetadata.getDefaultInstance()) {
|
||||
serviceScope.handledLaunch { nodeRepository.insertMetadata(MetadataEntity(mi.myNodeNum, metadata)) }
|
||||
}
|
||||
newMyNodeInfo = mi
|
||||
Timber.i("[MYNODE_REGEN] Set newMyNodeInfo (will be committed on configComplete)")
|
||||
Logger.i { "[MYNODE_REGEN] Set newMyNodeInfo (will be committed on configComplete)" }
|
||||
} else {
|
||||
Timber.w("[MYNODE_REGEN] rawMyNodeInfo is null, cannot regenerate")
|
||||
Logger.w { "[MYNODE_REGEN] rawMyNodeInfo is null, cannot regenerate" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2136,12 +2141,12 @@ class MeshService : Service() {
|
||||
|
||||
/** Update MyNodeInfo (called from either new API version or the old one) */
|
||||
private fun handleMyInfo(myInfo: MeshProtos.MyNodeInfo) {
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[MYINFO_RECEIVED] MyNodeInfo received - " +
|
||||
"nodeNum: ${myInfo.myNodeNum}, " +
|
||||
"minAppVersion: ${myInfo.minAppVersion}, " +
|
||||
"PII data: ${myInfo.toPIIString()}",
|
||||
)
|
||||
"PII data: ${myInfo.toPIIString()}"
|
||||
}
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -2153,7 +2158,7 @@ class MeshService : Service() {
|
||||
insertMeshLog(packetToSave)
|
||||
|
||||
rawMyNodeInfo = myInfo
|
||||
Timber.i("[MYINFO_RECEIVED] Set rawMyNodeInfo, calling regenMyNodeInfo()")
|
||||
Logger.i { "[MYINFO_RECEIVED] Set rawMyNodeInfo, calling regenMyNodeInfo()" }
|
||||
regenMyNodeInfo()
|
||||
|
||||
// We'll need to get a new set of channels and settings now
|
||||
@@ -2166,14 +2171,14 @@ class MeshService : Service() {
|
||||
|
||||
/** Update our DeviceMetadata */
|
||||
private fun handleMetadata(metadata: MeshProtos.DeviceMetadata) {
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[METADATA_RECEIVED] DeviceMetadata received - " +
|
||||
"firmwareVersion: ${metadata.firmwareVersion}, " +
|
||||
"hwModel: ${metadata.hwModel}, " +
|
||||
"hasWifi: ${metadata.hasWifi}, " +
|
||||
"hasBluetooth: ${metadata.hasBluetooth}, " +
|
||||
"PII data: ${metadata.toPIIString()}",
|
||||
)
|
||||
"PII data: ${metadata.toPIIString()}"
|
||||
}
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -2184,16 +2189,16 @@ class MeshService : Service() {
|
||||
)
|
||||
insertMeshLog(packetToSave)
|
||||
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[METADATA_RECEIVED] Calling regenMyNodeInfo with metadata - " +
|
||||
"This will update newMyNodeInfo with firmwareVersion: ${metadata.firmwareVersion}",
|
||||
)
|
||||
"This will update newMyNodeInfo with firmwareVersion: ${metadata.firmwareVersion}"
|
||||
}
|
||||
regenMyNodeInfo(metadata)
|
||||
}
|
||||
|
||||
/** Publish MqttClientProxyMessage (fromRadio) */
|
||||
private fun handleMqttProxyMessage(message: MeshProtos.MqttClientProxyMessage) {
|
||||
Timber.d("[mqttClientProxyMessage] ${message.toPIIString()}")
|
||||
Logger.d { "[mqttClientProxyMessage] ${message.toPIIString()}" }
|
||||
with(message) {
|
||||
when (payloadVariantCase) {
|
||||
MeshProtos.MqttClientProxyMessage.PayloadVariantCase.TEXT -> {
|
||||
@@ -2210,7 +2215,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleClientNotification(notification: MeshProtos.ClientNotification) {
|
||||
Timber.d("[clientNotification] ${notification.toPIIString()}")
|
||||
Logger.d { "[clientNotification] ${notification.toPIIString()}" }
|
||||
serviceRepository.setClientNotification(notification)
|
||||
serviceNotifications.showClientNotification(notification)
|
||||
// if the future for the originating request is still in the queue, complete as unsuccessful
|
||||
@@ -2228,7 +2233,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleFileInfo(fileInfo: MeshProtos.FileInfo) {
|
||||
Timber.d("[fileInfo] ${fileInfo.toPIIString()}")
|
||||
Logger.d { "[fileInfo] ${fileInfo.toPIIString()}" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -2241,7 +2246,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleLogRecord(logRecord: MeshProtos.LogRecord) {
|
||||
Timber.d("[logRecord] ${logRecord.toPIIString()}")
|
||||
Logger.d { "[logRecord] ${logRecord.toPIIString()}" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -2254,7 +2259,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleRebooted(rebooted: Boolean) {
|
||||
Timber.d("[rebooted] $rebooted")
|
||||
Logger.d { "[rebooted] $rebooted" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -2267,7 +2272,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleXmodemPacket(xmodemPacket: XmodemProtos.XModem) {
|
||||
Timber.d("[xmodemPacket] ${xmodemPacket.toPIIString()}")
|
||||
Logger.d { "[xmodemPacket] ${xmodemPacket.toPIIString()}" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -2280,7 +2285,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleDeviceUiConfig(deviceuiConfig: DeviceUIProtos.DeviceUIConfig) {
|
||||
Timber.d("[deviceuiConfig] ${deviceuiConfig.toPIIString()}")
|
||||
Logger.d { "[deviceuiConfig] ${deviceuiConfig.toPIIString()}" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -2308,7 +2313,7 @@ class MeshService : Service() {
|
||||
|
||||
private fun stopMqttClientProxy() {
|
||||
if (mqttMessageFlow?.isActive == true) {
|
||||
Timber.i("Stopping MqttClientProxy")
|
||||
Logger.i { "Stopping MqttClientProxy" }
|
||||
mqttMessageFlow?.cancel()
|
||||
mqttMessageFlow = null
|
||||
}
|
||||
@@ -2329,19 +2334,19 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
private fun handleConfigComplete(configCompleteId: Int) {
|
||||
Timber.d("[configCompleteId]: ${configCompleteId.toPIIString()}")
|
||||
Logger.d { "[configCompleteId]: ${configCompleteId.toPIIString()}" }
|
||||
when (configCompleteId) {
|
||||
configOnlyNonce -> handleConfigOnlyComplete()
|
||||
nodeInfoNonce -> handleNodeInfoComplete()
|
||||
else ->
|
||||
Timber.w(
|
||||
"Config complete id mismatch: received=$configCompleteId expected one of [$configOnlyNonce,$nodeInfoNonce]",
|
||||
)
|
||||
Logger.w {
|
||||
"Config complete id mismatch: received=$configCompleteId expected one of [$configOnlyNonce,$nodeInfoNonce]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleConfigOnlyComplete() {
|
||||
Timber.i("[CONFIG_COMPLETE] Config-only complete for nonce $configOnlyNonce")
|
||||
Logger.i { "[CONFIG_COMPLETE] Config-only complete for nonce $configOnlyNonce" }
|
||||
val packetToSave =
|
||||
MeshLog(
|
||||
uuid = UUID.randomUUID().toString(),
|
||||
@@ -2353,16 +2358,16 @@ class MeshService : Service() {
|
||||
insertMeshLog(packetToSave)
|
||||
|
||||
if (newMyNodeInfo == null) {
|
||||
Timber.e("[CONFIG_COMPLETE] Did not receive a valid config - newMyNodeInfo is null")
|
||||
Logger.e { "[CONFIG_COMPLETE] Did not receive a valid config - newMyNodeInfo is null" }
|
||||
} else {
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[CONFIG_COMPLETE] Committing newMyNodeInfo to myNodeInfo - " +
|
||||
"firmwareVersion: ${newMyNodeInfo?.firmwareVersion}, " +
|
||||
"hasWifi: ${newMyNodeInfo?.hasWifi}, " +
|
||||
"model: ${newMyNodeInfo?.model}",
|
||||
)
|
||||
"model: ${newMyNodeInfo?.model}"
|
||||
}
|
||||
myNodeInfo = newMyNodeInfo
|
||||
Timber.i("[CONFIG_COMPLETE] myNodeInfo committed successfully")
|
||||
Logger.i { "[CONFIG_COMPLETE] myNodeInfo committed successfully" }
|
||||
}
|
||||
// Keep BLE awake and allow the firmware to settle before the node-info stage.
|
||||
serviceScope.handledLaunch {
|
||||
@@ -2379,21 +2384,21 @@ class MeshService : Service() {
|
||||
packetHandler.sendToRadio(
|
||||
ToRadio.newBuilder().apply { heartbeat = MeshProtos.Heartbeat.getDefaultInstance() },
|
||||
)
|
||||
Timber.d("Heartbeat sent between nonce stages")
|
||||
Logger.d { "Heartbeat sent between nonce stages" }
|
||||
} catch (ex: Exception) {
|
||||
Timber.w(ex, "Failed to send heartbeat; proceeding with node-info stage")
|
||||
Logger.w(ex) { "Failed to send heartbeat; proceeding with node-info stage" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun startConfigOnly() {
|
||||
newMyNodeInfo = null
|
||||
Timber.d("Starting config-only nonce=$configOnlyNonce")
|
||||
Logger.d { "Starting config-only nonce=$configOnlyNonce" }
|
||||
packetHandler.sendToRadio(ToRadio.newBuilder().apply { this.wantConfigId = configOnlyNonce })
|
||||
}
|
||||
|
||||
private fun startNodeInfoOnly() {
|
||||
newNodes.clear()
|
||||
Timber.d("Starting node-info nonce=$nodeInfoNonce")
|
||||
Logger.d { "Starting node-info nonce=$nodeInfoNonce" }
|
||||
packetHandler.sendToRadio(ToRadio.newBuilder().apply { this.wantConfigId = nodeInfoNonce })
|
||||
}
|
||||
|
||||
@@ -2403,7 +2408,7 @@ class MeshService : Service() {
|
||||
val mi = myNodeInfo
|
||||
if (mi != null) {
|
||||
val idNum = destNum ?: mi.myNodeNum // when null we just send to the local node
|
||||
Timber.d("Sending our position/time to=$idNum ${Position(position)}")
|
||||
Logger.d { "Sending our position/time to=$idNum ${Position(position)}" }
|
||||
|
||||
// Also update our own map for our nodeNum, by handling the packet just like packets from other users
|
||||
if (!localConfig.position.fixedPosition) {
|
||||
@@ -2422,7 +2427,7 @@ class MeshService : Service() {
|
||||
)
|
||||
}
|
||||
} catch (_: BLEException) {
|
||||
Timber.w("Ignoring disconnected radio during gps location update")
|
||||
Logger.w { "Ignoring disconnected radio during gps location update" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2433,11 +2438,11 @@ class MeshService : Service() {
|
||||
|
||||
@Suppress("ComplexCondition")
|
||||
if (user == old) {
|
||||
Timber.d("Ignoring nop owner change")
|
||||
Logger.d { "Ignoring nop owner change" }
|
||||
} else {
|
||||
Timber.d(
|
||||
"setOwner Id: $id longName: ${longName.anonymize} shortName: $shortName isLicensed: $isLicensed isUnmessagable: $isUnmessagable",
|
||||
)
|
||||
Logger.d {
|
||||
"setOwner Id: $id longName: ${longName.anonymize} shortName: $shortName isLicensed: $isLicensed isUnmessagable: $isUnmessagable"
|
||||
}
|
||||
|
||||
// Also update our own map for our nodeNum, by handling the packet just like packets from other users
|
||||
handleReceivedUser(dest.num, user)
|
||||
@@ -2511,10 +2516,10 @@ class MeshService : Service() {
|
||||
packetHandler.sendToRadio(
|
||||
newMeshPacketTo(myNodeNum).buildAdminPacket {
|
||||
if (node.isFavorite) {
|
||||
Timber.d("removing node ${node.num} from favorite list")
|
||||
Logger.d { "removing node ${node.num} from favorite list" }
|
||||
removeFavoriteNode = node.num
|
||||
} else {
|
||||
Timber.d("adding node ${node.num} to favorite list")
|
||||
Logger.d { "adding node ${node.num} to favorite list" }
|
||||
setFavoriteNode = node.num
|
||||
}
|
||||
},
|
||||
@@ -2526,10 +2531,10 @@ class MeshService : Service() {
|
||||
packetHandler.sendToRadio(
|
||||
newMeshPacketTo(myNodeNum).buildAdminPacket {
|
||||
if (node.isIgnored) {
|
||||
Timber.d("removing node ${node.num} from ignore list")
|
||||
Logger.d { "removing node ${node.num} from ignore list" }
|
||||
removeIgnoredNode = node.num
|
||||
} else {
|
||||
Timber.d("adding node ${node.num} to ignore list")
|
||||
Logger.d { "adding node ${node.num} to ignore list" }
|
||||
setIgnoredNode = node.num
|
||||
}
|
||||
},
|
||||
@@ -2555,11 +2560,11 @@ class MeshService : Service() {
|
||||
|
||||
private fun updateLastAddress(deviceAddr: String?) {
|
||||
val currentAddr = meshPrefs.deviceAddress
|
||||
Timber.d("setDeviceAddress: received request to change to: ${deviceAddr.anonymize}")
|
||||
Logger.d { "setDeviceAddress: received request to change to: ${deviceAddr.anonymize}" }
|
||||
if (deviceAddr != currentAddr) {
|
||||
Timber.d(
|
||||
"SetDeviceAddress: Device address changed from ${currentAddr.anonymize} to ${deviceAddr.anonymize}",
|
||||
)
|
||||
Logger.d {
|
||||
"SetDeviceAddress: Device address changed from ${currentAddr.anonymize} to ${deviceAddr.anonymize}"
|
||||
}
|
||||
val currentLabel = currentAddr ?: "null"
|
||||
val nextLabel = deviceAddr ?: "null"
|
||||
val nextTransport = currentTransport(deviceAddr)
|
||||
@@ -2584,7 +2589,7 @@ class MeshService : Service() {
|
||||
loadCachedNodeDB()
|
||||
}
|
||||
} else {
|
||||
Timber.d("SetDeviceAddress: Device address is unchanged, ignoring.")
|
||||
Logger.d { "SetDeviceAddress: Device address is unchanged, ignoring." }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2596,7 +2601,7 @@ class MeshService : Service() {
|
||||
object : IMeshService.Stub() {
|
||||
|
||||
override fun setDeviceAddress(deviceAddr: String?) = toRemoteExceptions {
|
||||
Timber.d("Passing through device change to radio service: ${deviceAddr.anonymize}")
|
||||
Logger.d { "Passing through device change to radio service: ${deviceAddr.anonymize}" }
|
||||
updateLastAddress(deviceAddr)
|
||||
radioInterfaceService.setDeviceAddress(deviceAddr)
|
||||
}
|
||||
@@ -2642,9 +2647,9 @@ class MeshService : Service() {
|
||||
toRemoteExceptions {
|
||||
if (p.id == 0) p.id = generatePacketId()
|
||||
val bytes = p.bytes!!
|
||||
Timber.i(
|
||||
"sendData dest=${p.to}, id=${p.id} <- ${bytes.size} bytes (connectionState=${connectionStateHolder.connectionState.value})",
|
||||
)
|
||||
Logger.i {
|
||||
"sendData dest=${p.to}, id=${p.id} <- ${bytes.size} bytes (connectionState=${connectionStateHolder.connectionState.value})"
|
||||
}
|
||||
if (p.dataType == 0) throw Exception("Port numbers must be non-zero!")
|
||||
if (bytes.size >= MeshProtos.Constants.DATA_PAYLOAD_LEN.number) {
|
||||
p.status = MessageStatus.ERROR
|
||||
@@ -2656,7 +2661,7 @@ class MeshService : Service() {
|
||||
try {
|
||||
sendNow(p)
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Error sending message, so enqueueing")
|
||||
Logger.e(ex) { "Error sending message, so enqueueing" }
|
||||
enqueueForSending(p)
|
||||
}
|
||||
} else {
|
||||
@@ -2677,7 +2682,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
override fun setRemoteConfig(id: Int, num: Int, payload: ByteArray) = toRemoteExceptions {
|
||||
Timber.d("Setting new radio config!")
|
||||
Logger.d { "Setting new radio config!" }
|
||||
val config = ConfigProtos.Config.parseFrom(payload)
|
||||
packetHandler.sendToRadio(newMeshPacketTo(num).buildAdminPacket(id = id) { setConfig = config })
|
||||
if (num == myNodeNum) setLocalConfig(config)
|
||||
@@ -2696,7 +2701,7 @@ class MeshService : Service() {
|
||||
}
|
||||
|
||||
override fun setModuleConfig(id: Int, num: Int, payload: ByteArray) = toRemoteExceptions {
|
||||
Timber.d("Setting new module config!")
|
||||
Logger.d { "Setting new module config!" }
|
||||
val config = ModuleConfigProtos.ModuleConfig.parseFrom(payload)
|
||||
packetHandler.sendToRadio(newMeshPacketTo(num).buildAdminPacket(id = id) { setModuleConfig = config })
|
||||
if (num == myNodeNum) setLocalModuleConfig(config)
|
||||
@@ -2765,13 +2770,13 @@ class MeshService : Service() {
|
||||
|
||||
override fun getNodes(): MutableList<NodeInfo> = toRemoteExceptions {
|
||||
val r = nodeDBbyNodeNum.values.map { it.toNodeInfo() }.toMutableList()
|
||||
Timber.i("in getOnline, count=${r.size}")
|
||||
Logger.i { "in getOnline, count=${r.size}" }
|
||||
r
|
||||
}
|
||||
|
||||
override fun connectionState(): String = toRemoteExceptions {
|
||||
val r = connectionStateHolder.connectionState.value
|
||||
Timber.i("in connectionState=$r")
|
||||
Logger.i { "in connectionState=$r" }
|
||||
r.toString()
|
||||
}
|
||||
|
||||
@@ -2804,7 +2809,7 @@ class MeshService : Service() {
|
||||
lastNeighborInfo
|
||||
?: run {
|
||||
// If we don't have it, send dummy/interceptable data
|
||||
Timber.d("No stored neighbor info from connected radio, sending dummy data")
|
||||
Logger.d { "No stored neighbor info from connected radio, sending dummy data" }
|
||||
MeshProtos.NeighborInfo.newBuilder()
|
||||
.setNodeId(myNodeNum)
|
||||
.setLastSentById(myNodeNum)
|
||||
@@ -2844,7 +2849,7 @@ class MeshService : Service() {
|
||||
else -> nodeDBbyNodeNum[myNodeNum]?.position?.let { Position(it) }?.takeIf { it.isValid() }
|
||||
}
|
||||
if (currentPosition == null) {
|
||||
Timber.d("Position request skipped - no valid position available")
|
||||
Logger.d { "Position request skipped - no valid position available" }
|
||||
return@toRemoteExceptions
|
||||
}
|
||||
val meshPosition = position {
|
||||
|
||||
@@ -20,13 +20,13 @@ package com.geeksville.mesh.service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Parcelable
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.MessageStatus
|
||||
import org.meshtastic.core.model.NodeInfo
|
||||
import org.meshtastic.core.model.util.toPIIString
|
||||
import org.meshtastic.core.service.ServiceRepository
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -51,7 +51,7 @@ constructor(
|
||||
}
|
||||
|
||||
fun broadcastNodeChange(info: NodeInfo) {
|
||||
Timber.d("Broadcasting node change ${info.user?.toPIIString()}")
|
||||
Logger.d { "Broadcasting node change ${info.user?.toPIIString()}" }
|
||||
val intent = Intent(MeshService.ACTION_NODE_CHANGE).putExtra(EXTRA_NODEINFO, info)
|
||||
explicitBroadcast(intent)
|
||||
}
|
||||
@@ -60,10 +60,10 @@ constructor(
|
||||
|
||||
fun broadcastMessageStatus(id: Int, status: MessageStatus?) {
|
||||
if (id == 0) {
|
||||
Timber.d("Ignoring anonymous packet status")
|
||||
Logger.d { "Ignoring anonymous packet status" }
|
||||
} else {
|
||||
// Do not log, contains PII possibly
|
||||
// MeshService.Timber.d("Broadcasting message status $p")
|
||||
// MeshService.Logger.d { "Broadcasting message status $p" }
|
||||
val intent =
|
||||
Intent(MeshService.ACTION_MESSAGE_STATUS).apply {
|
||||
putExtra(EXTRA_PACKET_ID, id)
|
||||
|
||||
@@ -20,8 +20,8 @@ package com.geeksville.mesh.service
|
||||
import android.app.ForegroundServiceStartNotAllowedException
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.BuildConfig
|
||||
import timber.log.Timber
|
||||
|
||||
// / Helper function to start running our service
|
||||
fun MeshService.Companion.startService(context: Context) {
|
||||
@@ -33,14 +33,14 @@ 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.
|
||||
Timber.i("Trying to start service debug=${BuildConfig.DEBUG}")
|
||||
Logger.i { "Trying to start service debug=${BuildConfig.DEBUG}" }
|
||||
|
||||
val intent = createIntent(context)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
try {
|
||||
context.startForegroundService(intent)
|
||||
} catch (ex: ForegroundServiceStartNotAllowedException) {
|
||||
Timber.e("Unable to start service: ${ex.message}")
|
||||
Logger.e { "Unable to start service: ${ex.message}" }
|
||||
}
|
||||
} else {
|
||||
context.startForegroundService(intent)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package com.geeksville.mesh.service
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.concurrent.handledLaunch
|
||||
import com.geeksville.mesh.repository.radio.RadioInterfaceService
|
||||
import dagger.Lazy
|
||||
@@ -38,7 +39,6 @@ import org.meshtastic.proto.MeshProtos
|
||||
import org.meshtastic.proto.MeshProtos.MeshPacket
|
||||
import org.meshtastic.proto.MeshProtos.ToRadio
|
||||
import org.meshtastic.proto.fromRadio
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -73,7 +73,7 @@ constructor(
|
||||
*/
|
||||
fun sendToRadio(p: ToRadio.Builder) {
|
||||
val built = p.build()
|
||||
Timber.d("Sending to radio ${built.toPIIString()}")
|
||||
Logger.d { "Sending to radio ${built.toPIIString()}" }
|
||||
val b = built.toByteArray()
|
||||
|
||||
radioInterfaceService.sendToRadio(b)
|
||||
@@ -105,7 +105,7 @@ constructor(
|
||||
|
||||
fun stopPacketQueue() {
|
||||
if (queueJob?.isActive == true) {
|
||||
Timber.i("Stopping packet queueJob")
|
||||
Logger.i { "Stopping packet queueJob" }
|
||||
queueJob?.cancel()
|
||||
queueJob = null
|
||||
queuedPackets.clear()
|
||||
@@ -115,7 +115,7 @@ constructor(
|
||||
}
|
||||
|
||||
fun handleQueueStatus(queueStatus: MeshProtos.QueueStatus) {
|
||||
Timber.d("[queueStatus] ${queueStatus.toOneLineString()}")
|
||||
Logger.d { "[queueStatus] ${queueStatus.toOneLineString()}" }
|
||||
val (success, isFull, requestId) = with(queueStatus) { Triple(res == 0, free == 0, meshPacketId) }
|
||||
if (success && isFull) return // Queue is full, wait for free != 0
|
||||
if (requestId != 0) {
|
||||
@@ -134,20 +134,20 @@ constructor(
|
||||
if (queueJob?.isActive == true) return
|
||||
queueJob =
|
||||
scope.handledLaunch {
|
||||
Timber.d("packet queueJob started")
|
||||
Logger.d { "packet queueJob started" }
|
||||
while (connectionStateHolder.connectionState.value == ConnectionState.Connected) {
|
||||
// take the first packet from the queue head
|
||||
val packet = queuedPackets.poll() ?: break
|
||||
try {
|
||||
// send packet to the radio and wait for response
|
||||
val response = sendPacket(packet)
|
||||
Timber.d("queueJob packet id=${packet.id.toUInt()} waiting")
|
||||
Logger.d { "queueJob packet id=${packet.id.toUInt()} waiting" }
|
||||
val success = response.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
Timber.d("queueJob packet id=${packet.id.toUInt()} success $success")
|
||||
Logger.d { "queueJob packet id=${packet.id.toUInt()} success $success" }
|
||||
} catch (e: TimeoutException) {
|
||||
Timber.d("queueJob packet id=${packet.id.toUInt()} timeout")
|
||||
Logger.d { "queueJob packet id=${packet.id.toUInt()} timeout" }
|
||||
} catch (e: Exception) {
|
||||
Timber.d("queueJob packet id=${packet.id.toUInt()} failed")
|
||||
Logger.d { "queueJob packet id=${packet.id.toUInt()} failed" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +186,7 @@ constructor(
|
||||
}
|
||||
sendToRadio(ToRadio.newBuilder().apply { this.packet = packet })
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "sendToRadio error: ${ex.message}")
|
||||
Logger.e(ex) { "sendToRadio error: ${ex.message}" }
|
||||
future.complete(false)
|
||||
}
|
||||
return future
|
||||
|
||||
@@ -77,7 +77,6 @@ import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -88,6 +87,7 @@ import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.BuildConfig
|
||||
import com.geeksville.mesh.model.BTScanModel
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
@@ -157,7 +157,6 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusBlue
|
||||
import org.meshtastic.core.ui.theme.StatusColors.StatusGreen
|
||||
import org.meshtastic.feature.node.metrics.annotateTraceroute
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import timber.log.Timber
|
||||
|
||||
enum class TopLevelDestination(val label: StringResource, val icon: ImageVector, val route: Route) {
|
||||
Conversations(Res.string.conversations, MeshtasticIcons.Conversations, ContactsRoutes.ContactsGraph),
|
||||
@@ -610,7 +609,7 @@ private fun VersionChecks(viewModel: UIViewModel) {
|
||||
LaunchedEffect(connectionState, firmwareEdition) {
|
||||
if (connectionState == ConnectionState.Connected) {
|
||||
firmwareEdition?.let { edition ->
|
||||
Timber.d("FirmwareEdition: ${edition.name}")
|
||||
Logger.d { "FirmwareEdition: ${edition.name}" }
|
||||
when (edition) {
|
||||
MeshProtos.FirmwareEdition.VANILLA -> {
|
||||
// Handle any specific logic for VANILLA firmware edition if needed
|
||||
@@ -627,21 +626,21 @@ private fun VersionChecks(viewModel: UIViewModel) {
|
||||
// Check if the device is running an old app version or firmware version
|
||||
LaunchedEffect(connectionState, myNodeInfo) {
|
||||
if (connectionState == ConnectionState.Connected) {
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[FW_CHECK] Connection state: $connectionState, " +
|
||||
"myNodeInfo: ${if (myNodeInfo != null) "present" else "null"}, " +
|
||||
"firmwareVersion: ${myFirmwareVersion ?: "null"}",
|
||||
)
|
||||
"firmwareVersion: ${myFirmwareVersion ?: "null"}"
|
||||
}
|
||||
|
||||
myNodeInfo?.let { info ->
|
||||
val isOld = info.minAppVersion > BuildConfig.VERSION_CODE && BuildConfig.DEBUG.not()
|
||||
Timber.d(
|
||||
Logger.d {
|
||||
"[FW_CHECK] App version check - minAppVersion: ${info.minAppVersion}, " +
|
||||
"currentVersion: ${BuildConfig.VERSION_CODE}, isOld: $isOld",
|
||||
)
|
||||
"currentVersion: ${BuildConfig.VERSION_CODE}, isOld: $isOld"
|
||||
}
|
||||
|
||||
if (isOld) {
|
||||
Timber.w("[FW_CHECK] App too old - showing update prompt")
|
||||
Logger.w { "[FW_CHECK] App too old - showing update prompt" }
|
||||
viewModel.showAlert(
|
||||
getString(Res.string.app_too_old),
|
||||
getString(Res.string.must_update),
|
||||
@@ -654,18 +653,18 @@ private fun VersionChecks(viewModel: UIViewModel) {
|
||||
} else {
|
||||
myFirmwareVersion?.let { fwVersion ->
|
||||
val curVer = DeviceVersion(fwVersion)
|
||||
Timber.i(
|
||||
Logger.i {
|
||||
"[FW_CHECK] Firmware version comparison - " +
|
||||
"device: $curVer (raw: $fwVersion), " +
|
||||
"absoluteMin: ${MeshService.absoluteMinDeviceVersion}, " +
|
||||
"min: ${MeshService.minDeviceVersion}",
|
||||
)
|
||||
"min: ${MeshService.minDeviceVersion}"
|
||||
}
|
||||
|
||||
if (curVer < MeshService.absoluteMinDeviceVersion) {
|
||||
Timber.w(
|
||||
Logger.w {
|
||||
"[FW_CHECK] Firmware too old - " +
|
||||
"device: $curVer < absoluteMin: ${MeshService.absoluteMinDeviceVersion}",
|
||||
)
|
||||
"device: $curVer < absoluteMin: ${MeshService.absoluteMinDeviceVersion}"
|
||||
}
|
||||
val title = getString(Res.string.firmware_too_old)
|
||||
val message = getString(Res.string.firmware_old)
|
||||
viewModel.showAlert(
|
||||
@@ -678,21 +677,21 @@ private fun VersionChecks(viewModel: UIViewModel) {
|
||||
},
|
||||
)
|
||||
} else if (curVer < MeshService.minDeviceVersion) {
|
||||
Timber.w(
|
||||
Logger.w {
|
||||
"[FW_CHECK] Firmware should update - " +
|
||||
"device: $curVer < min: ${MeshService.minDeviceVersion}",
|
||||
)
|
||||
"device: $curVer < min: ${MeshService.minDeviceVersion}"
|
||||
}
|
||||
val title = getString(Res.string.should_update_firmware)
|
||||
val message = getString(Res.string.should_update, latestStableFirmwareRelease.asString)
|
||||
viewModel.showAlert(title = title, message = message, dismissable = false, onConfirm = {})
|
||||
} else {
|
||||
Timber.i("[FW_CHECK] Firmware version OK - device: $curVer meets requirements")
|
||||
Logger.i { "[FW_CHECK] Firmware version OK - device: $curVer meets requirements" }
|
||||
}
|
||||
} ?: run { Timber.w("[FW_CHECK] Firmware version is null despite myNodeInfo being present") }
|
||||
} ?: run { Logger.w { "[FW_CHECK] Firmware version is null despite myNodeInfo being present" } }
|
||||
}
|
||||
} ?: run { Timber.d("[FW_CHECK] myNodeInfo is null, skipping firmware check") }
|
||||
} ?: run { Logger.d { "[FW_CHECK] myNodeInfo is null, skipping firmware check" } }
|
||||
} else {
|
||||
Timber.d("[FW_CHECK] Not connected (state: $connectionState), skipping firmware check")
|
||||
Logger.d { "[FW_CHECK] Not connected (state: $connectionState), skipping firmware check" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.geeksville.mesh.model.DeviceListEntry
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withTimeout
|
||||
@@ -58,7 +59,6 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusRed
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import org.meshtastic.proto.PaxcountProtos
|
||||
import org.meshtastic.proto.TelemetryProtos
|
||||
import timber.log.Timber
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
private const val RSSI_DELAY = 10
|
||||
@@ -81,13 +81,13 @@ fun CurrentlyConnectedInfo(
|
||||
rssi = withTimeout(RSSI_TIMEOUT.seconds) { bleDevice.peripheral.readRssi() }
|
||||
delay(RSSI_DELAY.seconds)
|
||||
} catch (e: PeripheralNotConnectedException) {
|
||||
Timber.e(e, "Failed to read RSSI ${e.message}")
|
||||
Logger.e(e) { "Failed to read RSSI ${e.message}" }
|
||||
break
|
||||
} catch (e: OperationFailedException) {
|
||||
Timber.e(e, "Failed to read RSSI ${e.message}")
|
||||
Logger.e(e) { "Failed to read RSSI ${e.message}" }
|
||||
break
|
||||
} catch (e: SecurityException) {
|
||||
Timber.e(e, "Failed to read RSSI ${e.message}")
|
||||
Logger.e(e) { "Failed to read RSSI ${e.message}" }
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
@@ -133,7 +134,6 @@ import org.meshtastic.proto.ChannelProtos
|
||||
import org.meshtastic.proto.ConfigProtos
|
||||
import org.meshtastic.proto.channelSet
|
||||
import org.meshtastic.proto.copy
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Composable screen for managing and sharing Meshtastic channels. Allows users to view, edit, and share channel
|
||||
@@ -209,7 +209,7 @@ fun ChannelScreen(
|
||||
}
|
||||
|
||||
fun zxingScan() {
|
||||
Timber.d("Starting zxing QR code scanner")
|
||||
Logger.d { "Starting zxing QR code scanner" }
|
||||
val zxingScan = ScanOptions()
|
||||
zxingScan.setCameraId(0)
|
||||
zxingScan.setPrompt("")
|
||||
@@ -236,7 +236,7 @@ fun ChannelScreen(
|
||||
viewModel.setChannels(newChannelSet)
|
||||
// Since we are writing to DeviceConfig, that will trigger the rest of the GUI update (QR code etc)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "ignoring channel problem")
|
||||
Logger.e(ex) { "ignoring channel problem" }
|
||||
|
||||
channelSet = channels // Throw away user edits
|
||||
|
||||
@@ -264,7 +264,7 @@ fun ChannelScreen(
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
Timber.d("Switching back to default channel")
|
||||
Logger.d { "Switching back to default channel" }
|
||||
installSettings(
|
||||
Channel.default.settings,
|
||||
Channel.default.loraConfig.copy {
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.net.Uri
|
||||
import android.os.RemoteException
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -39,7 +40,6 @@ import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
|
||||
import org.meshtastic.proto.channelSet
|
||||
import org.meshtastic.proto.config
|
||||
import org.meshtastic.proto.copy
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@@ -80,7 +80,7 @@ constructor(
|
||||
|
||||
fun requestChannelUrl(url: Uri, onError: () -> Unit) = runCatching { _requestChannelSet.value = url.toChannelSet() }
|
||||
.onFailure { ex ->
|
||||
Timber.e(ex, "Channel url error")
|
||||
Logger.e(ex) { "Channel url error" }
|
||||
onError()
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ constructor(
|
||||
try {
|
||||
serviceRepository.meshService?.setChannel(channel.toByteArray())
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "Set channel error")
|
||||
Logger.e(ex) { "Set channel error" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ constructor(
|
||||
try {
|
||||
serviceRepository.meshService?.setConfig(config.toByteArray())
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "Set config error")
|
||||
Logger.e(ex) { "Set config error" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.geeksville.mesh.util
|
||||
|
||||
import android.os.RemoteException
|
||||
import android.util.Log
|
||||
import timber.log.Timber
|
||||
import co.touchlab.kermit.Logger
|
||||
|
||||
object Exceptions {
|
||||
// / Set in Application.onCreate
|
||||
@@ -31,10 +31,9 @@ object Exceptions {
|
||||
* After reporting return
|
||||
*/
|
||||
fun report(exception: Throwable, tag: String? = null, message: String? = null) {
|
||||
Timber.e(
|
||||
exception,
|
||||
"Exceptions.report: $tag $message",
|
||||
) // print the message to the log _before_ telling the crash reporter
|
||||
Logger.e(exception) {
|
||||
"Exceptions.report: $tag $message"
|
||||
} // print the message to the log _before_ telling the crash reporter
|
||||
reporter?.let { r -> r(exception, tag, message) }
|
||||
}
|
||||
}
|
||||
@@ -58,7 +57,7 @@ fun ignoreException(silent: Boolean = false, inner: () -> Unit) {
|
||||
inner()
|
||||
} catch (ex: Throwable) {
|
||||
// DO NOT THROW users expect we have fully handled/discarded the exception
|
||||
if (!silent) Timber.e("ignoring exception", ex)
|
||||
if (!silent) Logger.e(ex) { "ignoring exception" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ dependencies {
|
||||
implementation(libs.androidx.compose.runtime)
|
||||
implementation(libs.androidx.lifecycle.process)
|
||||
implementation(libs.androidx.navigation.runtime)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
|
||||
googleApi(libs.dd.sdk.android.compose)
|
||||
googleApi(libs.dd.sdk.android.logs)
|
||||
|
||||
@@ -19,9 +19,10 @@ package org.meshtastic.core.analytics.platform
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.NavHostController
|
||||
import co.touchlab.kermit.Logger
|
||||
import co.touchlab.kermit.Severity
|
||||
import org.meshtastic.core.analytics.BuildConfig
|
||||
import org.meshtastic.core.analytics.DataPair
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@@ -34,16 +35,17 @@ class FdroidPlatformAnalytics @Inject constructor() : PlatformAnalytics {
|
||||
// In debug builds we attach a DebugTree for convenient local logging, but
|
||||
// release builds rely on system logging only.
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.plant(Timber.DebugTree())
|
||||
Timber.i("F-Droid platform no-op analytics initialized (DebugTree planted).")
|
||||
Logger.setMinSeverity(Severity.Debug)
|
||||
Logger.i { "F-Droid platform no-op analytics initialized (Debug mode }." }
|
||||
} else {
|
||||
Timber.i("F-Droid platform no-op analytics initialized.")
|
||||
Logger.setMinSeverity(Severity.Info)
|
||||
Logger.i { "F-Droid platform no-op analytics initialized." }
|
||||
}
|
||||
}
|
||||
|
||||
override fun setDeviceAttributes(firmwareVersion: String, model: String) {
|
||||
// No-op for F-Droid
|
||||
Timber.d("Set device attributes called: firmwareVersion=$firmwareVersion, deviceHardware=$model")
|
||||
Logger.d { "Set device attributes called: firmwareVersion=$firmwareVersion, deviceHardware=$model" }
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -51,7 +53,7 @@ class FdroidPlatformAnalytics @Inject constructor() : PlatformAnalytics {
|
||||
// No-op for F-Droid, but we can log navigation if needed for debugging
|
||||
if (BuildConfig.DEBUG) {
|
||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
Timber.d("Navigation changed to: ${destination.route}")
|
||||
Logger.d { "Navigation changed to: ${destination.route}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +62,6 @@ class FdroidPlatformAnalytics @Inject constructor() : PlatformAnalytics {
|
||||
get() = false
|
||||
|
||||
override fun track(event: String, vararg properties: DataPair) {
|
||||
Timber.d("Track called: event=$event, properties=${properties.toList()}")
|
||||
Logger.d { "Track called: event=$event, properties=${properties.toList()}" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavHostController
|
||||
import co.touchlab.kermit.LogWriter
|
||||
import co.touchlab.kermit.Severity
|
||||
import com.datadog.android.Datadog
|
||||
import com.datadog.android.DatadogSite
|
||||
import com.datadog.android.compose.ExperimentalTrackingApi
|
||||
@@ -43,7 +45,6 @@ import com.datadog.android.rum.tracking.AcceptAllNavDestinations
|
||||
import com.datadog.android.sessionreplay.SessionReplay
|
||||
import com.datadog.android.sessionreplay.SessionReplayConfiguration
|
||||
import com.datadog.android.sessionreplay.compose.ComposeExtensionSupport
|
||||
import com.datadog.android.timber.DatadogTree
|
||||
import com.datadog.android.trace.Trace
|
||||
import com.datadog.android.trace.TraceConfiguration
|
||||
import com.datadog.android.trace.opentelemetry.DatadogOpenTelemetry
|
||||
@@ -61,10 +62,8 @@ import kotlinx.coroutines.flow.onEach
|
||||
import org.meshtastic.core.analytics.BuildConfig
|
||||
import org.meshtastic.core.analytics.DataPair
|
||||
import org.meshtastic.core.prefs.analytics.AnalyticsPrefs
|
||||
import timber.log.Timber
|
||||
import timber.log.Timber.DebugTree
|
||||
import timber.log.Timber.Tree
|
||||
import javax.inject.Inject
|
||||
import co.touchlab.kermit.Logger as KermitLogger
|
||||
|
||||
/**
|
||||
* Google Play Services specific implementation of [PlatformAnalytics]. This helper initializes and manages Firebase and
|
||||
@@ -102,14 +101,15 @@ constructor(
|
||||
.setBundleWithTraceEnabled(true)
|
||||
.setBundleWithRumEnabled(true)
|
||||
.build()
|
||||
buildList {
|
||||
add(DatadogTree(datadogLogger))
|
||||
add(CrashlyticsTree())
|
||||
val writers = buildList {
|
||||
add(DatadogLogWriter(datadogLogger))
|
||||
add(CrashlyticsLogWriter())
|
||||
if (BuildConfig.DEBUG) {
|
||||
add(DebugTree())
|
||||
add(co.touchlab.kermit.LogcatWriter())
|
||||
}
|
||||
}
|
||||
.forEach(Timber::plant)
|
||||
KermitLogger.setLogWriters(writers)
|
||||
KermitLogger.setMinSeverity(if (BuildConfig.DEBUG) Severity.Debug else Severity.Info)
|
||||
|
||||
// Initial consent state
|
||||
updateAnalyticsConsent(analyticsPrefs.analyticsAllowed)
|
||||
@@ -177,10 +177,10 @@ constructor(
|
||||
*/
|
||||
fun updateAnalyticsConsent(allowed: Boolean) {
|
||||
if (!isPlatformServicesAvailable || isInTestLab) {
|
||||
Timber.i("Analytics not available or in test lab, consent update skipped.")
|
||||
KermitLogger.i { "Analytics not available or in test lab, consent update skipped." }
|
||||
return
|
||||
}
|
||||
Timber.i(if (allowed) "Analytics enabled" else "Analytics disabled")
|
||||
KermitLogger.i { if (allowed) "Analytics enabled" else "Analytics disabled" }
|
||||
|
||||
Datadog.setTrackingConsent(if (allowed) TrackingConsent.GRANTED else TrackingConsent.NOT_GRANTED)
|
||||
Firebase.crashlytics.isCrashlyticsCollectionEnabled = allowed
|
||||
@@ -221,30 +221,45 @@ constructor(
|
||||
override val isPlatformServicesAvailable: Boolean
|
||||
get() = isGooglePlayAvailable && isDatadogAvailable
|
||||
|
||||
private class CrashlyticsTree : Tree() {
|
||||
private class CrashlyticsLogWriter : LogWriter() {
|
||||
companion object {
|
||||
private const val KEY_PRIORITY = "priority"
|
||||
private const val KEY_TAG = "tag"
|
||||
private const val KEY_MESSAGE = "message"
|
||||
}
|
||||
|
||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||
override fun log(severity: Severity, message: String, tag: String, throwable: Throwable?) {
|
||||
if (!Firebase.crashlytics.isCrashlyticsCollectionEnabled) return
|
||||
|
||||
Firebase.crashlytics.setCustomKeys {
|
||||
key(KEY_PRIORITY, priority)
|
||||
key(KEY_TAG, tag ?: "No Tag")
|
||||
key(KEY_PRIORITY, severity.ordinal)
|
||||
key(KEY_TAG, tag)
|
||||
key(KEY_MESSAGE, message)
|
||||
}
|
||||
|
||||
if (t == null) {
|
||||
if (throwable == null) {
|
||||
Firebase.crashlytics.recordException(Exception(message))
|
||||
} else {
|
||||
Firebase.crashlytics.recordException(t)
|
||||
Firebase.crashlytics.recordException(throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DatadogLogWriter(private val datadogLogger: Logger) : LogWriter() {
|
||||
override fun log(severity: Severity, message: String, tag: String, throwable: Throwable?) {
|
||||
val datadogPriority =
|
||||
when (severity) {
|
||||
Severity.Verbose -> android.util.Log.VERBOSE
|
||||
Severity.Debug -> android.util.Log.DEBUG
|
||||
Severity.Info -> android.util.Log.INFO
|
||||
Severity.Warn -> android.util.Log.WARN
|
||||
Severity.Error -> android.util.Log.ERROR
|
||||
Severity.Assert -> android.util.Log.ASSERT
|
||||
}
|
||||
datadogLogger.log(datadogPriority, message, throwable, mapOf("tag" to tag))
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.extractSemanticVersion(): String {
|
||||
val regex = "^(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?".toRegex()
|
||||
val matchResult = regex.find(this)
|
||||
@@ -263,7 +278,7 @@ constructor(
|
||||
is String -> bundle.putString(it.name, it.value as String?) // Explicitly handle String
|
||||
else -> bundle.putString(it.name, it.value.toString()) // Fallback for other types
|
||||
}
|
||||
Timber.tag(TAG).d("Analytics: track $event (${it.name} : ${it.value})")
|
||||
KermitLogger.withTag(TAG).d { "Analytics: track $event (${it.name} : ${it.value})" }
|
||||
}
|
||||
Firebase.analytics.logEvent(event, bundle)
|
||||
}
|
||||
|
||||
@@ -61,5 +61,5 @@ dependencies {
|
||||
implementation(libs.androidx.core.location.altitude)
|
||||
implementation(libs.androidx.paging.common)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
<ID>MagicNumber:LocationRepository.kt$LocationRepository$30</ID>
|
||||
<ID>MagicNumber:LocationRepository.kt$LocationRepository$31</ID>
|
||||
<ID>MagicNumber:PacketRepository.kt$PacketRepository$500</ID>
|
||||
<ID>MaxLineLength:DeviceHardwareRepository.kt$DeviceHardwareRepository$"DeviceHardwareRepository: applying quirk: requiresBootloaderUpgradeForOta=${quirk.requiresBootloaderUpgradeForOta}, infoUrl=${quirk.infoUrl}"</ID>
|
||||
<ID>MaxLineLength:DeviceHardwareRepository.kt$DeviceHardwareRepository$"DeviceHardwareRepository: cache ${if (staleEntity == null) "empty" else "incomplete"} for hwModel=$hwModel, falling back to bundled JSON asset"</ID>
|
||||
<ID>MaxLineLength:DeviceHardwareRepository.kt$DeviceHardwareRepository$"DeviceHardwareRepository: failed to load device hardware from bundled JSON for hwModel=$hwModel"</ID>
|
||||
<ID>MaxLineLength:DeviceHardwareRepository.kt$DeviceHardwareRepository$"DeviceHardwareRepository: lookup after JSON load for hwModel=$hwModel ${if (base != null) "succeeded" else "returned null"}"</ID>
|
||||
<ID>MaxLineLength:DeviceHardwareRepository.kt$DeviceHardwareRepository$"DeviceHardwareRepository: lookup after remote fetch for hwModel=$hwModel ${if (fromDb != null) "succeeded" else "returned null"}"</ID>
|
||||
<ID>TooGenericExceptionCaught:LocationRepository.kt$LocationRepository$e: Exception</ID>
|
||||
<ID>TooManyFunctions:PacketRepository.kt$PacketRepository</ID>
|
||||
</CurrentIssues>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.meshtastic.core.data.repository
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -26,10 +27,8 @@ import kotlinx.serialization.json.Json
|
||||
import org.meshtastic.core.data.model.CustomTileProviderConfig
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import org.meshtastic.core.prefs.map.MapTileProviderPrefs
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.collections.plus
|
||||
|
||||
interface CustomTileProviderRepository {
|
||||
fun getCustomTileProviders(): Flow<List<CustomTileProviderConfig>>
|
||||
@@ -88,7 +87,7 @@ constructor(
|
||||
try {
|
||||
customTileProvidersStateFlow.value = json.decodeFromString<List<CustomTileProviderConfig>>(jsonString)
|
||||
} catch (e: SerializationException) {
|
||||
Timber.e(e, "Error deserializing tile providers")
|
||||
Logger.e(e) { "Error deserializing tile providers" }
|
||||
customTileProvidersStateFlow.value = emptyList()
|
||||
}
|
||||
} else {
|
||||
@@ -102,7 +101,7 @@ constructor(
|
||||
val jsonString = json.encodeToString(providers)
|
||||
mapTileProviderPrefs.customTileProviders = jsonString
|
||||
} catch (e: SerializationException) {
|
||||
Timber.e(e, "Error serializing tile providers")
|
||||
Logger.e(e) { "Error serializing tile providers" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
package org.meshtastic.core.data.datasource
|
||||
|
||||
import android.app.Application
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import org.meshtastic.core.model.BootloaderOtaQuirk
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class BootloaderOtaQuirksJsonDataSource @Inject constructor(private val application: Application) {
|
||||
@@ -32,7 +32,7 @@ class BootloaderOtaQuirksJsonDataSource @Inject constructor(private val applicat
|
||||
val inputStream = application.assets.open("device_bootloader_ota_quirks.json")
|
||||
inputStream.use { Json.decodeFromStream<ListWrapper>(it).devices }
|
||||
}
|
||||
.onFailure { e -> Timber.w(e, "Failed to load device_bootloader_ota_quirks.json") }
|
||||
.onFailure { e -> Logger.w(e) { "Failed to load device_bootloader_ota_quirks.json" } }
|
||||
.getOrDefault(emptyList())
|
||||
|
||||
@Serializable private data class ListWrapper(val devices: List<BootloaderOtaQuirk> = emptyList())
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.meshtastic.core.data.repository
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.meshtastic.core.data.datasource.BootloaderOtaQuirksJsonDataSource
|
||||
@@ -27,7 +28,6 @@ import org.meshtastic.core.database.entity.asExternalModel
|
||||
import org.meshtastic.core.model.BootloaderOtaQuirk
|
||||
import org.meshtastic.core.model.DeviceHardware
|
||||
import org.meshtastic.core.network.DeviceHardwareRemoteDataSource
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -59,45 +59,40 @@ constructor(
|
||||
@Suppress("LongMethod")
|
||||
suspend fun getDeviceHardwareByModel(hwModel: Int, forceRefresh: Boolean = false): Result<DeviceHardware?> =
|
||||
withContext(Dispatchers.IO) {
|
||||
Timber.d(
|
||||
"DeviceHardwareRepository: getDeviceHardwareByModel(hwModel=%d, forceRefresh=%b)",
|
||||
hwModel,
|
||||
forceRefresh,
|
||||
)
|
||||
Logger.d {
|
||||
"DeviceHardwareRepository: getDeviceHardwareByModel(hwModel=$hwModel, forceRefresh=$forceRefresh)"
|
||||
}
|
||||
|
||||
val quirks = loadQuirks()
|
||||
|
||||
if (forceRefresh) {
|
||||
Timber.d("DeviceHardwareRepository: forceRefresh=true, clearing local device hardware cache")
|
||||
Logger.d { "DeviceHardwareRepository: forceRefresh=true, clearing local device hardware cache" }
|
||||
localDataSource.deleteAllDeviceHardware()
|
||||
} else {
|
||||
// 1. Attempt to retrieve from cache first
|
||||
val cachedEntity = localDataSource.getByHwModel(hwModel)
|
||||
if (cachedEntity != null && !cachedEntity.isStale()) {
|
||||
Timber.d("DeviceHardwareRepository: using fresh cached device hardware for hwModel=%d", hwModel)
|
||||
Logger.d { "DeviceHardwareRepository: using fresh cached device hardware for hwModel=$hwModel" }
|
||||
return@withContext Result.success(
|
||||
applyBootloaderQuirk(hwModel, cachedEntity.asExternalModel(), quirks),
|
||||
)
|
||||
}
|
||||
Timber.d("DeviceHardwareRepository: no fresh cache for hwModel=%d, attempting remote fetch", hwModel)
|
||||
Logger.d { "DeviceHardwareRepository: no fresh cache for hwModel=$hwModel, attempting remote fetch" }
|
||||
}
|
||||
|
||||
// 2. Fetch from remote API
|
||||
runCatching {
|
||||
Timber.d("DeviceHardwareRepository: fetching device hardware from remote API")
|
||||
Logger.d { "DeviceHardwareRepository: fetching device hardware from remote API" }
|
||||
val remoteHardware = remoteDataSource.getAllDeviceHardware()
|
||||
Timber.d(
|
||||
"DeviceHardwareRepository: remote API returned %d device hardware entries",
|
||||
remoteHardware.size,
|
||||
)
|
||||
Logger.d {
|
||||
"DeviceHardwareRepository: remote API returned ${remoteHardware.size} device hardware entries"
|
||||
}
|
||||
|
||||
localDataSource.insertAllDeviceHardware(remoteHardware)
|
||||
val fromDb = localDataSource.getByHwModel(hwModel)?.asExternalModel()
|
||||
Timber.d(
|
||||
"DeviceHardwareRepository: lookup after remote fetch for hwModel=%d %s",
|
||||
hwModel,
|
||||
if (fromDb != null) "succeeded" else "returned null",
|
||||
)
|
||||
Logger.d {
|
||||
"DeviceHardwareRepository: lookup after remote fetch for hwModel=$hwModel ${if (fromDb != null) "succeeded" else "returned null"}"
|
||||
}
|
||||
fromDb
|
||||
}
|
||||
.onSuccess {
|
||||
@@ -105,57 +100,48 @@ constructor(
|
||||
return@withContext Result.success(applyBootloaderQuirk(hwModel, it, quirks))
|
||||
}
|
||||
.onFailure { e ->
|
||||
Timber.w(
|
||||
e,
|
||||
"DeviceHardwareRepository: failed to fetch device hardware from server for hwModel=%d",
|
||||
hwModel,
|
||||
)
|
||||
Logger.w(e) {
|
||||
"DeviceHardwareRepository: failed to fetch device hardware from server for hwModel=$hwModel"
|
||||
}
|
||||
|
||||
// 3. Attempt to use stale cache as a fallback, but only if it looks complete.
|
||||
val staleEntity = localDataSource.getByHwModel(hwModel)
|
||||
if (staleEntity != null && !staleEntity.isIncomplete()) {
|
||||
Timber.d("DeviceHardwareRepository: using stale cached device hardware for hwModel=%d", hwModel)
|
||||
Logger.d { "DeviceHardwareRepository: using stale cached device hardware for hwModel=$hwModel" }
|
||||
return@withContext Result.success(
|
||||
applyBootloaderQuirk(hwModel, staleEntity.asExternalModel(), quirks),
|
||||
)
|
||||
}
|
||||
|
||||
// 4. Fallback to bundled JSON if cache is empty or incomplete
|
||||
Timber.d(
|
||||
"DeviceHardwareRepository: cache %s for hwModel=%d, falling back to bundled JSON asset",
|
||||
if (staleEntity == null) "empty" else "incomplete",
|
||||
hwModel,
|
||||
)
|
||||
Logger.d {
|
||||
"DeviceHardwareRepository: cache ${if (staleEntity == null) "empty" else "incomplete"} for hwModel=$hwModel, falling back to bundled JSON asset"
|
||||
}
|
||||
return@withContext loadFromBundledJson(hwModel, quirks)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadFromBundledJson(hwModel: Int, quirks: List<BootloaderOtaQuirk>): Result<DeviceHardware?> =
|
||||
runCatching {
|
||||
Timber.d("DeviceHardwareRepository: loading device hardware from bundled JSON for hwModel=%d", hwModel)
|
||||
Logger.d { "DeviceHardwareRepository: loading device hardware from bundled JSON for hwModel=$hwModel" }
|
||||
val jsonHardware = jsonDataSource.loadDeviceHardwareFromJsonAsset()
|
||||
Timber.d(
|
||||
"DeviceHardwareRepository: bundled JSON returned %d device hardware entries",
|
||||
jsonHardware.size,
|
||||
)
|
||||
Logger.d {
|
||||
"DeviceHardwareRepository: bundled JSON returned ${jsonHardware.size} device hardware entries"
|
||||
}
|
||||
|
||||
localDataSource.insertAllDeviceHardware(jsonHardware)
|
||||
val base = localDataSource.getByHwModel(hwModel)?.asExternalModel()
|
||||
Timber.d(
|
||||
"DeviceHardwareRepository: lookup after JSON load for hwModel=%d %s",
|
||||
hwModel,
|
||||
if (base != null) "succeeded" else "returned null",
|
||||
)
|
||||
Logger.d {
|
||||
"DeviceHardwareRepository: lookup after JSON load for hwModel=$hwModel ${if (base != null) "succeeded" else "returned null"}"
|
||||
}
|
||||
|
||||
applyBootloaderQuirk(hwModel, base, quirks)
|
||||
}
|
||||
.also { result ->
|
||||
result.exceptionOrNull()?.let { e ->
|
||||
Timber.e(
|
||||
e,
|
||||
"DeviceHardwareRepository: failed to load device hardware from bundled JSON for hwModel=%d",
|
||||
hwModel,
|
||||
)
|
||||
Logger.e(e) {
|
||||
"DeviceHardwareRepository: failed to load device hardware from bundled JSON for hwModel=$hwModel"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +160,7 @@ constructor(
|
||||
|
||||
private fun loadQuirks(): List<BootloaderOtaQuirk> {
|
||||
val quirks = bootloaderOtaQuirksJsonDataSource.loadBootloaderOtaQuirksFromJsonAsset()
|
||||
Timber.d("DeviceHardwareRepository: loaded %d bootloader quirks", quirks.size)
|
||||
Logger.d { "DeviceHardwareRepository: loaded ${quirks.size} bootloader quirks" }
|
||||
return quirks
|
||||
}
|
||||
|
||||
@@ -186,17 +172,11 @@ constructor(
|
||||
if (base == null) return null
|
||||
|
||||
val quirk = quirks.firstOrNull { it.hwModel == hwModel }
|
||||
Timber.d(
|
||||
"DeviceHardwareRepository: applyBootloaderQuirk for hwModel=%d, quirk found=%b",
|
||||
hwModel,
|
||||
quirk != null,
|
||||
)
|
||||
Logger.d { "DeviceHardwareRepository: applyBootloaderQuirk for hwModel=$hwModel, quirk found=${quirk != null}" }
|
||||
return if (quirk != null) {
|
||||
Timber.d(
|
||||
"DeviceHardwareRepository: applying quirk: requiresBootloaderUpgradeForOta=%b, infoUrl=%s",
|
||||
quirk.requiresBootloaderUpgradeForOta,
|
||||
quirk.infoUrl,
|
||||
)
|
||||
Logger.d {
|
||||
"DeviceHardwareRepository: applying quirk: requiresBootloaderUpgradeForOta=${quirk.requiresBootloaderUpgradeForOta}, infoUrl=${quirk.infoUrl}"
|
||||
}
|
||||
base.copy(
|
||||
requiresBootloaderUpgradeForOta = quirk.requiresBootloaderUpgradeForOta,
|
||||
bootloaderInfoUrl = quirk.infoUrl,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.meshtastic.core.data.repository
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import org.meshtastic.core.data.datasource.FirmwareReleaseJsonDataSource
|
||||
@@ -26,7 +27,6 @@ import org.meshtastic.core.database.entity.FirmwareReleaseEntity
|
||||
import org.meshtastic.core.database.entity.FirmwareReleaseType
|
||||
import org.meshtastic.core.database.entity.asExternalModel
|
||||
import org.meshtastic.core.network.FirmwareReleaseRemoteDataSource
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -68,7 +68,7 @@ constructor(
|
||||
// This gives the UI something to show immediately.
|
||||
val cachedRelease = localDataSource.getLatestRelease(releaseType)
|
||||
cachedRelease?.let {
|
||||
Timber.d("Emitting cached firmware for $releaseType (isStale=${it.isStale()})")
|
||||
Logger.d { "Emitting cached firmware for $releaseType (isStale=${it.isStale()})" }
|
||||
emit(it.asExternalModel())
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ constructor(
|
||||
// The `distinctUntilChanged()` operator on the collector side will prevent
|
||||
// re-emitting the same data if the cache wasn't actually updated.
|
||||
val finalRelease = localDataSource.getLatestRelease(releaseType)
|
||||
Timber.d("Emitting final firmware for $releaseType from cache.")
|
||||
Logger.d { "Emitting final firmware for $releaseType from cache." }
|
||||
emit(finalRelease?.asExternalModel())
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ constructor(
|
||||
private suspend fun updateCacheFromSources() {
|
||||
val remoteFetchSuccess =
|
||||
runCatching {
|
||||
Timber.d("Fetching fresh firmware releases from remote API.")
|
||||
Logger.d { "Fetching fresh firmware releases from remote API." }
|
||||
val networkReleases = remoteDataSource.getFirmwareReleases()
|
||||
|
||||
// The API fetches all release types, so we cache them all at once.
|
||||
@@ -109,13 +109,13 @@ constructor(
|
||||
|
||||
// If remote fetch failed, try the JSON fallback as a last resort.
|
||||
if (!remoteFetchSuccess) {
|
||||
Timber.w("Remote fetch failed, attempting to cache from bundled JSON.")
|
||||
Logger.w { "Remote fetch failed, attempting to cache from bundled JSON." }
|
||||
runCatching {
|
||||
val jsonReleases = jsonDataSource.loadFirmwareReleaseFromJsonAsset()
|
||||
localDataSource.insertFirmwareReleases(jsonReleases.releases.stable, FirmwareReleaseType.STABLE)
|
||||
localDataSource.insertFirmwareReleases(jsonReleases.releases.alpha, FirmwareReleaseType.ALPHA)
|
||||
}
|
||||
.onFailure { Timber.w("Failed to cache from JSON: ${it.message}") }
|
||||
.onFailure { Logger.w { "Failed to cache from JSON: ${it.message}" } }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import androidx.core.location.LocationListenerCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.core.location.LocationRequestCompat
|
||||
import androidx.core.location.altitude.AltitudeConverterCompat
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.asExecutor
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
@@ -34,7 +35,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import org.meshtastic.core.analytics.platform.PlatformAnalytics
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -68,7 +68,7 @@ constructor(
|
||||
try {
|
||||
AltitudeConverterCompat.addMslAltitudeToLocation(context, location)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "addMslAltitudeToLocation() failed")
|
||||
Logger.e(e) { "addMslAltitudeToLocation() failed" }
|
||||
}
|
||||
}
|
||||
// info("New location: $location")
|
||||
@@ -85,9 +85,9 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
Timber.i(
|
||||
"Starting location updates with $providerList intervalMs=${intervalMs}ms and minDistanceM=${minDistanceM}m",
|
||||
)
|
||||
Logger.i {
|
||||
"Starting location updates with $providerList intervalMs=${intervalMs}ms and minDistanceM=${minDistanceM}m"
|
||||
}
|
||||
_receivingLocationUpdates.value = true
|
||||
analytics.track("location_start") // Figure out how many users needed to use the phone GPS
|
||||
|
||||
@@ -106,7 +106,7 @@ constructor(
|
||||
}
|
||||
|
||||
awaitClose {
|
||||
Timber.i("Stopping location requests")
|
||||
Logger.i { "Stopping location requests" }
|
||||
_receivingLocationUpdates.value = false
|
||||
analytics.track("location_stop")
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ dependencies {
|
||||
|
||||
implementation(libs.androidx.room.paging)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
|
||||
ksp(libs.androidx.room.compiler)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.meshtastic.core.database
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.protobuf.ByteString
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import kotlinx.serialization.json.Json
|
||||
@@ -25,7 +26,6 @@ import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import org.meshtastic.proto.PaxcountProtos
|
||||
import org.meshtastic.proto.TelemetryProtos
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class Converters {
|
||||
@@ -45,7 +45,7 @@ class Converters {
|
||||
fun bytesToFromRadio(bytes: ByteArray): MeshProtos.FromRadio = try {
|
||||
MeshProtos.FromRadio.parseFrom(bytes)
|
||||
} catch (ex: InvalidProtocolBufferException) {
|
||||
Timber.e(ex, "bytesToFromRadio TypeConverter error")
|
||||
Logger.e(ex) { "bytesToFromRadio TypeConverter error" }
|
||||
MeshProtos.FromRadio.getDefaultInstance()
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class Converters {
|
||||
fun bytesToUser(bytes: ByteArray): MeshProtos.User = try {
|
||||
MeshProtos.User.parseFrom(bytes)
|
||||
} catch (ex: InvalidProtocolBufferException) {
|
||||
Timber.e(ex, "bytesToUser TypeConverter error")
|
||||
Logger.e(ex) { "bytesToUser TypeConverter error" }
|
||||
MeshProtos.User.getDefaultInstance()
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class Converters {
|
||||
fun bytesToPosition(bytes: ByteArray): MeshProtos.Position = try {
|
||||
MeshProtos.Position.parseFrom(bytes)
|
||||
} catch (ex: InvalidProtocolBufferException) {
|
||||
Timber.e(ex, "bytesToPosition TypeConverter error")
|
||||
Logger.e(ex) { "bytesToPosition TypeConverter error" }
|
||||
MeshProtos.Position.getDefaultInstance()
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class Converters {
|
||||
fun bytesToTelemetry(bytes: ByteArray): TelemetryProtos.Telemetry = try {
|
||||
TelemetryProtos.Telemetry.parseFrom(bytes)
|
||||
} catch (ex: InvalidProtocolBufferException) {
|
||||
Timber.e(ex, "bytesToTelemetry TypeConverter error")
|
||||
Logger.e(ex) { "bytesToTelemetry TypeConverter error" }
|
||||
TelemetryProtos.Telemetry.newBuilder().build() // Return an empty Telemetry object
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ class Converters {
|
||||
fun bytesToPaxcounter(bytes: ByteArray): PaxcountProtos.Paxcount = try {
|
||||
PaxcountProtos.Paxcount.parseFrom(bytes)
|
||||
} catch (ex: InvalidProtocolBufferException) {
|
||||
Timber.e(ex, "bytesToPaxcounter TypeConverter error")
|
||||
Logger.e(ex) { "bytesToPaxcounter TypeConverter error" }
|
||||
PaxcountProtos.Paxcount.getDefaultInstance()
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class Converters {
|
||||
fun bytesToMetadata(bytes: ByteArray): MeshProtos.DeviceMetadata = try {
|
||||
MeshProtos.DeviceMetadata.parseFrom(bytes)
|
||||
} catch (ex: InvalidProtocolBufferException) {
|
||||
Timber.e(ex, "bytesToMetadata TypeConverter error")
|
||||
Logger.e(ex) { "bytesToMetadata TypeConverter error" }
|
||||
MeshProtos.DeviceMetadata.getDefaultInstance()
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.room.Room
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
@@ -33,7 +34,6 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.security.MessageDigest
|
||||
import javax.inject.Inject
|
||||
@@ -106,7 +106,7 @@ class DatabaseManager @Inject constructor(private val app: Application) {
|
||||
// One-time cleanup: remove legacy DB if present and not active
|
||||
managerScope.launch(Dispatchers.IO) { cleanupLegacyDbIfNeeded(activeDbName = dbName) }
|
||||
|
||||
Timber.i("Switched active DB to ${anonymizeDbName(dbName)} for address ${anonymizeAddress(address)}")
|
||||
Logger.i { "Switched active DB to ${anonymizeDbName(dbName)} for address ${anonymizeAddress(address)}" }
|
||||
}
|
||||
|
||||
/** Execute [block] with the current DB instance. */
|
||||
@@ -138,26 +138,28 @@ class DatabaseManager @Inject constructor(private val app: Application) {
|
||||
// Only enforce the limit over device-specific DBs; exclude legacy and default DBs
|
||||
val deviceDbs =
|
||||
all.filterNot { it == DatabaseConstants.LEGACY_DB_NAME || it == DatabaseConstants.DEFAULT_DB_NAME }
|
||||
Timber.d(
|
||||
"LRU check: limit=%d, active=%s, deviceDbs=%s",
|
||||
limit,
|
||||
anonymizeDbName(activeDbName),
|
||||
deviceDbs.joinToString(", ") { anonymizeDbName(it) },
|
||||
)
|
||||
Logger.d {
|
||||
"LRU check: limit=$limit, active=${anonymizeDbName(
|
||||
activeDbName,
|
||||
)}, deviceDbs=${deviceDbs.joinToString(", ") {
|
||||
anonymizeDbName(it)
|
||||
}}"
|
||||
}
|
||||
if (deviceDbs.size <= limit) return
|
||||
val usageSnapshot = deviceDbs.associateWith { lastUsed(it) }
|
||||
Timber.d(
|
||||
"LRU lastUsed(ms): %s",
|
||||
usageSnapshot.entries.joinToString(", ") { (name, ts) -> "${anonymizeDbName(name)}=$ts" },
|
||||
)
|
||||
Logger.d {
|
||||
"LRU lastUsed(ms): ${usageSnapshot.entries.joinToString(", ") { (name, ts) ->
|
||||
"${anonymizeDbName(name)}=$ts"
|
||||
}}"
|
||||
}
|
||||
val victims = selectEvictionVictims(deviceDbs, activeDbName, limit, usageSnapshot)
|
||||
Timber.i("LRU victims: %s", victims.joinToString(", ") { anonymizeDbName(it) })
|
||||
Logger.i { "LRU victims: ${victims.joinToString(", ") { anonymizeDbName(it) }}" }
|
||||
victims.forEach { name ->
|
||||
runCatching { dbCache.remove(name)?.close() }
|
||||
.onFailure { Timber.w(it, "Failed to close database %s", name) }
|
||||
.onFailure { Logger.w(it) { "Failed to close database $name" } }
|
||||
app.deleteDatabase(name)
|
||||
prefs.edit().remove(lastUsedKey(name)).apply()
|
||||
Timber.i("Evicted cached DB ${anonymizeDbName(name)}")
|
||||
Logger.i { "Evicted cached DB ${anonymizeDbName(name)}" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,12 +188,12 @@ class DatabaseManager @Inject constructor(private val app: Application) {
|
||||
val legacyFile = getDbFile(app, legacy)
|
||||
if (legacyFile != null) {
|
||||
runCatching { dbCache.remove(legacy)?.close() }
|
||||
.onFailure { Timber.w(it, "Failed to close legacy database %s before deletion", legacy) }
|
||||
.onFailure { Logger.w(it) { "Failed to close legacy database $legacy before deletion" } }
|
||||
val deleted = app.deleteDatabase(legacy)
|
||||
if (deleted) {
|
||||
Timber.i("Deleted legacy DB ${anonymizeDbName(legacy)}")
|
||||
Logger.i { "Deleted legacy DB ${anonymizeDbName(legacy)}" }
|
||||
} else {
|
||||
Timber.w("Attempted to delete legacy DB %s but deleteDatabase returned false", legacy)
|
||||
Logger.w { "Attempted to delete legacy DB $legacy but deleteDatabase returned false" }
|
||||
}
|
||||
}
|
||||
prefs.edit().putBoolean(DatabaseConstants.LEGACY_DB_CLEANED_KEY, true).apply()
|
||||
|
||||
@@ -48,5 +48,5 @@ dependencies {
|
||||
implementation(libs.androidx.datastore)
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.json.Json
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -43,9 +43,9 @@ class BootloaderWarningDataSource @Inject constructor(private val dataStore: Dat
|
||||
runCatching { Json.decodeFromString<List<String>>(jsonString).toSet() }
|
||||
.onFailure { e ->
|
||||
if (e is IllegalArgumentException || e is SerializationException) {
|
||||
Timber.w(e, "Failed to parse dismissed bootloader warning addresses, resetting preference")
|
||||
Logger.w(e) { "Failed to parse dismissed bootloader warning addresses, resetting preference" }
|
||||
} else {
|
||||
Timber.w(e, "Unexpected error while parsing dismissed bootloader warning addresses")
|
||||
Logger.w(e) { "Unexpected error while parsing dismissed bootloader warning addresses" }
|
||||
}
|
||||
}
|
||||
.getOrDefault(emptySet())
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
package org.meshtastic.core.datastore
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import org.meshtastic.proto.AppOnlyProtos.ChannelSet
|
||||
import org.meshtastic.proto.ChannelProtos.Channel
|
||||
import org.meshtastic.proto.ChannelProtos.ChannelSettings
|
||||
import org.meshtastic.proto.ConfigProtos
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -36,7 +36,7 @@ class ChannelSetDataSource @Inject constructor(private val channelSetStore: Data
|
||||
channelSetStore.data.catch { exception ->
|
||||
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||
if (exception is IOException) {
|
||||
Timber.e("Error reading DeviceConfig settings: ${exception.message}")
|
||||
Logger.e { "Error reading DeviceConfig settings: ${exception.message}" }
|
||||
emit(ChannelSet.getDefaultInstance())
|
||||
} else {
|
||||
throw exception
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
package org.meshtastic.core.datastore
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import org.meshtastic.proto.ConfigProtos.Config
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -34,7 +34,7 @@ class LocalConfigDataSource @Inject constructor(private val localConfigStore: Da
|
||||
localConfigStore.data.catch { exception ->
|
||||
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||
if (exception is IOException) {
|
||||
Timber.e("Error reading LocalConfig settings: ${exception.message}")
|
||||
Logger.e { "Error reading LocalConfig settings: ${exception.message}" }
|
||||
emit(LocalConfig.getDefaultInstance())
|
||||
} else {
|
||||
throw exception
|
||||
@@ -53,7 +53,7 @@ class LocalConfigDataSource @Inject constructor(private val localConfigStore: Da
|
||||
if (localField != null) {
|
||||
builder.setField(localField, value)
|
||||
} else {
|
||||
Timber.e("Error writing LocalConfig settings: ${config.payloadVariantCase}")
|
||||
Logger.e { "Error writing LocalConfig settings: ${config.payloadVariantCase}" }
|
||||
}
|
||||
}
|
||||
builder.build()
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
package org.meshtastic.core.datastore
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalModuleConfig
|
||||
import org.meshtastic.proto.ModuleConfigProtos.ModuleConfig
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -34,7 +34,7 @@ class ModuleConfigDataSource @Inject constructor(private val moduleConfigStore:
|
||||
moduleConfigStore.data.catch { exception ->
|
||||
// dataStore.data throws an IOException when an error is encountered when reading data
|
||||
if (exception is IOException) {
|
||||
Timber.e("Error reading LocalModuleConfig settings: ${exception.message}")
|
||||
Logger.e { "Error reading LocalModuleConfig settings: ${exception.message}" }
|
||||
emit(LocalModuleConfig.getDefaultInstance())
|
||||
} else {
|
||||
throw exception
|
||||
@@ -53,7 +53,7 @@ class ModuleConfigDataSource @Inject constructor(private val moduleConfigStore:
|
||||
if (localField != null) {
|
||||
builder.setField(localField, value)
|
||||
} else {
|
||||
Timber.e("Error writing LocalModuleConfig settings: ${config.payloadVariantCase}")
|
||||
Logger.e { "Error writing LocalModuleConfig settings: ${config.payloadVariantCase}" }
|
||||
}
|
||||
}
|
||||
builder.build()
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -29,7 +30,6 @@ import kotlinx.serialization.json.Json
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.meshtastic.core.datastore.model.RecentAddress
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -46,11 +46,11 @@ class RecentAddressesDataSource @Inject constructor(private val dataStore: DataS
|
||||
try {
|
||||
Json.decodeFromString<List<RecentAddress>>(jsonString)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.w("Could not parse recent addresses, falling back to legacy parsing: ${e.message}")
|
||||
Logger.w { "Could not parse recent addresses, falling back to legacy parsing: ${e.message}" }
|
||||
// Fallback to legacy parsing
|
||||
parseLegacyRecentAddresses(jsonString)
|
||||
} catch (e: SerializationException) {
|
||||
Timber.w("Could not parse recent addresses, falling back to legacy parsing: ${e.message}")
|
||||
Logger.w { "Could not parse recent addresses, falling back to legacy parsing: ${e.message}" }
|
||||
// Fallback to legacy parsing
|
||||
parseLegacyRecentAddresses(jsonString)
|
||||
}
|
||||
@@ -73,7 +73,7 @@ class RecentAddressesDataSource @Inject constructor(private val dataStore: DataS
|
||||
}
|
||||
else -> {
|
||||
// Unknown format, log or handle as an error if necessary
|
||||
Timber.w("Unknown item type in recent IP addresses: $item")
|
||||
Logger.w { "Unknown item type in recent IP addresses: $item" }
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ dependencies {
|
||||
|
||||
implementation(libs.androidx.annotation)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
implementation(libs.zxing.android.embedded) { isTransitive = false }
|
||||
implementation(libs.zxing.core)
|
||||
|
||||
|
||||
@@ -2,30 +2,6 @@
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>MagicNumber:Channel.kt$0xff</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$.03125f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$.0625f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$.203125f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$.40625f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$.8125f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$1.6250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$1000f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$1600</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$200</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$3.25f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$31</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$400</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$5</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$62</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$800</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.LONG_FAST$.250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.LONG_MODERATE$.125f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.LONG_SLOW$.125f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.MEDIUM_FAST$.250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.MEDIUM_SLOW$.250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.SHORT_FAST$.250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.SHORT_SLOW$.250f</ID>
|
||||
<ID>MagicNumber:ChannelOption.kt$ChannelOption.VERY_LONG_SLOW$.0625f</ID>
|
||||
<ID>MagicNumber:ChannelSet.kt$40</ID>
|
||||
<ID>MagicNumber:ChannelSet.kt$960</ID>
|
||||
<ID>SwallowedException:ChannelSet.kt$ex: Throwable</ID>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package org.meshtastic.core.model
|
||||
|
||||
import timber.log.Timber
|
||||
import co.touchlab.kermit.Logger
|
||||
|
||||
/** Provide structured access to parse and compare device version strings */
|
||||
data class DeviceVersion(val asString: String) : Comparable<DeviceVersion> {
|
||||
@@ -28,7 +28,7 @@ data class DeviceVersion(val asString: String) : Comparable<DeviceVersion> {
|
||||
try {
|
||||
verStringToInt(asString)
|
||||
} catch (e: Exception) {
|
||||
Timber.w("Exception while parsing version '$asString', assuming version 0")
|
||||
Logger.w { "Exception while parsing version '$asString', assuming version 0" }
|
||||
0
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,13 @@ package org.meshtastic.core.model.util
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.util.Base64
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.MultiFormatWriter
|
||||
import com.journeyapps.barcodescanner.BarcodeEncoder
|
||||
import org.meshtastic.core.model.Channel
|
||||
import org.meshtastic.proto.AppOnlyProtos.ChannelSet
|
||||
import timber.log.Timber
|
||||
import java.net.MalformedURLException
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
private const val MESHTASTIC_HOST = "meshtastic.org"
|
||||
private const val CHANNEL_PATH = "/e/"
|
||||
@@ -86,6 +85,6 @@ fun ChannelSet.qrCode(shouldAdd: Boolean): Bitmap? = try {
|
||||
val barcodeEncoder = BarcodeEncoder()
|
||||
barcodeEncoder.createBitmap(bitMatrix)
|
||||
} catch (ex: Throwable) {
|
||||
Timber.e("URL was too complex to render as barcode")
|
||||
Logger.e { "URL was too complex to render as barcode" }
|
||||
null
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@ dependencies {
|
||||
implementation(projects.core.proto)
|
||||
implementation(libs.javax.inject)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -25,7 +26,6 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import org.meshtastic.proto.MeshProtos.MeshPacket
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -66,7 +66,7 @@ class ServiceRepository @Inject constructor() {
|
||||
get() = _clientNotification
|
||||
|
||||
fun setClientNotification(notification: MeshProtos.ClientNotification?) {
|
||||
Timber.e(notification?.message.orEmpty())
|
||||
Logger.e { notification?.message.orEmpty() }
|
||||
|
||||
_clientNotification.value = notification
|
||||
}
|
||||
@@ -80,7 +80,7 @@ class ServiceRepository @Inject constructor() {
|
||||
get() = _errorMessage
|
||||
|
||||
fun setErrorMessage(text: String) {
|
||||
Timber.e(text)
|
||||
Logger.e { text }
|
||||
_errorMessage.value = text
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ dependencies {
|
||||
implementation(libs.guava)
|
||||
implementation(libs.zxing.core)
|
||||
implementation(libs.zxing.android.embedded)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
|
||||
|
||||
@@ -2,18 +2,6 @@
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>ComposableParamOrder:AlertDialogs.kt$SimpleAlertDialog</ID>
|
||||
<ID>ComposableParamOrder:EditBase64Preference.kt$EditBase64Preference</ID>
|
||||
<ID>ComposableParamOrder:EditTextPreference.kt$EditTextPreference</ID>
|
||||
<ID>ComposableParamOrder:MainAppBar.kt$MainAppBar</ID>
|
||||
<ID>ComposableParamOrder:MaterialBatteryInfo.kt$MaterialBatteryInfo</ID>
|
||||
<ID>ComposableParamOrder:NodeChip.kt$NodeChip</ID>
|
||||
<ID>ComposableParamOrder:NodeKeyStatusIcon.kt$NodeKeyStatusIcon</ID>
|
||||
<ID>ComposableParamOrder:SignalInfo.kt$SignalInfo</ID>
|
||||
<ID>ComposableParamOrder:SwitchPreference.kt$SwitchPreference</ID>
|
||||
<ID>ContentSlotReused:AdaptiveTwoPane.kt$second</ID>
|
||||
<ID>LambdaParameterEventTrailing:ContactSharing.kt$onSharedContactRequested</ID>
|
||||
<ID>LambdaParameterEventTrailing:MainAppBar.kt$onClickChip</ID>
|
||||
<ID>MagicNumber:EditIPv4Preference.kt$0xff</ID>
|
||||
<ID>MagicNumber:EditIPv4Preference.kt$16</ID>
|
||||
<ID>MagicNumber:EditIPv4Preference.kt$24</ID>
|
||||
@@ -22,47 +10,5 @@
|
||||
<ID>MagicNumber:EditListPreference.kt$12345</ID>
|
||||
<ID>MagicNumber:EditListPreference.kt$67890</ID>
|
||||
<ID>MagicNumber:LazyColumnDragAndDropDemo.kt$50</ID>
|
||||
<ID>ModifierMissing:AdaptiveTwoPane.kt$AdaptiveTwoPane</ID>
|
||||
<ID>ModifierMissing:ChannelItem.kt$ChannelItem</ID>
|
||||
<ID>ModifierMissing:ChannelSelection.kt$ChannelSelection</ID>
|
||||
<ID>ModifierMissing:ContactSharing.kt$SharedContactDialog</ID>
|
||||
<ID>ModifierMissing:EmojiPicker.kt$EmojiPicker</ID>
|
||||
<ID>ModifierMissing:EmojiPicker.kt$EmojiPickerDialog</ID>
|
||||
<ID>ModifierMissing:IndoorAirQuality.kt$IndoorAirQuality</ID>
|
||||
<ID>ModifierMissing:LoraSignalIndicator.kt$LoraSignalIndicator</ID>
|
||||
<ID>ModifierMissing:LoraSignalIndicator.kt$Rssi</ID>
|
||||
<ID>ModifierMissing:LoraSignalIndicator.kt$Snr</ID>
|
||||
<ID>ModifierMissing:LoraSignalIndicator.kt$SnrAndRssi</ID>
|
||||
<ID>ModifierMissing:PreferenceDivider.kt$PreferenceDivider</ID>
|
||||
<ID>ModifierMissing:SecurityIcon.kt$SecurityIcon</ID>
|
||||
<ID>ModifierMissing:SharedContactDialog.kt$SharedContactDialog</ID>
|
||||
<ID>ModifierMissing:SimpleAlertDialog.kt$SimpleAlertDialog</ID>
|
||||
<ID>ModifierMissing:SlidingSelector.kt$OptionLabel</ID>
|
||||
<ID>ModifierNotUsedAtRoot:TextDividerPreference.kt$modifier = modifier.fillMaxWidth().padding(all = 16.dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:TextDividerPreference.kt$modifier = modifier.fillMaxWidth().wrapContentWidth(Alignment.End)</ID>
|
||||
<ID>ModifierReused:PreferenceCategory.kt$Card(modifier = modifier.padding(bottom = 8.dp)) { Column( modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { ProvideTextStyle(MaterialTheme.typography.bodyLarge) { content() } } }</ID>
|
||||
<ID>ModifierReused:PreferenceCategory.kt$Text( text, modifier = modifier.padding(start = 16.dp, top = 24.dp, bottom = 8.dp, end = 16.dp), style = MaterialTheme.typography.titleLarge, )</ID>
|
||||
<ID>ModifierReused:TextDividerPreference.kt$Card(modifier = modifier.fillMaxWidth()) { Row(modifier = modifier.fillMaxWidth().padding(all = 16.dp), verticalAlignment = Alignment.CenterVertically) { Text( text = title, style = MaterialTheme.typography.bodyLarge, color = if (!enabled) { MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) } else { Color.Unspecified }, ) if (trailingIcon != null) { Icon(trailingIcon, "trailingIcon", modifier = modifier.fillMaxWidth().wrapContentWidth(Alignment.End)) } } }</ID>
|
||||
<ID>ModifierReused:TextDividerPreference.kt$Icon(trailingIcon, "trailingIcon", modifier = modifier.fillMaxWidth().wrapContentWidth(Alignment.End))</ID>
|
||||
<ID>ModifierReused:TextDividerPreference.kt$Row(modifier = modifier.fillMaxWidth().padding(all = 16.dp), verticalAlignment = Alignment.CenterVertically) { Text( text = title, style = MaterialTheme.typography.bodyLarge, color = if (!enabled) { MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) } else { Color.Unspecified }, ) if (trailingIcon != null) { Icon(trailingIcon, "trailingIcon", modifier = modifier.fillMaxWidth().wrapContentWidth(Alignment.End)) } }</ID>
|
||||
<ID>MultipleEmitters:PreferenceCategory.kt$PreferenceCategory</ID>
|
||||
<ID>ParameterNaming:BitwisePreference.kt$onItemSelected</ID>
|
||||
<ID>ParameterNaming:ChannelSelection.kt$onSelected</ID>
|
||||
<ID>ParameterNaming:ContactSharing.kt$onSharedContactRequested</ID>
|
||||
<ID>ParameterNaming:DropDownPreference.kt$onItemSelected</ID>
|
||||
<ID>ParameterNaming:EditIPv4Preference.kt$onValueChanged</ID>
|
||||
<ID>ParameterNaming:EditListPreference.kt$onValuesChanged</ID>
|
||||
<ID>ParameterNaming:EditPasswordPreference.kt$onValueChanged</ID>
|
||||
<ID>ParameterNaming:EditTextPreference.kt$onValueChanged</ID>
|
||||
<ID>ParameterNaming:PositionPrecisionPreference.kt$onValueChanged</ID>
|
||||
<ID>ParameterNaming:PreferenceFooter.kt$onNegativeClicked</ID>
|
||||
<ID>ParameterNaming:PreferenceFooter.kt$onPositiveClicked</ID>
|
||||
<ID>ParameterNaming:SlidingSelector.kt$onOptionSelected</ID>
|
||||
<ID>PreviewPublic:IndoorAirQuality.kt$IAQScalePreview</ID>
|
||||
<ID>PreviewPublic:LazyColumnDragAndDropDemo.kt$LazyColumnDragAndDropDemo</ID>
|
||||
<ID>PreviewPublic:MaterialBatteryInfo.kt$MaterialBatteryInfoPreview</ID>
|
||||
<ID>PreviewPublic:SignalInfo.kt$SignalInfoPreview</ID>
|
||||
<ID>PreviewPublic:SignalInfo.kt$SignalInfoSelfPreview</ID>
|
||||
<ID>PreviewPublic:SignalInfo.kt$SignalInfoSimplePreview</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
||||
@@ -43,6 +43,7 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.isGranted
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
@@ -65,7 +66,6 @@ import org.meshtastic.core.ui.R
|
||||
import org.meshtastic.core.ui.share.SharedContactDialog
|
||||
import org.meshtastic.proto.AdminProtos
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import timber.log.Timber
|
||||
import java.net.MalformedURLException
|
||||
|
||||
/**
|
||||
@@ -90,7 +90,7 @@ fun AddContactFAB(
|
||||
try {
|
||||
uri.toSharedContact()
|
||||
} catch (ex: MalformedURLException) {
|
||||
Timber.e("URL was malformed: ${ex.message}")
|
||||
Logger.e { "URL was malformed: ${ex.message}" }
|
||||
null
|
||||
}
|
||||
if (sharedContact != null) {
|
||||
@@ -102,7 +102,7 @@ fun AddContactFAB(
|
||||
sharedContact?.let { SharedContactDialog(sharedContact = it, onDismiss = { onSharedContactRequested(null) }) }
|
||||
|
||||
fun zxingScan() {
|
||||
Timber.d("Starting zxing QR code scanner")
|
||||
Logger.d { "Starting zxing QR code scanner" }
|
||||
val zxingScan = ScanOptions()
|
||||
zxingScan.setCameraId(CAMERA_ID)
|
||||
zxingScan.setPrompt("")
|
||||
@@ -115,9 +115,9 @@ fun AddContactFAB(
|
||||
|
||||
LaunchedEffect(cameraPermissionState.status) {
|
||||
if (cameraPermissionState.status.isGranted) {
|
||||
Timber.d("Camera permission granted")
|
||||
Logger.d { "Camera permission granted" }
|
||||
} else {
|
||||
Timber.d("Camera permission denied")
|
||||
Logger.d { "Camera permission denied" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ val Uri.qrCode: Bitmap?
|
||||
val barcodeEncoder = BarcodeEncoder()
|
||||
barcodeEncoder.createBitmap(bitMatrix)
|
||||
} catch (ex: WriterException) {
|
||||
Timber.e("URL was too complex to render as barcode: ${ex.message}")
|
||||
Logger.e { "URL was too complex to render as barcode: ${ex.message}" }
|
||||
null
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.meshtastic.core.ui.qr
|
||||
import android.os.RemoteException
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.data.repository.RadioConfigRepository
|
||||
@@ -32,7 +33,6 @@ import org.meshtastic.proto.ConfigProtos.Config
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
|
||||
import org.meshtastic.proto.channelSet
|
||||
import org.meshtastic.proto.config
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@@ -61,7 +61,7 @@ constructor(
|
||||
try {
|
||||
serviceRepository.meshService?.setChannel(channel.toByteArray())
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "Set channel error")
|
||||
Logger.e(ex) { "Set channel error" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ constructor(
|
||||
try {
|
||||
serviceRepository.meshService?.setConfig(config.toByteArray())
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "Set config error")
|
||||
Logger.e(ex) { "Set config error" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ dependencies {
|
||||
implementation(libs.androidx.hilt.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
|
||||
implementation(libs.nordic)
|
||||
implementation(libs.nordic.dfu)
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.meshtastic.feature.firmware
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -27,7 +28,6 @@ import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.meshtastic.core.model.DeviceHardware
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
@@ -57,7 +57,7 @@ constructor(
|
||||
}
|
||||
tempDir.mkdirs()
|
||||
}
|
||||
.onFailure { e -> Timber.w(e, "Failed to cleanup temp directory") }
|
||||
.onFailure { e -> Logger.w(e) { "Failed to cleanup temp directory" } }
|
||||
}
|
||||
|
||||
suspend fun checkUrlExists(url: String): Boolean = withContext(Dispatchers.IO) {
|
||||
@@ -65,7 +65,7 @@ constructor(
|
||||
try {
|
||||
client.newCall(request).execute().use { response -> response.isSuccessful }
|
||||
} catch (e: IOException) {
|
||||
Timber.w(e, "Failed to check URL existence: $url")
|
||||
Logger.w(e) { "Failed to check URL existence: $url" }
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -77,12 +77,12 @@ constructor(
|
||||
try {
|
||||
client.newCall(request).execute()
|
||||
} catch (e: IOException) {
|
||||
Timber.w(e, "Download failed for $url")
|
||||
Logger.w(e) { "Download failed for $url" }
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
Timber.w("Download failed: ${response.code} for $url")
|
||||
Logger.w { "Download failed: ${response.code} for $url" }
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ constructor(
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Timber.w(e, "Failed to extract firmware from URI")
|
||||
Logger.w(e) { "Failed to extract firmware from URI" }
|
||||
return@withContext null
|
||||
}
|
||||
matchingEntries.minByOrNull { it.first.name.length }?.second
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CancellationException
|
||||
@@ -72,7 +73,6 @@ import org.meshtastic.core.strings.firmware_update_starting_service
|
||||
import org.meshtastic.core.strings.firmware_update_unknown_hardware
|
||||
import org.meshtastic.core.strings.firmware_update_updating
|
||||
import org.meshtastic.core.strings.unknown
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -169,7 +169,7 @@ constructor(
|
||||
}
|
||||
.onFailure { e ->
|
||||
if (e is CancellationException) throw e
|
||||
Timber.e(e)
|
||||
Logger.e(e) { "Error checking for updates" }
|
||||
_state.value = FirmwareUpdateState.Error(e.message ?: "Unknown error")
|
||||
}
|
||||
}
|
||||
@@ -224,13 +224,13 @@ constructor(
|
||||
|
||||
_state.value = FirmwareUpdateState.Processing(getString(Res.string.firmware_update_flashing))
|
||||
withTimeoutOrNull(DEVICE_DETACH_TIMEOUT) { waitForDeviceDetach(context).first() }
|
||||
?: Timber.w("Timed out waiting for device to detach, assuming success")
|
||||
?: Logger.w { "Timed out waiting for device to detach, assuming success" }
|
||||
|
||||
_state.value = FirmwareUpdateState.Success
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
Logger.e(e) { "Error saving DFU file" }
|
||||
_state.value = FirmwareUpdateState.Error(e.message ?: getString(Res.string.firmware_update_failed))
|
||||
} finally {
|
||||
cleanupTemporaryFiles(fileHandler, tempFirmwareFile)
|
||||
@@ -279,7 +279,7 @@ constructor(
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
Logger.e(e) { "Error starting update from file" }
|
||||
_state.value = FirmwareUpdateState.Error(e.message ?: "Local update failed")
|
||||
}
|
||||
}
|
||||
@@ -345,7 +345,7 @@ private fun cleanupTemporaryFiles(fileHandler: FirmwareFileHandler, tempFirmware
|
||||
tempFirmwareFile?.takeIf { it.exists() }?.delete()
|
||||
fileHandler.cleanupAllTemporaryFiles()
|
||||
}
|
||||
.onFailure { e -> Timber.w(e, "Failed to cleanup temp files") }
|
||||
.onFailure { e -> Logger.w(e) { "Failed to cleanup temp files" } }
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.meshtastic.feature.firmware
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -26,7 +27,6 @@ import no.nordicsemi.android.dfu.DfuServiceInitiator
|
||||
import org.meshtastic.core.database.entity.FirmwareRelease
|
||||
import org.meshtastic.core.model.DeviceHardware
|
||||
import org.meshtastic.core.service.ServiceRepository
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -81,7 +81,7 @@ class FirmwareRetriever @Inject constructor(private val fileHandler: FirmwareFil
|
||||
return it
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.w(e, "Direct download for $filename failed, falling back to release zip")
|
||||
Logger.w(e) { "Direct download for $filename failed, falling back to release zip" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ constructor(
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
Logger.e(e) { "OTA Update failed" }
|
||||
updateState(FirmwareUpdateState.Error(e.message ?: "OTA Update failed"))
|
||||
null
|
||||
}
|
||||
@@ -214,7 +214,7 @@ constructor(
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
Logger.e(e) { "USB Update failed" }
|
||||
updateState(FirmwareUpdateState.Error(e.message ?: "USB Update failed"))
|
||||
null
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>ComposableParamOrder:PermissionScreenLayout.kt$PermissionScreenLayout</ID>
|
||||
<ID>ParameterNaming:WelcomeScreen.kt$onGetStarted</ID>
|
||||
</CurrentIssues>
|
||||
<CurrentIssues/>
|
||||
</SmellBaseline>
|
||||
|
||||
@@ -67,7 +67,7 @@ dependencies {
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.navigation.common)
|
||||
implementation(libs.material)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
|
||||
fdroidImplementation(libs.osmbonuspack)
|
||||
fdroidImplementation(libs.osmdroid.android)
|
||||
|
||||
@@ -76,6 +76,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi // Added for Accompanist
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState // Added for Accompanist
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
@@ -156,7 +157,6 @@ import org.osmdroid.views.overlay.Polygon
|
||||
import org.osmdroid.views.overlay.Polyline
|
||||
import org.osmdroid.views.overlay.infowindow.InfoWindow
|
||||
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.text.DateFormat
|
||||
import kotlin.math.abs
|
||||
@@ -170,7 +170,7 @@ private fun MapView.updateMarkers(
|
||||
waypointMarkers: List<MarkerWithLabel>,
|
||||
nodeClusterer: RadiusMarkerClusterer,
|
||||
) {
|
||||
Timber.d("Showing on map: ${nodeMarkers.size} nodes ${waypointMarkers.size} waypoints")
|
||||
Logger.d { "Showing on map: ${nodeMarkers.size} nodes ${waypointMarkers.size} waypoints" }
|
||||
overlays.removeAll { it is MarkerWithLabel }
|
||||
// overlays.addAll(nodeMarkers + waypointMarkers)
|
||||
overlays.addAll(waypointMarkers)
|
||||
@@ -271,7 +271,7 @@ fun MapView(
|
||||
|
||||
fun loadOnlineTileSourceBase(): ITileSource {
|
||||
val id = mapViewModel.mapStyleId
|
||||
Timber.d("mapStyleId from prefs: $id")
|
||||
Logger.d { "mapStyleId from prefs: $id" }
|
||||
return CustomTileSource.getTileSource(id).also {
|
||||
zoomLevelMax = it.maximumZoomLevel.toDouble()
|
||||
showDownloadButton = if (it is OnlineTileSourceBase) it.tileSourcePolicy.acceptsBulkDownload() else false
|
||||
@@ -295,11 +295,11 @@ fun MapView(
|
||||
|
||||
fun MapView.toggleMyLocation() {
|
||||
if (context.gpsDisabled()) {
|
||||
Timber.d("Telling user we need location turned on for MyLocationNewOverlay")
|
||||
Logger.d { "Telling user we need location turned on for MyLocationNewOverlay" }
|
||||
scope.launch { context.showToast(Res.string.location_disabled) }
|
||||
return
|
||||
}
|
||||
Timber.d("user clicked MyLocationNewOverlay ${myLocationOverlay == null}")
|
||||
Logger.d { "user clicked MyLocationNewOverlay ${myLocationOverlay == null}" }
|
||||
if (myLocationOverlay == null) {
|
||||
myLocationOverlay =
|
||||
MyLocationNewOverlay(this).apply {
|
||||
@@ -454,15 +454,15 @@ fun MapView(
|
||||
val builder = MaterialAlertDialogBuilder(context)
|
||||
builder.setTitle(com.meshtastic.core.strings.getString(Res.string.waypoint_delete))
|
||||
builder.setNeutralButton(com.meshtastic.core.strings.getString(Res.string.cancel)) { _, _ ->
|
||||
Timber.d("User canceled marker delete dialog")
|
||||
Logger.d { "User canceled marker delete dialog" }
|
||||
}
|
||||
builder.setNegativeButton(com.meshtastic.core.strings.getString(Res.string.delete_for_me)) { _, _ ->
|
||||
Timber.d("User deleted waypoint ${waypoint.id} for me")
|
||||
Logger.d { "User deleted waypoint ${waypoint.id} for me" }
|
||||
mapViewModel.deleteWaypoint(waypoint.id)
|
||||
}
|
||||
if (waypoint.lockedTo in setOf(0, mapViewModel.myNodeNum ?: 0) && isConnected) {
|
||||
builder.setPositiveButton(com.meshtastic.core.strings.getString(Res.string.delete_for_everyone)) { _, _ ->
|
||||
Timber.d("User deleted waypoint ${waypoint.id} for everyone")
|
||||
Logger.d { "User deleted waypoint ${waypoint.id} for everyone" }
|
||||
mapViewModel.sendWaypoint(waypoint.copy { expire = 1 })
|
||||
mapViewModel.deleteWaypoint(waypoint.id)
|
||||
}
|
||||
@@ -485,7 +485,7 @@ fun MapView(
|
||||
|
||||
fun showMarkerLongPressDialog(id: Int) {
|
||||
performHapticFeedback()
|
||||
Timber.d("marker long pressed id=$id")
|
||||
Logger.d { "marker long pressed id=$id" }
|
||||
val waypoint = waypoints[id]?.data?.waypoint ?: return
|
||||
// edit only when unlocked or lockedTo myNodeNum
|
||||
if (waypoint.lockedTo in setOf(0, mapViewModel.myNodeNum ?: 0) && isConnected) {
|
||||
@@ -691,9 +691,9 @@ fun MapView(
|
||||
),
|
||||
)
|
||||
} catch (ex: TileSourcePolicyException) {
|
||||
Timber.d("Tile source does not allow archiving: ${ex.message}")
|
||||
Logger.d { "Tile source does not allow archiving: ${ex.message}" }
|
||||
} catch (ex: Exception) {
|
||||
Timber.d("Tile source exception: ${ex.message}")
|
||||
Logger.d { "Tile source exception: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -897,7 +897,7 @@ fun MapView(
|
||||
EditWaypointDialog(
|
||||
waypoint = showEditWaypointDialog ?: return, // Safe call
|
||||
onSendClicked = { waypoint ->
|
||||
Timber.d("User clicked send waypoint ${waypoint.id}")
|
||||
Logger.d { "User clicked send waypoint ${waypoint.id}" }
|
||||
showEditWaypointDialog = null
|
||||
mapViewModel.sendWaypoint(
|
||||
waypoint.copy {
|
||||
@@ -910,12 +910,12 @@ fun MapView(
|
||||
)
|
||||
},
|
||||
onDeleteClicked = { waypoint ->
|
||||
Timber.d("User clicked delete waypoint ${waypoint.id}")
|
||||
Logger.d { "User clicked delete waypoint ${waypoint.id}" }
|
||||
showEditWaypointDialog = null
|
||||
showDeleteMarkerDialog(waypoint)
|
||||
},
|
||||
onDismissRequest = {
|
||||
Timber.d("User clicked cancel marker edit dialog")
|
||||
Logger.d { "User clicked cancel marker edit dialog" }
|
||||
showEditWaypointDialog = null
|
||||
},
|
||||
)
|
||||
|
||||
@@ -33,6 +33,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import co.touchlab.kermit.Logger
|
||||
import org.osmdroid.config.Configuration
|
||||
import org.osmdroid.tileprovider.tilesource.ITileSource
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
@@ -40,7 +41,6 @@ import org.osmdroid.util.BoundingBox
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.CustomZoomButtonsController
|
||||
import org.osmdroid.views.MapView
|
||||
import timber.log.Timber
|
||||
|
||||
@SuppressLint("WakelockTimeout")
|
||||
private fun PowerManager.WakeLock.safeAcquire() {
|
||||
@@ -48,9 +48,9 @@ private fun PowerManager.WakeLock.safeAcquire() {
|
||||
try {
|
||||
acquire()
|
||||
} catch (e: SecurityException) {
|
||||
Timber.e("WakeLock permission exception: ${e.message}")
|
||||
Logger.e { "WakeLock permission exception: ${e.message}" }
|
||||
} catch (e: IllegalStateException) {
|
||||
Timber.e("WakeLock acquire() exception: ${e.message}")
|
||||
Logger.e { "WakeLock acquire() exception: ${e.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ private fun PowerManager.WakeLock.safeRelease() {
|
||||
try {
|
||||
release()
|
||||
} catch (e: IllegalStateException) {
|
||||
Timber.e("WakeLock release() exception: ${e.message}")
|
||||
Logger.e { "WakeLock release() exception: ${e.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.ContextCompat
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.android.gms.common.api.ResolvableApiException
|
||||
import com.google.android.gms.location.LocationRequest
|
||||
import com.google.android.gms.location.LocationServices
|
||||
import com.google.android.gms.location.LocationSettingsRequest
|
||||
import com.google.android.gms.location.Priority
|
||||
import timber.log.Timber
|
||||
|
||||
private const val INTERVAL_MILLIS = 10000L
|
||||
|
||||
@@ -66,11 +66,11 @@ fun LocationPermissionsHandler(onPermissionResult: (Boolean) -> Unit) {
|
||||
val locationSettingsLauncher =
|
||||
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartIntentSenderForResult()) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
Timber.d("Location settings changed by user.")
|
||||
Logger.d { "Location settings changed by user." }
|
||||
// User has enabled location services or improved accuracy.
|
||||
onPermissionResult(true) // Settings are now adequate, and permission was already granted.
|
||||
} else {
|
||||
Timber.d("Location settings change cancelled by user.")
|
||||
Logger.d { "Location settings change cancelled by user." }
|
||||
// User chose not to change settings. The permission itself is still granted,
|
||||
// but the experience might be degraded. For the purpose of enabling map features,
|
||||
// we consider this as success if the core permission is there.
|
||||
@@ -111,7 +111,7 @@ fun LocationPermissionsHandler(onPermissionResult: (Boolean) -> Unit) {
|
||||
val task = client.checkLocationSettings(builder.build())
|
||||
|
||||
task.addOnSuccessListener {
|
||||
Timber.d("Location settings are satisfied.")
|
||||
Logger.d { "Location settings are satisfied." }
|
||||
onPermissionResult(true) // Permission granted and settings are good
|
||||
}
|
||||
|
||||
@@ -122,11 +122,11 @@ fun LocationPermissionsHandler(onPermissionResult: (Boolean) -> Unit) {
|
||||
locationSettingsLauncher.launch(intentSenderRequest)
|
||||
// Result of this launch will be handled by locationSettingsLauncher's callback
|
||||
} catch (sendEx: ActivityNotFoundException) {
|
||||
Timber.d("Error launching location settings resolution ${sendEx.message}.")
|
||||
Logger.d { "Error launching location settings resolution ${sendEx.message}." }
|
||||
onPermissionResult(true) // Permission is granted, but settings dialog failed. Proceed.
|
||||
}
|
||||
} else {
|
||||
Timber.d("Location settings are not satisfiable.${exception.message}")
|
||||
Logger.d { "Location settings are not satisfiable.${exception.message}" }
|
||||
onPermissionResult(true) // Permission is granted, but settings not ideal. Proceed.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.core.graphics.createBitmap
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.android.gms.location.LocationCallback
|
||||
import com.google.android.gms.location.LocationRequest
|
||||
import com.google.android.gms.location.LocationResult
|
||||
@@ -124,7 +125,6 @@ import org.meshtastic.proto.MeshProtos.Position
|
||||
import org.meshtastic.proto.MeshProtos.Waypoint
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.waypoint
|
||||
import timber.log.Timber
|
||||
import java.text.DateFormat
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
@@ -219,7 +219,7 @@ fun MapView(
|
||||
try {
|
||||
cameraPositionState.animate(cameraUpdate)
|
||||
} catch (e: IllegalStateException) {
|
||||
Timber.d("Error animating camera to location: ${e.message}")
|
||||
Logger.d { "Error animating camera to location: ${e.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,14 +239,14 @@ fun MapView(
|
||||
try {
|
||||
@Suppress("MissingPermission")
|
||||
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)
|
||||
Timber.d("Started location tracking")
|
||||
Logger.d { "Started location tracking" }
|
||||
} catch (e: SecurityException) {
|
||||
Timber.d("Location permission not available: ${e.message}")
|
||||
Logger.d { "Location permission not available: ${e.message}" }
|
||||
isLocationTrackingEnabled = false
|
||||
}
|
||||
} else {
|
||||
fusedLocationClient.removeLocationUpdates(locationCallback)
|
||||
Timber.d("Stopped location tracking")
|
||||
Logger.d { "Stopped location tracking" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,7 +412,7 @@ fun MapView(
|
||||
cameraPositionState.animate(cameraUpdate)
|
||||
hasCenteredTraceroute = true
|
||||
} catch (e: IllegalStateException) {
|
||||
Timber.d("Error centering traceroute overlay: ${e.message}")
|
||||
Logger.d { "Error centering traceroute overlay: ${e.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -548,7 +548,7 @@ fun MapView(
|
||||
CameraUpdateFactory.newLatLngBounds(bounds.build(), 100),
|
||||
)
|
||||
}
|
||||
Timber.d("Cluster clicked! $cluster")
|
||||
Logger.d { "Cluster clicked! $cluster" }
|
||||
}
|
||||
true
|
||||
},
|
||||
@@ -679,9 +679,9 @@ fun MapView(
|
||||
val currentPosition = cameraPositionState.position
|
||||
val newCameraPosition = CameraPosition.Builder(currentPosition).bearing(0f).build()
|
||||
cameraPositionState.animate(CameraUpdateFactory.newCameraPosition(newCameraPosition))
|
||||
Timber.d("Oriented map to north")
|
||||
Logger.d { "Oriented map to north" }
|
||||
} catch (e: IllegalStateException) {
|
||||
Timber.d("Error orienting map to north: ${e.message}")
|
||||
Logger.d { "Error orienting map to north: ${e.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -715,7 +715,7 @@ fun MapView(
|
||||
internal fun convertIntToEmoji(unicodeCodePoint: Int): String = try {
|
||||
String(Character.toChars(unicodeCodePoint))
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.w(e, "Invalid unicode code point: $unicodeCodePoint")
|
||||
Logger.w(e) { "Invalid unicode code point: $unicodeCodePoint" }
|
||||
"\uD83D\uDCCD"
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.app.Application
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toFile
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.android.gms.maps.GoogleMap
|
||||
import com.google.android.gms.maps.model.CameraPosition
|
||||
import com.google.android.gms.maps.model.LatLng
|
||||
@@ -56,7 +57,6 @@ import org.meshtastic.core.prefs.map.MapPrefs
|
||||
import org.meshtastic.core.service.ServiceRepository
|
||||
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
|
||||
import org.meshtastic.proto.ConfigProtos
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
@@ -200,7 +200,7 @@ constructor(
|
||||
fun selectCustomTileProvider(config: CustomTileProviderConfig?) {
|
||||
if (config != null) {
|
||||
if (!isValidTileUrlTemplate(config.urlTemplate)) {
|
||||
Timber.tag("MapViewModel").w("Attempted to select invalid URL template: ${config.urlTemplate}")
|
||||
Logger.withTag("MapViewModel").w("Attempted to select invalid URL template: ${config.urlTemplate}")
|
||||
_selectedCustomTileProviderUrl.value = null
|
||||
googleMapsPrefs.selectedCustomTileUrl = null
|
||||
return
|
||||
@@ -224,7 +224,8 @@ constructor(
|
||||
|
||||
fun createUrlTileProvider(urlString: String): TileProvider? {
|
||||
if (!isValidTileUrlTemplate(urlString)) {
|
||||
Timber.tag("MapViewModel").e("Tile URL does not contain valid {x}, {y}, and {z} placeholders: $urlString")
|
||||
Logger.withTag("MapViewModel")
|
||||
.e("Tile URL does not contain valid {x}, {y}, and {z} placeholders: $urlString")
|
||||
return null
|
||||
}
|
||||
return object : UrlTileProvider(TILE_SIZE, TILE_SIZE) {
|
||||
@@ -237,7 +238,7 @@ constructor(
|
||||
return try {
|
||||
URL(formattedUrl)
|
||||
} catch (e: MalformedURLException) {
|
||||
Timber.tag("MapViewModel").e(e, "Malformed URL: $formattedUrl")
|
||||
Logger.withTag("MapViewModel").e(e) { "Malformed URL: $formattedUrl" }
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -290,7 +291,7 @@ constructor(
|
||||
try {
|
||||
_selectedGoogleMapType.value = MapType.valueOf(savedGoogleMapTypeName ?: MapType.NORMAL.name)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.e(e, "Invalid saved Google Map type: $savedGoogleMapTypeName")
|
||||
Logger.e(e) { "Invalid saved Google Map type: $savedGoogleMapTypeName" }
|
||||
_selectedGoogleMapType.value = MapType.NORMAL // Fallback in case of invalid stored name
|
||||
googleMapsPrefs.selectedGoogleMapType = null
|
||||
}
|
||||
@@ -335,14 +336,14 @@ constructor(
|
||||
}
|
||||
_mapLayers.value = loadedItems
|
||||
if (loadedItems.isNotEmpty()) {
|
||||
Timber.tag("MapViewModel").i("Loaded ${loadedItems.size} persisted map layers.")
|
||||
Logger.withTag("MapViewModel").i("Loaded ${loadedItems.size} persisted map layers.")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Timber.tag("MapViewModel").i("Map layers directory does not exist. No layers loaded.")
|
||||
Logger.withTag("MapViewModel").i("Map layers directory does not exist. No layers loaded.")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.tag("MapViewModel").e(e, "Error loading persisted map layers")
|
||||
Logger.withTag("MapViewModel").e(e) { "Error loading persisted map layers" }
|
||||
_mapLayers.value = emptyList()
|
||||
}
|
||||
}
|
||||
@@ -367,7 +368,7 @@ constructor(
|
||||
}
|
||||
|
||||
if (layerType == null) {
|
||||
Timber.tag("MapViewModel").e("Unsupported map layer file type: $extension")
|
||||
Logger.withTag("MapViewModel").e("Unsupported map layer file type: $extension")
|
||||
return@launch
|
||||
}
|
||||
|
||||
@@ -384,7 +385,7 @@ constructor(
|
||||
val newItem = MapLayerItem(name = layerName, uri = localFileUri, layerType = layerType)
|
||||
_mapLayers.value = _mapLayers.value + newItem
|
||||
} else {
|
||||
Timber.tag("MapViewModel").e("Failed to copy file to internal storage.")
|
||||
Logger.withTag("MapViewModel").e("Failed to copy file to internal storage.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,7 +403,7 @@ constructor(
|
||||
inputStream?.use { input -> outputStream.use { output -> input.copyTo(output) } }
|
||||
Uri.fromFile(outputFile)
|
||||
} catch (e: IOException) {
|
||||
Timber.tag("MapViewModel").e(e, "Error copying file to internal storage")
|
||||
Logger.withTag("MapViewModel").e(e) { "Error copying file to internal storage" }
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -453,7 +454,7 @@ constructor(
|
||||
file.delete()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.tag("MapViewModel").e(e, "Error deleting file from internal storage")
|
||||
Logger.withTag("MapViewModel").e(e) { "Error deleting file from internal storage" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -465,7 +466,7 @@ constructor(
|
||||
try {
|
||||
application.contentResolver.openInputStream(uriToLoad)
|
||||
} catch (_: Exception) {
|
||||
Timber.d("MapViewModel: Error opening InputStream from URI: $uriToLoad")
|
||||
Logger.d { "MapViewModel: Error opening InputStream from URI: $uriToLoad" }
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -480,7 +481,7 @@ constructor(
|
||||
LayerType.GEOJSON -> loadGeoJsonLayerIfNeeded(layerItem, map)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.tag("MapViewModel").e(e, "Error loading map layer for ${layerItem.uri}")
|
||||
Logger.withTag("MapViewModel").e(e) { "Error loading map layer for ${layerItem.uri}" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.meshtastic.feature.map
|
||||
import android.os.RemoteException
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -44,7 +45,6 @@ import org.meshtastic.core.strings.two_days
|
||||
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
|
||||
import org.meshtastic.feature.map.model.TracerouteOverlay
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@@ -150,7 +150,7 @@ abstract class BaseMapViewModel(
|
||||
return try {
|
||||
serviceRepository.meshService?.packetId
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("RemoteException: ${ex.message}")
|
||||
Logger.e { "RemoteException: ${ex.message}" }
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -170,7 +170,7 @@ abstract class BaseMapViewModel(
|
||||
try {
|
||||
serviceRepository.meshService?.send(p)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("Send DataPacket error: ${ex.message}")
|
||||
Logger.e { "Send DataPacket error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ dependencies {
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.hilt.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.paging.compose)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
|
||||
|
||||
@@ -1,26 +1,5 @@
|
||||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>ComposableParamOrder:Message.kt$MessageScreen</ID>
|
||||
<ID>ComposableParamOrder:Message.kt$QuickChatRow</ID>
|
||||
<ID>ComposableParamOrder:MessageActions.kt$MessageActions</ID>
|
||||
<ID>ComposableParamOrder:MessageActions.kt$MessageStatusButton</ID>
|
||||
<ID>ComposableParamOrder:MessageItem.kt$MessageItem</ID>
|
||||
<ID>ComposableParamOrder:MessageList.kt$DeliveryInfo</ID>
|
||||
<ID>ComposableParamOrder:MessageList.kt$MessageList</ID>
|
||||
<ID>ComposableParamOrder:QuickChat.kt$OutlinedTextFieldWithCounter</ID>
|
||||
<ID>LambdaParameterEventTrailing:Message.kt$onClick</ID>
|
||||
<ID>LambdaParameterEventTrailing:Message.kt$onSendMessage</ID>
|
||||
<ID>LambdaParameterEventTrailing:MessageList.kt$onReply</ID>
|
||||
<ID>LambdaParameterEventTrailing:QuickChat.kt$onNavigateUp</ID>
|
||||
<ID>LambdaParameterInRestartableEffect:MessageList.kt$onUnreadChanged</ID>
|
||||
<ID>LongParameterList:MessageViewModel.kt$MessageViewModel$( private val nodeRepository: NodeRepository, radioConfigRepository: RadioConfigRepository, quickChatActionRepository: QuickChatActionRepository, private val serviceRepository: ServiceRepository, private val packetRepository: PacketRepository, private val uiPrefs: UiPrefs, private val meshServiceNotifications: MeshServiceNotifications, )</ID>
|
||||
<ID>ModifierMissing:Message.kt$MessageScreen</ID>
|
||||
<ID>ModifierNotUsedAtRoot:QuickChat.kt$modifier = modifier.fillMaxSize().padding(innerPadding)</ID>
|
||||
<ID>MutableStateParam:MessageList.kt$selectedIds</ID>
|
||||
<ID>ParameterNaming:MessageList.kt$onUnreadChanged</ID>
|
||||
<ID>TooManyFunctions:MessageViewModel.kt$MessageViewModel : ViewModel</ID>
|
||||
<ID>Wrapping:Message.kt${ event -> when (event) { is MessageScreenEvent.SendMessage -> { viewModel.sendMessage(event.text, contactKey, event.replyingToPacketId) if (event.replyingToPacketId != null) replyingToPacketId = null messageInputState.clearText() } is MessageScreenEvent.SendReaction -> viewModel.sendReaction(event.emoji, event.messageId, contactKey) is MessageScreenEvent.DeleteMessages -> { viewModel.deleteMessages(event.ids) selectedMessageIds.value = emptySet() showDeleteDialog = false } is MessageScreenEvent.ClearUnreadCount -> viewModel.clearUnreadCount(contactKey, event.lastReadMessageId) is MessageScreenEvent.NodeDetails -> navigateToNodeDetails(event.node.num) is MessageScreenEvent.SetTitle -> viewModel.setTitle(event.title) is MessageScreenEvent.NavigateToMessages -> navigateToMessages(event.contactKey) is MessageScreenEvent.NavigateToNodeDetails -> navigateToNodeDetails(event.nodeNum) MessageScreenEvent.NavigateBack -> onNavigateBack() is MessageScreenEvent.CopyToClipboard -> { clipboardManager.nativeClipboard.setPrimaryClip(ClipData.newPlainText(event.text, event.text)) selectedMessageIds.value = emptySet() } } }</ID>
|
||||
</CurrentIssues>
|
||||
<CurrentIssues/>
|
||||
</SmellBaseline>
|
||||
|
||||
@@ -23,6 +23,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -50,7 +51,6 @@ import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
|
||||
import org.meshtastic.proto.ConfigProtos.Config.DeviceConfig.Role
|
||||
import org.meshtastic.proto.channelSet
|
||||
import org.meshtastic.proto.sharedContact
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val VERIFIED_CONTACT_FIRMWARE_CUTOFF = "2.7.12"
|
||||
@@ -198,7 +198,7 @@ constructor(
|
||||
try {
|
||||
serviceRepository.onServiceAction(ServiceAction.Favorite(node))
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "Favorite node error")
|
||||
Logger.e(ex) { "Favorite node error" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ constructor(
|
||||
}
|
||||
serviceRepository.onServiceAction(ServiceAction.SendContact(contact = contact))
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "Send shared contact error")
|
||||
Logger.e(ex) { "Send shared contact error" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ constructor(
|
||||
try {
|
||||
serviceRepository.meshService?.send(p)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("Send DataPacket error: ${ex.message}")
|
||||
Logger.e { "Send DataPacket error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ dependencies {
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.hilt.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.navigation.common)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
implementation(libs.coil)
|
||||
implementation(libs.markdown.renderer.android)
|
||||
implementation(libs.markdown.renderer.m3)
|
||||
|
||||
@@ -3,86 +3,9 @@
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>CommentWrapping:SignalMetrics.kt$Metric.SNR$/* Selected 12 as the max to get 4 equal vertical sections. */</ID>
|
||||
<ID>ComposableParamOrder:DeviceMetrics.kt$DeviceMetricsChart</ID>
|
||||
<ID>ComposableParamOrder:ElevationInfo.kt$ElevationInfo</ID>
|
||||
<ID>ComposableParamOrder:EnvironmentCharts.kt$ChartContent</ID>
|
||||
<ID>ComposableParamOrder:EnvironmentCharts.kt$EnvironmentMetricsChart</ID>
|
||||
<ID>ComposableParamOrder:EnvironmentCharts.kt$MetricPlottingCanvas</ID>
|
||||
<ID>ComposableParamOrder:HostMetricsLog.kt$HostMetricsItem</ID>
|
||||
<ID>ComposableParamOrder:HostMetricsLog.kt$LogLine</ID>
|
||||
<ID>ComposableParamOrder:LastHeardInfo.kt$LastHeardInfo</ID>
|
||||
<ID>ComposableParamOrder:NodeFilterTextField.kt$NodeFilterTextField</ID>
|
||||
<ID>ComposableParamOrder:NodeItem.kt$NodeItem</ID>
|
||||
<ID>ComposableParamOrder:PaxMetrics.kt$PaxMetricsChart</ID>
|
||||
<ID>ComposableParamOrder:PowerMetrics.kt$PowerMetricsChart</ID>
|
||||
<ID>ComposableParamOrder:SatelliteCountInfo.kt$SatelliteCountInfo</ID>
|
||||
<ID>ComposableParamOrder:SignalMetrics.kt$SignalMetricsChart</ID>
|
||||
<ID>ComposableParamOrder:TracerouteButton.kt$TracerouteButton</ID>
|
||||
<ID>LambdaParameterEventTrailing:TracerouteLog.kt$onNavigateUp</ID>
|
||||
<ID>LongMethod:EnvironmentMetrics.kt$@Composable fun EnvironmentMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigateUp: () -> Unit)</ID>
|
||||
<ID>LongMethod:NodeDetailsSection.kt$@Composable private fun MainNodeDetails(node: Node)</ID>
|
||||
<ID>MagicNumber:MetricsViewModel.kt$MetricsViewModel$1000L</ID>
|
||||
<ID>MagicNumber:MetricsViewModel.kt$MetricsViewModel$1e-5</ID>
|
||||
<ID>MagicNumber:MetricsViewModel.kt$MetricsViewModel$1e-7</ID>
|
||||
<ID>ModifierMissing:CommonCharts.kt$ChartHeader</ID>
|
||||
<ID>ModifierMissing:CommonCharts.kt$Legend</ID>
|
||||
<ID>ModifierMissing:CommonCharts.kt$TimeLabels</ID>
|
||||
<ID>ModifierMissing:DeviceMetrics.kt$DeviceMetricsScreen</ID>
|
||||
<ID>ModifierMissing:EnvironmentMetrics.kt$EnvironmentMetricsScreen</ID>
|
||||
<ID>ModifierMissing:HostMetricsLog.kt$HostMetricsLogScreen</ID>
|
||||
<ID>ModifierMissing:NodeListScreen.kt$NodeListScreen</ID>
|
||||
<ID>ModifierMissing:NodeStatusIcons.kt$NodeStatusIcons</ID>
|
||||
<ID>ModifierMissing:PaxMetrics.kt$PaxMetricsItem</ID>
|
||||
<ID>ModifierMissing:PaxMetrics.kt$PaxMetricsScreen</ID>
|
||||
<ID>ModifierMissing:PositionLog.kt$PositionItem</ID>
|
||||
<ID>ModifierMissing:PositionLog.kt$PositionLogScreen</ID>
|
||||
<ID>ModifierMissing:PowerMetrics.kt$PowerMetricsScreen</ID>
|
||||
<ID>ModifierMissing:SignalMetrics.kt$SignalMetricsScreen</ID>
|
||||
<ID>ModifierNotUsedAtRoot:DeviceMetrics.kt$modifier = modifier.weight(weight = Y_AXIS_WEIGHT)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:DeviceMetrics.kt$modifier = modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:DeviceMetrics.kt$modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:EnvironmentCharts.kt$modifier = modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:EnvironmentCharts.kt$modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:PaxMetrics.kt$modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:PowerMetrics.kt$modifier = modifier.weight(weight = Y_AXIS_WEIGHT)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:PowerMetrics.kt$modifier = modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:PowerMetrics.kt$modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:SignalMetrics.kt$modifier = modifier.weight(weight = Y_AXIS_WEIGHT)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:SignalMetrics.kt$modifier = modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:SignalMetrics.kt$modifier.width(dp)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:TracerouteLog.kt$modifier = modifier.fillMaxSize().padding(innerPadding)</ID>
|
||||
<ID>ModifierReused:DeviceMetrics.kt$Canvas(modifier = modifier.width(dp)) { val height = size.height val width = size.width for (i in telemetries.indices) { val telemetry = telemetries[i] /* x-value time */ val xRatio = (telemetry.time - oldest.time).toFloat() / timeDiff val x = xRatio * width /* Channel Utilization */ plotPoint( drawContext = drawContext, color = Device.CH_UTIL.color, x = x, value = telemetry.deviceMetrics.channelUtilization, divisor = MAX_PERCENT_VALUE, ) /* Air Utilization Transmit */ plotPoint( drawContext = drawContext, color = Device.AIR_UTIL.color, x = x, value = telemetry.deviceMetrics.airUtilTx, divisor = MAX_PERCENT_VALUE, ) } /* Battery Line */ var index = 0 while (index < telemetries.size) { val path = Path() index = createPath( telemetries = telemetries, index = index, path = path, oldestTime = oldest.time, timeRange = timeDiff, width = width, timeThreshold = selectedTime.timeThreshold(), ) { i -> val telemetry = telemetries.getOrNull(i) ?: telemetries.last() val ratio = telemetry.deviceMetrics.batteryLevel / MAX_PERCENT_VALUE val y = height - (ratio * height) return@createPath y } drawPath( path = path, color = Device.BATTERY.color, style = Stroke(width = GraphUtil.RADIUS, cap = StrokeCap.Round), ) } }</ID>
|
||||
<ID>ModifierReused:DeviceMetrics.kt$HorizontalLinesOverlay( modifier.width(dp), lineColors = listOf(graphColor, Color.Yellow, Color.Red, graphColor, graphColor), )</ID>
|
||||
<ID>ModifierReused:DeviceMetrics.kt$TimeAxisOverlay(modifier.width(dp), oldest = oldest.time, newest = newest.time, selectedTime.lineInterval())</ID>
|
||||
<ID>ModifierReused:EnvironmentCharts.kt$Box( contentAlignment = Alignment.TopStart, modifier = modifier.horizontalScroll(state = scrollState, reverseScrolling = true), ) { HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor }) TimeAxisOverlay(modifier = modifier.width(dp), oldest = oldest, newest = newest, selectedTime.lineInterval()) MetricPlottingCanvas( modifier = modifier.width(dp), telemetries = telemetries, graphData = graphData, selectedTime = selectedTime, oldest = oldest, timeDiff = timeDiff, rightMin = rightMin, rightMax = rightMax, ) }</ID>
|
||||
<ID>ModifierReused:EnvironmentCharts.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor })</ID>
|
||||
<ID>ModifierReused:EnvironmentCharts.kt$MetricPlottingCanvas( modifier = modifier.width(dp), telemetries = telemetries, graphData = graphData, selectedTime = selectedTime, oldest = oldest, timeDiff = timeDiff, rightMin = rightMin, rightMax = rightMax, )</ID>
|
||||
<ID>ModifierReused:EnvironmentCharts.kt$TimeAxisOverlay(modifier = modifier.width(dp), oldest = oldest, newest = newest, selectedTime.lineInterval())</ID>
|
||||
<ID>ModifierReused:PaxMetrics.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { Color.LightGray })</ID>
|
||||
<ID>ModifierReused:PaxMetrics.kt$Row(modifier = modifier.fillMaxWidth().fillMaxHeight(fraction = 0.33f)) { YAxisLabels( modifier = Modifier.weight(Y_AXIS_WEIGHT).fillMaxHeight().padding(start = 8.dp), labelColor = MaterialTheme.colorScheme.onSurface, minValue = minValue, maxValue = maxValue, ) Box( contentAlignment = Alignment.TopStart, modifier = Modifier.horizontalScroll(state = scrollState, reverseScrolling = true).weight(CHART_WEIGHT), ) { HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { Color.LightGray }) TimeAxisOverlay(modifier.width(dp), oldest = minTime, newest = maxTime, timeFrame.lineInterval()) Canvas(modifier = Modifier.width(dp).fillMaxHeight()) { val width = size.width val height = size.height fun xForTime(t: Int): Float = if (maxTime == minTime) width / 2 else (t - minTime).toFloat() / (maxTime - minTime) * width fun yForValue(v: Int): Float = height - (v - minValue) / (maxValue - minValue) * height fun drawLine(series: List<Pair<Int, Int>>, color: Color) { for (i in 1 until series.size) { drawLine( color = color, start = Offset(xForTime(series[i - 1].first), yForValue(series[i - 1].second)), end = Offset(xForTime(series[i].first), yForValue(series[i].second)), strokeWidth = 2.dp.toPx(), ) } } drawLine(bleSeries, PaxSeries.BLE.color) drawLine(wifiSeries, PaxSeries.WIFI.color) drawLine(totalSeries, PaxSeries.PAX.color) } } YAxisLabels( modifier = Modifier.weight(Y_AXIS_WEIGHT).fillMaxHeight().padding(end = 8.dp), labelColor = MaterialTheme.colorScheme.onSurface, minValue = minValue, maxValue = maxValue, ) }</ID>
|
||||
<ID>ModifierReused:PaxMetrics.kt$TimeAxisOverlay(modifier.width(dp), oldest = minTime, newest = maxTime, timeFrame.lineInterval())</ID>
|
||||
<ID>ModifierReused:PowerMetrics.kt$Canvas(modifier = modifier.width(dp)) { val width = size.width val height = size.height /* Voltage */ var index = 0 while (index < telemetries.size) { val path = Path() index = createPath( telemetries = telemetries, index = index, path = path, oldestTime = oldest.time, timeRange = timeDiff, width = width, timeThreshold = selectedTime.timeThreshold(), ) { i -> val telemetry = telemetries.getOrNull(i) ?: telemetries.last() val ratio = (retrieveVoltage(selectedChannel, telemetry) - voltageMin) / voltageDiff val y = height - (ratio * height) return@createPath y } drawPath( path = path, color = VOLTAGE_COLOR, style = Stroke(width = GraphUtil.RADIUS, cap = StrokeCap.Round), ) } /* Current */ index = 0 while (index < telemetries.size) { val path = Path() index = createPath( telemetries = telemetries, index = index, path = path, oldestTime = oldest.time, timeRange = timeDiff, width = width, timeThreshold = selectedTime.timeThreshold(), ) { i -> val telemetry = telemetries.getOrNull(i) ?: telemetries.last() val ratio = (retrieveCurrent(selectedChannel, telemetry) - Power.CURRENT.min) / currentDiff val y = height - (ratio * height) return@createPath y } drawPath( path = path, color = Power.CURRENT.color, style = Stroke(width = GraphUtil.RADIUS, cap = StrokeCap.Round), ) } }</ID>
|
||||
<ID>ModifierReused:PowerMetrics.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor })</ID>
|
||||
<ID>ModifierReused:PowerMetrics.kt$TimeAxisOverlay(modifier.width(dp), oldest = oldest.time, newest = newest.time, selectedTime.lineInterval())</ID>
|
||||
<ID>ModifierReused:PowerMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Power.CURRENT.color, minValue = Power.CURRENT.min, maxValue = Power.CURRENT.max, )</ID>
|
||||
<ID>ModifierReused:PowerMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), VOLTAGE_COLOR, minValue = voltageMin, maxValue = voltageMax, )</ID>
|
||||
<ID>ModifierReused:SignalMetrics.kt$Canvas(modifier = modifier.width(dp)) { val width = size.width /* Plot */ for (packet in meshPackets) { val xRatio = (packet.rxTime - oldest.rxTime).toFloat() / timeDiff val x = xRatio * width /* SNR */ plotPoint( drawContext = drawContext, color = Metric.SNR.color, x = x, value = packet.rxSnr - Metric.SNR.min, divisor = snrDiff, ) /* RSSI */ plotPoint( drawContext = drawContext, color = Metric.RSSI.color, x = x, value = packet.rxRssi - Metric.RSSI.min, divisor = rssiDiff, ) } }</ID>
|
||||
<ID>ModifierReused:SignalMetrics.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor })</ID>
|
||||
<ID>ModifierReused:SignalMetrics.kt$TimeAxisOverlay( modifier.width(dp), oldest = oldest.rxTime, newest = newest.rxTime, selectedTime.lineInterval(), )</ID>
|
||||
<ID>ModifierReused:SignalMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Metric.RSSI.color, minValue = Metric.RSSI.min, maxValue = Metric.RSSI.max, )</ID>
|
||||
<ID>ModifierReused:SignalMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Metric.SNR.color, minValue = Metric.SNR.min, maxValue = Metric.SNR.max, )</ID>
|
||||
<ID>ModifierWithoutDefault:CommonCharts.kt$modifier</ID>
|
||||
<ID>ModifierWithoutDefault:EnvironmentCharts.kt$modifier</ID>
|
||||
<ID>MultipleEmitters:CommonCharts.kt$LegendLabel</ID>
|
||||
<ID>MultipleEmitters:DeviceMetrics.kt$DeviceMetricsChart</ID>
|
||||
<ID>MultipleEmitters:EnvironmentCharts.kt$EnvironmentMetricsChart</ID>
|
||||
<ID>MultipleEmitters:NodeDetailsSection.kt$MainNodeDetails</ID>
|
||||
<ID>MultipleEmitters:PaxMetrics.kt$PaxMetricsChart</ID>
|
||||
<ID>MultipleEmitters:PowerMetrics.kt$PowerMetricsChart</ID>
|
||||
<ID>MultipleEmitters:RemoteDeviceActions.kt$RemoteDeviceActions</ID>
|
||||
<ID>MultipleEmitters:SignalMetrics.kt$SignalMetricsChart</ID>
|
||||
<ID>ParameterNaming:NodeFilterTextField.kt$onToggleShowIgnored</ID>
|
||||
<ID>PreviewPublic:NodeItem.kt$NodeInfoPreview</ID>
|
||||
<ID>PreviewPublic:NodeItem.kt$NodeInfoSimplePreview</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
||||
@@ -41,6 +41,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.mikepenz.markdown.m3.Markdown
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
@@ -50,7 +51,6 @@ import org.meshtastic.core.strings.download
|
||||
import org.meshtastic.core.strings.error_no_app_to_handle_link
|
||||
import org.meshtastic.core.strings.view_release
|
||||
import org.meshtastic.core.ui.util.showToast
|
||||
import timber.log.Timber
|
||||
|
||||
@Composable
|
||||
fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modifier = Modifier) {
|
||||
@@ -72,7 +72,7 @@ fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modi
|
||||
context.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
scope.launch { context.showToast(Res.string.error_no_app_to_handle_link) }
|
||||
Timber.e(e)
|
||||
Logger.e(e) { "Failed to handle release page URL" }
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
@@ -88,7 +88,7 @@ fun FirmwareReleaseSheetContent(firmwareRelease: FirmwareRelease, modifier: Modi
|
||||
context.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
scope.launch { context.showToast(Res.string.error_no_app_to_handle_link) }
|
||||
Timber.e(e)
|
||||
Logger.e(e) { "Failed to handle release zip URL" }
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.core.net.toUri
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.database.model.Node
|
||||
@@ -47,7 +48,6 @@ import org.meshtastic.core.ui.component.icon
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.core.ui.util.showToast
|
||||
import org.meshtastic.proto.ConfigProtos.Config.DisplayConfig.DisplayUnits
|
||||
import timber.log.Timber
|
||||
import java.net.URLEncoder
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@@ -82,7 +82,7 @@ fun LinkedCoordinatesItem(node: Node, displayUnits: DisplayUnits = DisplayUnits.
|
||||
coroutineScope.launch { context.showToast("No application available to open this location!") }
|
||||
}
|
||||
} catch (ex: ActivityNotFoundException) {
|
||||
Timber.d("Failed to open geo intent: $ex")
|
||||
Logger.d { "Failed to open geo intent: $ex" }
|
||||
}
|
||||
},
|
||||
onLongClick = {
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.meshtastic.feature.node.detail
|
||||
import android.os.RemoteException
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -33,7 +34,6 @@ import org.meshtastic.core.model.TelemetryType
|
||||
import org.meshtastic.core.service.ServiceAction
|
||||
import org.meshtastic.core.service.ServiceRepository
|
||||
import org.meshtastic.feature.node.component.NodeMenuAction
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@@ -77,20 +77,20 @@ constructor(
|
||||
try {
|
||||
nodeRepository.setNodeNotes(nodeNum, notes)
|
||||
} catch (ex: java.io.IOException) {
|
||||
Timber.e("Set node notes IO error: ${ex.message}")
|
||||
Logger.e { "Set node notes IO error: ${ex.message}" }
|
||||
} catch (ex: java.sql.SQLException) {
|
||||
Timber.e("Set node notes SQL error: ${ex.message}")
|
||||
Logger.e { "Set node notes SQL error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeNode(nodeNum: Int) = viewModelScope.launch(Dispatchers.IO) {
|
||||
Timber.i("Removing node '$nodeNum'")
|
||||
Logger.i { "Removing node '$nodeNum'" }
|
||||
try {
|
||||
val packetId = serviceRepository.meshService?.packetId ?: return@launch
|
||||
serviceRepository.meshService?.removeByNodenum(packetId, nodeNum)
|
||||
nodeRepository.deleteNode(nodeNum)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("Remove node error: ${ex.message}")
|
||||
Logger.e { "Remove node error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ constructor(
|
||||
try {
|
||||
serviceRepository.onServiceAction(ServiceAction.Ignore(node))
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "Ignore node error")
|
||||
Logger.e(ex) { "Ignore node error" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,55 +106,55 @@ constructor(
|
||||
try {
|
||||
serviceRepository.onServiceAction(ServiceAction.Favorite(node))
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "Favorite node error")
|
||||
Logger.e(ex) { "Favorite node error" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestUserInfo(destNum: Int) {
|
||||
Timber.i("Requesting UserInfo for '$destNum'")
|
||||
Logger.i { "Requesting UserInfo for '$destNum'" }
|
||||
try {
|
||||
serviceRepository.meshService?.requestUserInfo(destNum)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("Request NodeInfo error: ${ex.message}")
|
||||
Logger.e { "Request NodeInfo error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestNeighborInfo(destNum: Int) {
|
||||
Timber.i("Requesting NeighborInfo for '$destNum'")
|
||||
Logger.i { "Requesting NeighborInfo for '$destNum'" }
|
||||
try {
|
||||
val packetId = serviceRepository.meshService?.packetId ?: return
|
||||
serviceRepository.meshService?.requestNeighborInfo(packetId, destNum)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("Request NeighborInfo error: ${ex.message}")
|
||||
Logger.e { "Request NeighborInfo error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestPosition(destNum: Int, position: Position = Position(0.0, 0.0, 0)) {
|
||||
Timber.i("Requesting position for '$destNum'")
|
||||
Logger.i { "Requesting position for '$destNum'" }
|
||||
try {
|
||||
serviceRepository.meshService?.requestPosition(destNum, position)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("Request position error: ${ex.message}")
|
||||
Logger.e { "Request position error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestTelemetry(destNum: Int, type: TelemetryType) {
|
||||
Timber.i("Requesting telemetry for '$destNum'")
|
||||
Logger.i { "Requesting telemetry for '$destNum'" }
|
||||
try {
|
||||
val packetId = serviceRepository.meshService?.packetId ?: return
|
||||
serviceRepository.meshService?.requestTelemetry(packetId, destNum, type.ordinal)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("Request telemetry error: ${ex.message}")
|
||||
Logger.e { "Request telemetry error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestTraceroute(destNum: Int) {
|
||||
Timber.i("Requesting traceroute for '$destNum'")
|
||||
Logger.i { "Requesting traceroute for '$destNum'" }
|
||||
try {
|
||||
val packetId = serviceRepository.meshService?.packetId ?: return
|
||||
serviceRepository.meshService?.requestTraceroute(packetId, destNum)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("Request traceroute error: ${ex.message}")
|
||||
Logger.e { "Request traceroute error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
package org.meshtastic.feature.node.list
|
||||
|
||||
import android.os.RemoteException
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.meshtastic.core.data.repository.NodeRepository
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.service.ServiceAction
|
||||
import org.meshtastic.core.service.ServiceRepository
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class NodeActions
|
||||
@@ -37,7 +37,7 @@ constructor(
|
||||
try {
|
||||
serviceRepository.onServiceAction(ServiceAction.Favorite(node))
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "Favorite node error")
|
||||
Logger.e(ex) { "Favorite node error" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,18 +45,18 @@ constructor(
|
||||
try {
|
||||
serviceRepository.onServiceAction(ServiceAction.Ignore(node))
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e(ex, "Ignore node error")
|
||||
Logger.e(ex) { "Ignore node error" }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun removeNode(nodeNum: Int) = withContext(Dispatchers.IO) {
|
||||
Timber.i("Removing node '$nodeNum'")
|
||||
Logger.i { "Removing node '$nodeNum'" }
|
||||
try {
|
||||
val packetId = serviceRepository.meshService?.packetId ?: return@withContext
|
||||
serviceRepository.meshService?.removeByNodenum(packetId, nodeNum)
|
||||
nodeRepository.deleteNode(nodeNum)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("Remove node error: ${ex.message}")
|
||||
Logger.e { "Remove node error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
package org.meshtastic.feature.node.metrics
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Safely extracts the hardware model number from a HardwareModel enum.
|
||||
@@ -34,6 +34,6 @@ import timber.log.Timber
|
||||
fun MeshProtos.HardwareModel.safeNumber(fallbackValue: Int = -1): Int = try {
|
||||
this.number
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.w("Unknown hardware model enum value: $this, using fallback value: $fallbackValue")
|
||||
Logger.w { "Unknown hardware model enum value: $this, using fallback value: $fallbackValue" }
|
||||
fallbackValue
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.toRoute
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -63,7 +64,6 @@ import org.meshtastic.proto.MeshProtos
|
||||
import org.meshtastic.proto.MeshProtos.MeshPacket
|
||||
import org.meshtastic.proto.Portnums
|
||||
import org.meshtastic.proto.Portnums.PortNum
|
||||
import timber.log.Timber
|
||||
import java.io.BufferedWriter
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.FileWriter
|
||||
@@ -343,16 +343,16 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
Timber.d("MetricsViewModel created")
|
||||
Logger.d { "MetricsViewModel created" }
|
||||
} else {
|
||||
Timber.d("MetricsViewModel: destNum is null, skipping metrics flows initialization.")
|
||||
Logger.d { "MetricsViewModel: destNum is null, skipping metrics flows initialization." }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
Timber.d("MetricsViewModel cleared")
|
||||
Logger.d { "MetricsViewModel cleared" }
|
||||
}
|
||||
|
||||
fun setTimeFrame(timeFrame: TimeFrame) {
|
||||
@@ -395,7 +395,7 @@ constructor(
|
||||
}
|
||||
}
|
||||
} catch (ex: FileNotFoundException) {
|
||||
Timber.e(ex, "Can't write file error")
|
||||
Logger.e(ex) { "Can't write file error" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ dependencies {
|
||||
implementation(libs.androidx.hilt.lifecycle.viewmodel.compose)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
implementation(libs.timber)
|
||||
implementation(libs.kermit)
|
||||
implementation(libs.zxing.android.embedded)
|
||||
|
||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||
|
||||
@@ -2,28 +2,18 @@
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>ComposableParamOrder:ChannelConfigScreen.kt$ChannelConfigScreen</ID>
|
||||
<ID>ComposableParamOrder:Debug.kt$DecodedPayloadBlock</ID>
|
||||
<ID>ComposableParamOrder:DebugSearch.kt$DebugSearchState</ID>
|
||||
<ID>ComposableParamOrder:DebugSearch.kt$DebugSearchStateviewModelDefaults</ID>
|
||||
<ID>ComposableParamOrder:MapReportingPreference.kt$MapReportingPreference</ID>
|
||||
<ID>ComposableParamOrder:NodeActionButton.kt$NodeActionButton</ID>
|
||||
<ID>ComposableParamOrder:WarningDialog.kt$WarningDialog</ID>
|
||||
<ID>CyclomaticComplexMethod:NetworkConfigItemList.kt$@Composable fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>CyclomaticComplexMethod:PositionConfigItemList.kt$@OptIn(ExperimentalPermissionsApi::class) @Composable fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>CyclomaticComplexMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)</ID>
|
||||
<ID>LambdaParameterEventTrailing:NodeActionButton.kt$onClick</ID>
|
||||
<ID>LongMethod:AudioConfigItemList.kt$@Composable fun AudioConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:CannedMessageConfigItemList.kt$@Composable fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:DetectionSensorConfigItemList.kt$@Composable fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:DeviceConfigItemList.kt$@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:DisplayConfigItemList.kt$@Composable fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:ExternalNotificationConfigItemList.kt$@Composable fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:LoRaConfigItemList.kt$@Composable fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:NetworkConfigItemList.kt$@Composable fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:PositionConfigItemList.kt$@OptIn(ExperimentalPermissionsApi::class) @Composable fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:PowerConfigItemList.kt$@Composable fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:RadioConfigScreenList.kt$@Composable fun <T : MessageLite> RadioConfigScreenList( title: String, onBack: () -> Unit, responseState: ResponseState<Any>, onDismissPacketResponse: () -> Unit, configState: ConfigState<T>, enabled: Boolean, onSave: (T) -> Unit, content: LazyListScope.() -> Unit, )</ID>
|
||||
<ID>LongMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)</ID>
|
||||
<ID>LongMethod:SecurityConfigItemList.kt$@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun SecurityConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
<ID>LongMethod:SerialConfigItemList.kt$@Composable fun SerialConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)</ID>
|
||||
@@ -34,35 +24,10 @@
|
||||
<ID>MagicNumber:EditChannelDialog.kt$16</ID>
|
||||
<ID>MagicNumber:EditChannelDialog.kt$32</ID>
|
||||
<ID>MagicNumber:PacketResponseStateDialog.kt$100</ID>
|
||||
<ID>ModifierMissing:CleanNodeDatabaseScreen.kt$CleanNodeDatabaseScreen</ID>
|
||||
<ID>ModifierMissing:Debug.kt$DebugScreen</ID>
|
||||
<ID>ModifierMissing:DeviceConfigItemList.kt$DeviceConfigScreen</ID>
|
||||
<ID>ModifierMissing:MapReportingPreference.kt$MapReportingPreference</ID>
|
||||
<ID>ModifierMissing:NetworkConfigItemList.kt$NetworkConfigScreen</ID>
|
||||
<ID>ModifierMissing:PositionConfigItemList.kt$PositionConfigScreen</ID>
|
||||
<ID>ModifierMissing:RadioConfig.kt$RadioConfigItemList</ID>
|
||||
<ID>ModifierMissing:RadioConfigScreenList.kt$RadioConfigScreenList</ID>
|
||||
<ID>ModifierMissing:SecurityConfigItemList.kt$SecurityConfigScreen</ID>
|
||||
<ID>ModifierMissing:SettingsScreen.kt$SettingsScreen</ID>
|
||||
<ID>ModifierNotUsedAtRoot:EditChannelDialog.kt$modifier = modifier.weight(1f)</ID>
|
||||
<ID>ModifierNotUsedAtRoot:EditDeviceProfileDialog.kt$modifier = modifier.weight(1f)</ID>
|
||||
<ID>MultipleEmitters:CleanNodeDatabaseScreen.kt$NodesDeletionPreview</ID>
|
||||
<ID>MultipleEmitters:RadioConfig.kt$RadioConfigItemList</ID>
|
||||
<ID>NestedBlockDepth:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)</ID>
|
||||
<ID>ParameterNaming:ChannelConfigScreen.kt$onPositiveClicked</ID>
|
||||
<ID>ParameterNaming:CleanNodeDatabaseScreen.kt$onCheckedChanged</ID>
|
||||
<ID>ParameterNaming:CleanNodeDatabaseScreen.kt$onDaysChanged</ID>
|
||||
<ID>ParameterNaming:MapReportingPreference.kt$onMapReportingEnabledChanged</ID>
|
||||
<ID>ParameterNaming:MapReportingPreference.kt$onPositionPrecisionChanged</ID>
|
||||
<ID>ParameterNaming:MapReportingPreference.kt$onPublishIntervalSecsChanged</ID>
|
||||
<ID>ParameterNaming:MapReportingPreference.kt$onShouldReportLocationChanged</ID>
|
||||
<ID>PreviewPublic:MapReportingPreference.kt$MapReportingPreview</ID>
|
||||
<ID>ReturnCount:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)</ID>
|
||||
<ID>TooGenericExceptionCaught:LanguageUtils.kt$LanguageUtils$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:RadioConfigViewModel.kt$RadioConfigViewModel$ex: Exception</ID>
|
||||
<ID>TooManyFunctions:RadioConfigViewModel.kt$RadioConfigViewModel : ViewModel</ID>
|
||||
<ID>UnusedParameter:ChannelConfigScreen.kt$onBack: () -> Unit</ID>
|
||||
<ID>UnusedParameter:ChannelConfigScreen.kt$title: String</ID>
|
||||
<ID>ViewModelInjection:DebugSearch.kt$viewModel</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.app.Application
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -57,7 +58,6 @@ import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import org.meshtastic.proto.Portnums
|
||||
import timber.log.Timber
|
||||
import java.io.BufferedWriter
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.FileWriter
|
||||
@@ -287,7 +287,7 @@ constructor(
|
||||
}
|
||||
}
|
||||
} catch (ex: FileNotFoundException) {
|
||||
Timber.e("Can't write file error: ${ex.message}")
|
||||
Logger.e { "Can't write file error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.touchlab.kermit.Logger
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -96,7 +97,6 @@ import org.meshtastic.core.ui.theme.AnnotationColor
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.core.ui.util.showToast
|
||||
import org.meshtastic.feature.settings.debugging.DebugViewModel.UiMeshLog
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.io.OutputStreamWriter
|
||||
import java.nio.charset.StandardCharsets
|
||||
@@ -408,7 +408,7 @@ private suspend fun exportAllLogsToUri(context: Context, targetUri: Uri, logs: L
|
||||
withContext(Dispatchers.Main) { context.showToast(Res.string.debug_export_success, logs.size) }
|
||||
} catch (e: IOException) {
|
||||
withContext(Dispatchers.Main) { context.showToast(Res.string.debug_export_failed, e.message ?: "") }
|
||||
Timber.w(e, "Error:IOException ")
|
||||
Logger.w(e) { "Error:IOException" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.meshtastic.feature.settings.debugging
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -44,7 +45,6 @@ import org.meshtastic.proto.PaxcountProtos
|
||||
import org.meshtastic.proto.Portnums.PortNum
|
||||
import org.meshtastic.proto.StoreAndForwardProtos
|
||||
import org.meshtastic.proto.TelemetryProtos
|
||||
import timber.log.Timber
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
@@ -237,7 +237,7 @@ constructor(
|
||||
}
|
||||
|
||||
init {
|
||||
Timber.d("DebugViewModel created")
|
||||
Logger.d { "DebugViewModel created" }
|
||||
viewModelScope.launch {
|
||||
combine(searchManager.searchText, filterManager.filteredLogs) { searchText, logs ->
|
||||
searchManager.findSearchMatches(searchText, logs)
|
||||
@@ -250,7 +250,7 @@ constructor(
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
Timber.d("DebugViewModel cleared")
|
||||
Logger.d { "DebugViewModel cleared" }
|
||||
}
|
||||
|
||||
private fun toUiState(databaseLogs: List<MeshLog>) = databaseLogs
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.toRoute
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.google.protobuf.MessageLite
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -80,7 +81,6 @@ import org.meshtastic.proto.Portnums
|
||||
import org.meshtastic.proto.config
|
||||
import org.meshtastic.proto.deviceProfile
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
import timber.log.Timber
|
||||
import java.io.FileOutputStream
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -179,7 +179,7 @@ constructor(
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
Timber.d("RadioConfigViewModel created")
|
||||
Logger.d { "RadioConfigViewModel created" }
|
||||
}
|
||||
|
||||
private val myNodeInfo: StateFlow<MyNodeEntity?>
|
||||
@@ -205,7 +205,7 @@ constructor(
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
Timber.d("RadioConfigViewModel cleared")
|
||||
Logger.d { "RadioConfigViewModel cleared" }
|
||||
}
|
||||
|
||||
private fun request(destNum: Int, requestAction: suspend (IMeshService, Int, Int) -> Unit, errorMessage: String) =
|
||||
@@ -227,7 +227,7 @@ constructor(
|
||||
}
|
||||
}
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("$errorMessage: ${ex.message}")
|
||||
Logger.e { "$errorMessage: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -422,7 +422,7 @@ constructor(
|
||||
try {
|
||||
meshService?.setFixedPosition(destNum, position)
|
||||
} catch (ex: RemoteException) {
|
||||
Timber.e("Set fixed position error: ${ex.message}")
|
||||
Logger.e { "Set fixed position error: ${ex.message}" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ constructor(
|
||||
onResult(protobuf)
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
Timber.e("Import DeviceProfile error: ${ex.message}")
|
||||
Logger.e { "Import DeviceProfile error: ${ex.message}" }
|
||||
sendError(ex.customMessage)
|
||||
}
|
||||
}
|
||||
@@ -452,7 +452,7 @@ constructor(
|
||||
}
|
||||
setResponseStateSuccess()
|
||||
} catch (ex: Exception) {
|
||||
Timber.e("Can't write file error: ${ex.message}")
|
||||
Logger.e { "Can't write file error: ${ex.message}" }
|
||||
sendError(ex.customMessage)
|
||||
}
|
||||
}
|
||||
@@ -491,7 +491,7 @@ constructor(
|
||||
setResponseStateSuccess()
|
||||
} catch (ex: Exception) {
|
||||
val errorMessage = "Can't write security keys JSON error: ${ex.message}"
|
||||
Timber.e(errorMessage)
|
||||
Logger.e { errorMessage }
|
||||
sendError(ex.customMessage)
|
||||
}
|
||||
}
|
||||
@@ -514,7 +514,7 @@ constructor(
|
||||
try {
|
||||
setChannels(channelUrl)
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "DeviceProfile channel import error")
|
||||
Logger.e(ex) { "DeviceProfile channel import error" }
|
||||
sendError(ex.customMessage)
|
||||
}
|
||||
}
|
||||
@@ -656,7 +656,7 @@ constructor(
|
||||
|
||||
if (data?.portnumValue == Portnums.PortNum.ROUTING_APP_VALUE) {
|
||||
val parsed = MeshProtos.Routing.parseFrom(data.payload)
|
||||
Timber.d(debugMsg.format(parsed.errorReason.name))
|
||||
Logger.d { debugMsg.format(parsed.errorReason.name) }
|
||||
if (parsed.errorReason != MeshProtos.Routing.Error.NONE) {
|
||||
sendError(getStringResFrom(parsed.errorReasonValue))
|
||||
} else if (packet.from == destNum && route.isEmpty()) {
|
||||
@@ -670,7 +670,7 @@ constructor(
|
||||
}
|
||||
if (data?.portnumValue == Portnums.PortNum.ADMIN_APP_VALUE) {
|
||||
val parsed = AdminProtos.AdminMessage.parseFrom(data.payload)
|
||||
Timber.d(debugMsg.format(parsed.payloadVariantCase.name))
|
||||
Logger.d { debugMsg.format(parsed.payloadVariantCase.name) }
|
||||
if (destNum != packet.from) {
|
||||
sendError("Unexpected sender: ${packet.from.toUInt()} instead of ${destNum.toUInt()}.")
|
||||
return
|
||||
@@ -744,7 +744,7 @@ constructor(
|
||||
incrementCompleted()
|
||||
}
|
||||
|
||||
else -> Timber.d("No custom processing needed for ${parsed.payloadVariantCase}")
|
||||
else -> Logger.d { "No custom processing needed for ${parsed.payloadVariantCase}" }
|
||||
}
|
||||
|
||||
if (AdminRoute.entries.any { it.name == route }) {
|
||||
|
||||
@@ -44,6 +44,7 @@ import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.touchlab.kermit.Logger
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.advanced
|
||||
@@ -79,7 +80,6 @@ import org.meshtastic.feature.settings.util.gpioPins
|
||||
import org.meshtastic.feature.settings.util.toDisplayString
|
||||
import org.meshtastic.proto.copy
|
||||
import org.meshtastic.proto.moduleConfig
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
private const val MAX_RINGTONE_SIZE = 230
|
||||
@@ -116,7 +116,7 @@ fun ExternalNotificationConfigScreen(
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error importing ringtone")
|
||||
Logger.e(e) { "Error importing ringtone" }
|
||||
Toast.makeText(context, "Error importing: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
@@ -308,7 +308,7 @@ fun ExternalNotificationConfigScreen(
|
||||
tempFile.delete()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to play ringtone")
|
||||
Logger.e(e) { "Failed to play ringtone" }
|
||||
}
|
||||
},
|
||||
enabled = state.connected,
|
||||
|
||||
@@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalResources
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import co.touchlab.kermit.Logger
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.meshtastic.core.strings.Res
|
||||
import org.meshtastic.core.strings.fr_HT
|
||||
@@ -30,7 +31,6 @@ import org.meshtastic.core.strings.pt_BR
|
||||
import org.meshtastic.core.strings.zh_CN
|
||||
import org.meshtastic.core.strings.zh_TW
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
||||
object LanguageUtils {
|
||||
@@ -69,7 +69,7 @@ object LanguageUtils {
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e("Error parsing locale_config.xml: ${e.message}")
|
||||
Logger.e { "Error parsing locale_config.xml: ${e.message}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,8 @@ osmbonuspack = { module = "com.github.MKergall:osmbonuspack", version = "6.9.0"
|
||||
osmdroid-android = { module = "org.osmdroid:osmdroid-android", version.ref = "osmdroid-android" }
|
||||
osmdroid-geopackage = { module = "org.osmdroid:osmdroid-geopackage", version.ref = "osmdroid-android" }
|
||||
streamsupport-minifuture = { module = "net.sourceforge.streamsupport:streamsupport-minifuture", version = "1.7.4" }
|
||||
timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" }
|
||||
kermit = { module = "co.touchlab:kermit", version = "2.0.5" }
|
||||
|
||||
usb-serial-android = { module = "com.github.mik3y:usb-serial-for-android", version = "3.10.0" }
|
||||
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version = "4.3.0" }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user