diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 7b95c1444..837a8458d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -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)
diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml
index b5b3189af..0227b52b2 100644
--- a/app/detekt-baseline.xml
+++ b/app/detekt-baseline.xml
@@ -5,11 +5,6 @@
CommentSpacing:BLEException.kt$BLEConnectionClosing$/// Our interface is being shut down
CommentSpacing:Constants.kt$/// a bool true means we expect this condition to continue until, false means device might come back
CommentSpacing:Coroutines.kt$/// Wrap launch with an exception handler, FIXME, move into a utility lib
- ComposableParamOrder:Channel.kt$ChannelScreen
- ComposableParamOrder:Channel.kt$EditChannelUrl
- ComposableParamOrder:ConnectionsNavIcon.kt$ConnectionsNavIcon
- ComposableParamOrder:EmptyStateContent.kt$EmptyStateContent
- ComposableParamOrder:Share.kt$ShareScreen
CyclomaticComplexMethod:BleError.kt$BleError.Companion$fun from(exception: Throwable): BleError
CyclomaticComplexMethod:MeshService.kt$MeshService$private fun handleReceivedData(packet: MeshPacket)
CyclomaticComplexMethod:SettingsNavigation.kt$@Suppress("LongMethod") fun NavGraphBuilder.settingsGraph(navController: NavHostController)
@@ -32,10 +27,9 @@
FinalNewline:SerialInterfaceFactory.kt$com.geeksville.mesh.repository.radio.SerialInterfaceFactory.kt
FinalNewline:TCPInterfaceFactory.kt$com.geeksville.mesh.repository.radio.TCPInterfaceFactory.kt
FinalNewline:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt
- LambdaParameterEventTrailing:Channel.kt$onConfirm
- LambdaParameterInRestartableEffect:Channel.kt$onConfirm
LargeClass:MeshService.kt$MeshService : Service
LongMethod:MeshService.kt$MeshService$private fun handleReceivedData(packet: MeshPacket)
+ LongMethod:TCPInterface.kt$TCPInterface$private suspend fun startConnect()
MagicNumber:Contacts.kt$7
MagicNumber:Contacts.kt$8
MagicNumber:MQTTRepository.kt$MQTTRepository$512
@@ -60,27 +54,15 @@
MagicNumber:StreamInterface.kt$StreamInterface$4
MagicNumber:StreamInterface.kt$StreamInterface$8
MagicNumber:TCPInterface.kt$TCPInterface$1000
- MagicNumber:TCPInterface.kt$TCPInterface$180
- MagicNumber:TCPInterface.kt$TCPInterface$500
MagicNumber:UIState.kt$4
MaxLineLength:MeshService.kt$MeshService$"Config complete id mismatch: received=$configCompleteId expected one of [$configOnlyNonce,$nodeInfoNonce]"
+ MaxLineLength:MeshService.kt$MeshService$"Neighbor info response filtered: ToUs=$isAddressedToUs, isRecentRequest=$isRecentRequest"
MaxLineLength:MeshService.kt$MeshService$"setOwner Id: $id longName: ${longName.anonymize} shortName: $shortName isLicensed: $isLicensed isUnmessagable: $isUnmessagable"
MaxLineLength:MeshService.kt$MeshService.<no name provided>$"sendData dest=${p.to}, id=${p.id} <- ${bytes.size} bytes (connectionState=${connectionStateHolder.connectionState.value})"
MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found fromNum: ${fromNumCharacteristic?.uuid}, ${fromNumCharacteristic?.instanceId}"
MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found fromRadio: ${fromRadioCharacteristic?.uuid}, ${fromRadioCharacteristic?.instanceId}"
MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found logRadio: ${logRadioCharacteristic?.uuid}, ${logRadioCharacteristic?.instanceId}"
MaxLineLength:NordicBleInterface.kt$NordicBleInterface$"[$address] Found toRadio: ${toRadioCharacteristic?.uuid}, ${toRadioCharacteristic?.instanceId}"
- ModifierClickableOrder:Channel.kt$clickable(onClick = onClick)
- ModifierMissing:BLEDevices.kt$BLEDevices
- ModifierMissing:Channel.kt$ChannelScreen
- ModifierMissing:Contacts.kt$ContactListView
- ModifierMissing:Contacts.kt$ContactsScreen
- ModifierMissing:Contacts.kt$SelectionToolbar
- ModifierMissing:EmptyStateContent.kt$EmptyStateContent
- ModifierMissing:Main.kt$MainScreen
- ModifierMissing:NetworkDevices.kt$NetworkDevices
- ModifierMissing:Share.kt$ShareScreen
- MutableStateAutoboxing:Contacts.kt$mutableStateOf(2)
NestedBlockDepth:MeshService.kt$MeshService$private fun handleReceivedAdmin(fromNodeNum: Int, a: AdminProtos.AdminMessage)
NestedBlockDepth:MeshService.kt$MeshService$private fun handleReceivedData(packet: MeshPacket)
NewLineAtEndOfFile:BLEException.kt$com.geeksville.mesh.service.BLEException.kt
@@ -106,11 +88,6 @@
NoEmptyClassBody:DebugLogFile.kt$BinaryLogFile${ }
NoSemicolons:DateUtils.kt$DateUtils$;
OptionalAbstractKeyword:SyncContinuation.kt$Continuation$abstract
- ParameterNaming:Contacts.kt$onDeleteSelected
- ParameterNaming:Contacts.kt$onMuteSelected
- ParameterNaming:UsbDevices.kt$onDeviceSelected
- PreviewPublic:Channel.kt$ModemPresetInfoPreview
- PreviewPublic:EmptyStateContent.kt$EmptyStateContentPreview
RethrowCaughtException:SyncContinuation.kt$Continuation$throw ex
SwallowedException:Exceptions.kt$ex: Throwable
SwallowedException:NsdManager.kt$ex: IllegalArgumentException
@@ -138,6 +115,5 @@
TooManyFunctions:UIState.kt$UIViewModel : ViewModel
TopLevelPropertyNaming:Constants.kt$const val prefix = "com.geeksville.mesh"
UtilityClassWithPublicConstructor:NetworkRepositoryModule.kt$NetworkRepositoryModule
- ViewModelForwarding:Main.kt$VersionChecks(uIViewModel)
diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
index daf26128e..914d69c79 100644
--- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt
+++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
@@ -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" }
}
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/MeshServiceClient.kt b/app/src/main/java/com/geeksville/mesh/MeshServiceClient.kt
index f57e487ce..94cd1a191 100644
--- a/app/src/main/java/com/geeksville/mesh/MeshServiceClient.kt
+++ b/app/src/main/java/com/geeksville/mesh/MeshServiceClient.kt
@@ -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)
diff --git a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt b/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
index 11feaa972..c2df57fa3 100644
--- a/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
+++ b/app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
@@ -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
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/android/ServiceClient.kt b/app/src/main/java/com/geeksville/mesh/android/ServiceClient.kt
index f10baf5bf..3216f7daa 100644
--- a/app/src/main/java/com/geeksville/mesh/android/ServiceClient.kt
+++ b/app/src/main/java/com/geeksville/mesh/android/ServiceClient.kt
@@ -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(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(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(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" }
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/concurrent/DeferredExecution.kt b/app/src/main/java/com/geeksville/mesh/concurrent/DeferredExecution.kt
index 934d8910b..4bfa231f6 100644
--- a/app/src/main/java/com/geeksville/mesh/concurrent/DeferredExecution.kt
+++ b/app/src/main/java/com/geeksville/mesh/concurrent/DeferredExecution.kt
@@ -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()
}
diff --git a/app/src/main/java/com/geeksville/mesh/concurrent/SyncContinuation.kt b/app/src/main/java/com/geeksville/mesh/concurrent/SyncContinuation.kt
index 6e7d56ce8..ee81e75a4 100644
--- a/app/src/main/java/com/geeksville/mesh/concurrent/SyncContinuation.kt
+++ b/app/src/main/java/com/geeksville/mesh/concurrent/SyncContinuation.kt
@@ -27,7 +27,7 @@ interface Continuation {
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
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt b/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt
index e09146993..a8cca8e9b 100644
--- a/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt
+++ b/app/src/main/java/com/geeksville/mesh/model/BTScanModel.kt
@@ -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)
diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt
index 0d340c9cb..880810aec 100644
--- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt
+++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt
@@ -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 = 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
diff --git a/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothRepository.kt
index 0544556b2..f41869beb 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothRepository.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/bluetooth/BluetoothRepository.kt
@@ -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")
diff --git a/app/src/main/java/com/geeksville/mesh/repository/network/MQTTRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/network/MQTTRepository.kt
index 074d1851f..d1b29cf80 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/network/MQTTRepository.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/network/MQTTRepository.kt
@@ -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}" }
}
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/repository/network/NsdManager.kt b/app/src/main/java/com/geeksville/mesh/repository/network/NsdManager.kt
index 47a47f7b8..4646543b3 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/network/NsdManager.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/network/NsdManager.kt
@@ -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 {
diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/MockInterface.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/MockInterface.kt
index a2f80bae1..3dc1da8b8 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/radio/MockInterface.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/radio/MockInterface.kt
@@ -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")
diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterface.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterface.kt
index 9ceed32c7..e88508b6d 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterface.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterface.kt
@@ -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)
diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceSpec.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceSpec.kt
index 8316cea2b..98698206b 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceSpec.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/radio/NordicBleInterfaceSpec.kt
@@ -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
diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt
index 995db651b..b34756481 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt
@@ -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" }
}
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterface.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterface.kt
index 264d0cab3..d015621a5 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterface.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/radio/SerialInterface.kt
@@ -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" }
}
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/StreamInterface.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/StreamInterface.kt
index a7e712e63..3dd50cea0 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/radio/StreamInterface.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/radio/StreamInterface.kt
@@ -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
}
diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterface.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterface.kt
index 5bc3442b8..68333275e 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterface.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/radio/TCPInterface.kt
@@ -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)"
+ }
}
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnectionImpl.kt b/app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnectionImpl.kt
index ad9c3bc47..508e9642e 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnectionImpl.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/usb/SerialConnectionImpl.kt
@@ -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,
diff --git a/app/src/main/java/com/geeksville/mesh/repository/usb/UsbBroadcastReceiver.kt b/app/src/main/java/com/geeksville/mesh/repository/usb/UsbBroadcastReceiver.kt
index e7f60855a..3f324ec05 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/usb/UsbBroadcastReceiver.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/usb/UsbBroadcastReceiver.kt
@@ -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()
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
index 0e14bf926..f99b05224 100644
--- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt
@@ -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 = 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 {
diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshServiceBroadcasts.kt b/app/src/main/java/com/geeksville/mesh/service/MeshServiceBroadcasts.kt
index 5f148da72..1588394d3 100644
--- a/app/src/main/java/com/geeksville/mesh/service/MeshServiceBroadcasts.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/MeshServiceBroadcasts.kt
@@ -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)
diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshServiceStarter.kt b/app/src/main/java/com/geeksville/mesh/service/MeshServiceStarter.kt
index 67274eaa6..ed59ff5b2 100644
--- a/app/src/main/java/com/geeksville/mesh/service/MeshServiceStarter.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/MeshServiceStarter.kt
@@ -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)
diff --git a/app/src/main/java/com/geeksville/mesh/service/PacketHandler.kt b/app/src/main/java/com/geeksville/mesh/service/PacketHandler.kt
index dd4886613..a46b835ba 100644
--- a/app/src/main/java/com/geeksville/mesh/service/PacketHandler.kt
+++ b/app/src/main/java/com/geeksville/mesh/service/PacketHandler.kt
@@ -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
diff --git a/app/src/main/java/com/geeksville/mesh/ui/Main.kt b/app/src/main/java/com/geeksville/mesh/ui/Main.kt
index 1ea9d75a7..ea07f4f46 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/Main.kt
@@ -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" }
}
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt
index 595d7994e..90b233d1c 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt
@@ -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
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt b/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt
index 79e070200..a36f8fc2e 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/sharing/Channel.kt
@@ -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 {
diff --git a/app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt b/app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt
index a7a7a76c7..fb6c6d29f 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/sharing/ChannelViewModel.kt
@@ -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" }
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/util/Exceptions.kt b/app/src/main/java/com/geeksville/mesh/util/Exceptions.kt
index 108a07cf9..4471efd4f 100644
--- a/app/src/main/java/com/geeksville/mesh/util/Exceptions.kt
+++ b/app/src/main/java/com/geeksville/mesh/util/Exceptions.kt
@@ -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" }
}
}
diff --git a/core/analytics/build.gradle.kts b/core/analytics/build.gradle.kts
index c19f95a56..7d23198df 100644
--- a/core/analytics/build.gradle.kts
+++ b/core/analytics/build.gradle.kts
@@ -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)
diff --git a/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt b/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt
index 84bd0ff07..a8b4532d1 100644
--- a/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt
+++ b/core/analytics/src/fdroid/kotlin/org/meshtastic/core/analytics/platform/FdroidPlatformAnalytics.kt
@@ -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()}" }
}
}
diff --git a/core/analytics/src/google/kotlin/org/meshtastic/core/analytics/platform/GooglePlatformAnalytics.kt b/core/analytics/src/google/kotlin/org/meshtastic/core/analytics/platform/GooglePlatformAnalytics.kt
index 412fed033..36d5db6ca 100644
--- a/core/analytics/src/google/kotlin/org/meshtastic/core/analytics/platform/GooglePlatformAnalytics.kt
+++ b/core/analytics/src/google/kotlin/org/meshtastic/core/analytics/platform/GooglePlatformAnalytics.kt
@@ -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)
}
diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts
index 220af53c9..3f11c5cb9 100644
--- a/core/data/build.gradle.kts
+++ b/core/data/build.gradle.kts
@@ -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)
}
diff --git a/core/data/detekt-baseline.xml b/core/data/detekt-baseline.xml
index b15fbf373..572c2ff08 100644
--- a/core/data/detekt-baseline.xml
+++ b/core/data/detekt-baseline.xml
@@ -6,6 +6,11 @@
MagicNumber:LocationRepository.kt$LocationRepository$30
MagicNumber:LocationRepository.kt$LocationRepository$31
MagicNumber:PacketRepository.kt$PacketRepository$500
+ MaxLineLength:DeviceHardwareRepository.kt$DeviceHardwareRepository$"DeviceHardwareRepository: applying quirk: requiresBootloaderUpgradeForOta=${quirk.requiresBootloaderUpgradeForOta}, infoUrl=${quirk.infoUrl}"
+ MaxLineLength:DeviceHardwareRepository.kt$DeviceHardwareRepository$"DeviceHardwareRepository: cache ${if (staleEntity == null) "empty" else "incomplete"} for hwModel=$hwModel, falling back to bundled JSON asset"
+ MaxLineLength:DeviceHardwareRepository.kt$DeviceHardwareRepository$"DeviceHardwareRepository: failed to load device hardware from bundled JSON for hwModel=$hwModel"
+ MaxLineLength:DeviceHardwareRepository.kt$DeviceHardwareRepository$"DeviceHardwareRepository: lookup after JSON load for hwModel=$hwModel ${if (base != null) "succeeded" else "returned null"}"
+ MaxLineLength:DeviceHardwareRepository.kt$DeviceHardwareRepository$"DeviceHardwareRepository: lookup after remote fetch for hwModel=$hwModel ${if (fromDb != null) "succeeded" else "returned null"}"
TooGenericExceptionCaught:LocationRepository.kt$LocationRepository$e: Exception
TooManyFunctions:PacketRepository.kt$PacketRepository
diff --git a/core/data/src/google/kotlin/org/meshtastic/core/data/repository/CustomTileProviderRepository.kt b/core/data/src/google/kotlin/org/meshtastic/core/data/repository/CustomTileProviderRepository.kt
index 219a247ce..9ce615f53 100644
--- a/core/data/src/google/kotlin/org/meshtastic/core/data/repository/CustomTileProviderRepository.kt
+++ b/core/data/src/google/kotlin/org/meshtastic/core/data/repository/CustomTileProviderRepository.kt
@@ -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>
@@ -88,7 +87,7 @@ constructor(
try {
customTileProvidersStateFlow.value = json.decodeFromString>(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" }
}
}
}
diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt b/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt
index 5cc5081ba..29376cd16 100644
--- a/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt
+++ b/core/data/src/main/kotlin/org/meshtastic/core/data/datasource/BootloaderOtaQuirksJsonDataSource.kt
@@ -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(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 = emptyList())
diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepository.kt b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepository.kt
index fcd0b30cf..f527ae6c8 100644
--- a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepository.kt
+++ b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/DeviceHardwareRepository.kt
@@ -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 =
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): Result =
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 {
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,
diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt
index ae0d1b389..c14ac47bf 100644
--- a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt
+++ b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepository.kt
@@ -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}" } }
}
}
diff --git a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/LocationRepository.kt b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/LocationRepository.kt
index 7849ea1df..35d087bf3 100644
--- a/core/data/src/main/kotlin/org/meshtastic/core/data/repository/LocationRepository.kt
+++ b/core/data/src/main/kotlin/org/meshtastic/core/data/repository/LocationRepository.kt
@@ -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")
diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts
index 2b4dae22d..6de311544 100644
--- a/core/database/build.gradle.kts
+++ b/core/database/build.gradle.kts
@@ -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)
diff --git a/core/database/src/main/kotlin/org/meshtastic/core/database/Converters.kt b/core/database/src/main/kotlin/org/meshtastic/core/database/Converters.kt
index 4237db7d4..f9078a5ff 100644
--- a/core/database/src/main/kotlin/org/meshtastic/core/database/Converters.kt
+++ b/core/database/src/main/kotlin/org/meshtastic/core/database/Converters.kt
@@ -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()
}
diff --git a/core/database/src/main/kotlin/org/meshtastic/core/database/DatabaseManager.kt b/core/database/src/main/kotlin/org/meshtastic/core/database/DatabaseManager.kt
index 936e50e13..7f6250887 100644
--- a/core/database/src/main/kotlin/org/meshtastic/core/database/DatabaseManager.kt
+++ b/core/database/src/main/kotlin/org/meshtastic/core/database/DatabaseManager.kt
@@ -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()
diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts
index 03397aa88..ac23e9b57 100644
--- a/core/datastore/build.gradle.kts
+++ b/core/datastore/build.gradle.kts
@@ -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)
}
diff --git a/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/BootloaderWarningDataSource.kt b/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/BootloaderWarningDataSource.kt
index 7770ea05b..f90176671 100644
--- a/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/BootloaderWarningDataSource.kt
+++ b/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/BootloaderWarningDataSource.kt
@@ -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>(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())
diff --git a/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/ChannelSetDataSource.kt b/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/ChannelSetDataSource.kt
index 5d726936e..fd64decdf 100644
--- a/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/ChannelSetDataSource.kt
+++ b/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/ChannelSetDataSource.kt
@@ -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
diff --git a/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/LocalConfigDataSource.kt b/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/LocalConfigDataSource.kt
index 95795f46f..06423c6b8 100644
--- a/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/LocalConfigDataSource.kt
+++ b/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/LocalConfigDataSource.kt
@@ -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()
diff --git a/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/ModuleConfigDataSource.kt b/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/ModuleConfigDataSource.kt
index 54dcf03e4..d96c3113e 100644
--- a/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/ModuleConfigDataSource.kt
+++ b/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/ModuleConfigDataSource.kt
@@ -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()
diff --git a/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/RecentAddressesDataSource.kt b/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/RecentAddressesDataSource.kt
index a8d6d0db1..63501dc91 100644
--- a/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/RecentAddressesDataSource.kt
+++ b/core/datastore/src/main/kotlin/org/meshtastic/core/datastore/RecentAddressesDataSource.kt
@@ -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>(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
}
}
diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts
index bb2de5bdb..2fbf17116 100644
--- a/core/model/build.gradle.kts
+++ b/core/model/build.gradle.kts
@@ -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)
diff --git a/core/model/detekt-baseline.xml b/core/model/detekt-baseline.xml
index f459aeba8..49dc09531 100644
--- a/core/model/detekt-baseline.xml
+++ b/core/model/detekt-baseline.xml
@@ -2,30 +2,6 @@
- MagicNumber:Channel.kt$0xff
- MagicNumber:ChannelOption.kt$.03125f
- MagicNumber:ChannelOption.kt$.0625f
- MagicNumber:ChannelOption.kt$.203125f
- MagicNumber:ChannelOption.kt$.40625f
- MagicNumber:ChannelOption.kt$.8125f
- MagicNumber:ChannelOption.kt$1.6250f
- MagicNumber:ChannelOption.kt$1000f
- MagicNumber:ChannelOption.kt$1600
- MagicNumber:ChannelOption.kt$200
- MagicNumber:ChannelOption.kt$3.25f
- MagicNumber:ChannelOption.kt$31
- MagicNumber:ChannelOption.kt$400
- MagicNumber:ChannelOption.kt$5
- MagicNumber:ChannelOption.kt$62
- MagicNumber:ChannelOption.kt$800
- MagicNumber:ChannelOption.kt$ChannelOption.LONG_FAST$.250f
- MagicNumber:ChannelOption.kt$ChannelOption.LONG_MODERATE$.125f
- MagicNumber:ChannelOption.kt$ChannelOption.LONG_SLOW$.125f
- MagicNumber:ChannelOption.kt$ChannelOption.MEDIUM_FAST$.250f
- MagicNumber:ChannelOption.kt$ChannelOption.MEDIUM_SLOW$.250f
- MagicNumber:ChannelOption.kt$ChannelOption.SHORT_FAST$.250f
- MagicNumber:ChannelOption.kt$ChannelOption.SHORT_SLOW$.250f
- MagicNumber:ChannelOption.kt$ChannelOption.VERY_LONG_SLOW$.0625f
MagicNumber:ChannelSet.kt$40
MagicNumber:ChannelSet.kt$960
SwallowedException:ChannelSet.kt$ex: Throwable
diff --git a/core/model/src/main/kotlin/org/meshtastic/core/model/DeviceVersion.kt b/core/model/src/main/kotlin/org/meshtastic/core/model/DeviceVersion.kt
index 5d07720a6..d9eda30cb 100644
--- a/core/model/src/main/kotlin/org/meshtastic/core/model/DeviceVersion.kt
+++ b/core/model/src/main/kotlin/org/meshtastic/core/model/DeviceVersion.kt
@@ -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 {
@@ -28,7 +28,7 @@ data class DeviceVersion(val asString: String) : Comparable {
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
}
diff --git a/core/model/src/main/kotlin/org/meshtastic/core/model/util/ChannelSet.kt b/core/model/src/main/kotlin/org/meshtastic/core/model/util/ChannelSet.kt
index 757fefc3e..560bda0d8 100644
--- a/core/model/src/main/kotlin/org/meshtastic/core/model/util/ChannelSet.kt
+++ b/core/model/src/main/kotlin/org/meshtastic/core/model/util/ChannelSet.kt
@@ -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
}
diff --git a/core/service/build.gradle.kts b/core/service/build.gradle.kts
index 3e0453957..4d4235083 100644
--- a/core/service/build.gradle.kts
+++ b/core/service/build.gradle.kts
@@ -46,5 +46,5 @@ dependencies {
implementation(projects.core.proto)
implementation(libs.javax.inject)
implementation(libs.kotlinx.coroutines.core)
- implementation(libs.timber)
+ implementation(libs.kermit)
}
diff --git a/core/service/src/main/kotlin/org/meshtastic/core/service/ServiceRepository.kt b/core/service/src/main/kotlin/org/meshtastic/core/service/ServiceRepository.kt
index f6c2eb274..a74df12e2 100644
--- a/core/service/src/main/kotlin/org/meshtastic/core/service/ServiceRepository.kt
+++ b/core/service/src/main/kotlin/org/meshtastic/core/service/ServiceRepository.kt
@@ -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
}
diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts
index 772f8aba7..1960906c9 100644
--- a/core/ui/build.gradle.kts
+++ b/core/ui/build.gradle.kts
@@ -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)
diff --git a/core/ui/detekt-baseline.xml b/core/ui/detekt-baseline.xml
index aa0daf951..04634d26e 100644
--- a/core/ui/detekt-baseline.xml
+++ b/core/ui/detekt-baseline.xml
@@ -2,18 +2,6 @@
- ComposableParamOrder:AlertDialogs.kt$SimpleAlertDialog
- ComposableParamOrder:EditBase64Preference.kt$EditBase64Preference
- ComposableParamOrder:EditTextPreference.kt$EditTextPreference
- ComposableParamOrder:MainAppBar.kt$MainAppBar
- ComposableParamOrder:MaterialBatteryInfo.kt$MaterialBatteryInfo
- ComposableParamOrder:NodeChip.kt$NodeChip
- ComposableParamOrder:NodeKeyStatusIcon.kt$NodeKeyStatusIcon
- ComposableParamOrder:SignalInfo.kt$SignalInfo
- ComposableParamOrder:SwitchPreference.kt$SwitchPreference
- ContentSlotReused:AdaptiveTwoPane.kt$second
- LambdaParameterEventTrailing:ContactSharing.kt$onSharedContactRequested
- LambdaParameterEventTrailing:MainAppBar.kt$onClickChip
MagicNumber:EditIPv4Preference.kt$0xff
MagicNumber:EditIPv4Preference.kt$16
MagicNumber:EditIPv4Preference.kt$24
@@ -22,47 +10,5 @@
MagicNumber:EditListPreference.kt$12345
MagicNumber:EditListPreference.kt$67890
MagicNumber:LazyColumnDragAndDropDemo.kt$50
- ModifierMissing:AdaptiveTwoPane.kt$AdaptiveTwoPane
- ModifierMissing:ChannelItem.kt$ChannelItem
- ModifierMissing:ChannelSelection.kt$ChannelSelection
- ModifierMissing:ContactSharing.kt$SharedContactDialog
- ModifierMissing:EmojiPicker.kt$EmojiPicker
- ModifierMissing:EmojiPicker.kt$EmojiPickerDialog
- ModifierMissing:IndoorAirQuality.kt$IndoorAirQuality
- ModifierMissing:LoraSignalIndicator.kt$LoraSignalIndicator
- ModifierMissing:LoraSignalIndicator.kt$Rssi
- ModifierMissing:LoraSignalIndicator.kt$Snr
- ModifierMissing:LoraSignalIndicator.kt$SnrAndRssi
- ModifierMissing:PreferenceDivider.kt$PreferenceDivider
- ModifierMissing:SecurityIcon.kt$SecurityIcon
- ModifierMissing:SharedContactDialog.kt$SharedContactDialog
- ModifierMissing:SimpleAlertDialog.kt$SimpleAlertDialog
- ModifierMissing:SlidingSelector.kt$OptionLabel
- ModifierNotUsedAtRoot:TextDividerPreference.kt$modifier = modifier.fillMaxWidth().padding(all = 16.dp)
- ModifierNotUsedAtRoot:TextDividerPreference.kt$modifier = modifier.fillMaxWidth().wrapContentWidth(Alignment.End)
- 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() } } }
- ModifierReused:PreferenceCategory.kt$Text( text, modifier = modifier.padding(start = 16.dp, top = 24.dp, bottom = 8.dp, end = 16.dp), style = MaterialTheme.typography.titleLarge, )
- 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)) } } }
- ModifierReused:TextDividerPreference.kt$Icon(trailingIcon, "trailingIcon", modifier = modifier.fillMaxWidth().wrapContentWidth(Alignment.End))
- 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)) } }
- MultipleEmitters:PreferenceCategory.kt$PreferenceCategory
- ParameterNaming:BitwisePreference.kt$onItemSelected
- ParameterNaming:ChannelSelection.kt$onSelected
- ParameterNaming:ContactSharing.kt$onSharedContactRequested
- ParameterNaming:DropDownPreference.kt$onItemSelected
- ParameterNaming:EditIPv4Preference.kt$onValueChanged
- ParameterNaming:EditListPreference.kt$onValuesChanged
- ParameterNaming:EditPasswordPreference.kt$onValueChanged
- ParameterNaming:EditTextPreference.kt$onValueChanged
- ParameterNaming:PositionPrecisionPreference.kt$onValueChanged
- ParameterNaming:PreferenceFooter.kt$onNegativeClicked
- ParameterNaming:PreferenceFooter.kt$onPositiveClicked
- ParameterNaming:SlidingSelector.kt$onOptionSelected
- PreviewPublic:IndoorAirQuality.kt$IAQScalePreview
- PreviewPublic:LazyColumnDragAndDropDemo.kt$LazyColumnDragAndDropDemo
- PreviewPublic:MaterialBatteryInfo.kt$MaterialBatteryInfoPreview
- PreviewPublic:SignalInfo.kt$SignalInfoPreview
- PreviewPublic:SignalInfo.kt$SignalInfoSelfPreview
- PreviewPublic:SignalInfo.kt$SignalInfoSimplePreview
diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ContactSharing.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ContactSharing.kt
index 4d8f00cb8..e8b8cd202 100644
--- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ContactSharing.kt
+++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/component/ContactSharing.kt
@@ -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
}
diff --git a/core/ui/src/main/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeViewModel.kt b/core/ui/src/main/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeViewModel.kt
index 6563700ce..bb4b90322 100644
--- a/core/ui/src/main/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeViewModel.kt
+++ b/core/ui/src/main/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeViewModel.kt
@@ -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" }
}
}
}
diff --git a/feature/firmware/build.gradle.kts b/feature/firmware/build.gradle.kts
index 7a3ddcc96..e0ba936e6 100644
--- a/feature/firmware/build.gradle.kts
+++ b/feature/firmware/build.gradle.kts
@@ -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)
diff --git a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareFileHandler.kt b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareFileHandler.kt
index a4bf93c9e..b5b346949 100644
--- a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareFileHandler.kt
+++ b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareFileHandler.kt
@@ -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
diff --git a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt
index 68a89af2e..a9f938b90 100644
--- a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt
+++ b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateViewModel.kt
@@ -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
}
diff --git a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/UpdateHandler.kt b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/UpdateHandler.kt
index ce21ea6d9..8f23d2270 100644
--- a/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/UpdateHandler.kt
+++ b/feature/firmware/src/main/kotlin/org/meshtastic/feature/firmware/UpdateHandler.kt
@@ -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
}
diff --git a/feature/intro/detekt-baseline.xml b/feature/intro/detekt-baseline.xml
index 5c5d30217..ecf2e0cce 100644
--- a/feature/intro/detekt-baseline.xml
+++ b/feature/intro/detekt-baseline.xml
@@ -1,8 +1,5 @@
-
- ComposableParamOrder:PermissionScreenLayout.kt$PermissionScreenLayout
- ParameterNaming:WelcomeScreen.kt$onGetStarted
-
+
diff --git a/feature/map/build.gradle.kts b/feature/map/build.gradle.kts
index 4c175dab4..b69e19e25 100644
--- a/feature/map/build.gradle.kts
+++ b/feature/map/build.gradle.kts
@@ -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)
diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt
index e4781766b..606004375 100644
--- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt
+++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapView.kt
@@ -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,
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
},
)
diff --git a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewWithLifecycle.kt b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewWithLifecycle.kt
index 090ae1b98..a32e49a0a 100644
--- a/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewWithLifecycle.kt
+++ b/feature/map/src/fdroid/kotlin/org/meshtastic/feature/map/MapViewWithLifecycle.kt
@@ -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}" }
}
}
}
diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/LocationHandler.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/LocationHandler.kt
index c7b5ffa15..ac4d632ed 100644
--- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/LocationHandler.kt
+++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/LocationHandler.kt
@@ -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.
}
}
diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt
index 1350b9aad..d19568b17 100644
--- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt
+++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapView.kt
@@ -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"
}
diff --git a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt
index f08a2c0d9..6aaaa4b55 100644
--- a/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt
+++ b/feature/map/src/google/kotlin/org/meshtastic/feature/map/MapViewModel.kt
@@ -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}" }
}
}
diff --git a/feature/map/src/main/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt b/feature/map/src/main/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt
index ef6021799..8301daba2 100644
--- a/feature/map/src/main/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt
+++ b/feature/map/src/main/kotlin/org/meshtastic/feature/map/BaseMapViewModel.kt
@@ -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}" }
}
}
diff --git a/feature/messaging/build.gradle.kts b/feature/messaging/build.gradle.kts
index 9f80e291d..804daf1f3 100644
--- a/feature/messaging/build.gradle.kts
+++ b/feature/messaging/build.gradle.kts
@@ -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)
diff --git a/feature/messaging/detekt-baseline.xml b/feature/messaging/detekt-baseline.xml
index e117b9886..ecf2e0cce 100644
--- a/feature/messaging/detekt-baseline.xml
+++ b/feature/messaging/detekt-baseline.xml
@@ -1,26 +1,5 @@
-
- ComposableParamOrder:Message.kt$MessageScreen
- ComposableParamOrder:Message.kt$QuickChatRow
- ComposableParamOrder:MessageActions.kt$MessageActions
- ComposableParamOrder:MessageActions.kt$MessageStatusButton
- ComposableParamOrder:MessageItem.kt$MessageItem
- ComposableParamOrder:MessageList.kt$DeliveryInfo
- ComposableParamOrder:MessageList.kt$MessageList
- ComposableParamOrder:QuickChat.kt$OutlinedTextFieldWithCounter
- LambdaParameterEventTrailing:Message.kt$onClick
- LambdaParameterEventTrailing:Message.kt$onSendMessage
- LambdaParameterEventTrailing:MessageList.kt$onReply
- LambdaParameterEventTrailing:QuickChat.kt$onNavigateUp
- LambdaParameterInRestartableEffect:MessageList.kt$onUnreadChanged
- 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, )
- ModifierMissing:Message.kt$MessageScreen
- ModifierNotUsedAtRoot:QuickChat.kt$modifier = modifier.fillMaxSize().padding(innerPadding)
- MutableStateParam:MessageList.kt$selectedIds
- ParameterNaming:MessageList.kt$onUnreadChanged
- TooManyFunctions:MessageViewModel.kt$MessageViewModel : ViewModel
- 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() } } }
-
+
diff --git a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt
index 0ea758b4c..750fa17cd 100644
--- a/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt
+++ b/feature/messaging/src/main/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt
@@ -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}" }
}
}
}
diff --git a/feature/node/build.gradle.kts b/feature/node/build.gradle.kts
index a7f89e78b..be1a651c0 100644
--- a/feature/node/build.gradle.kts
+++ b/feature/node/build.gradle.kts
@@ -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)
diff --git a/feature/node/detekt-baseline.xml b/feature/node/detekt-baseline.xml
index d3fc2282a..3f154d934 100644
--- a/feature/node/detekt-baseline.xml
+++ b/feature/node/detekt-baseline.xml
@@ -3,86 +3,9 @@
CommentWrapping:SignalMetrics.kt$Metric.SNR$/* Selected 12 as the max to get 4 equal vertical sections. */
- ComposableParamOrder:DeviceMetrics.kt$DeviceMetricsChart
- ComposableParamOrder:ElevationInfo.kt$ElevationInfo
- ComposableParamOrder:EnvironmentCharts.kt$ChartContent
- ComposableParamOrder:EnvironmentCharts.kt$EnvironmentMetricsChart
- ComposableParamOrder:EnvironmentCharts.kt$MetricPlottingCanvas
- ComposableParamOrder:HostMetricsLog.kt$HostMetricsItem
- ComposableParamOrder:HostMetricsLog.kt$LogLine
- ComposableParamOrder:LastHeardInfo.kt$LastHeardInfo
- ComposableParamOrder:NodeFilterTextField.kt$NodeFilterTextField
- ComposableParamOrder:NodeItem.kt$NodeItem
- ComposableParamOrder:PaxMetrics.kt$PaxMetricsChart
- ComposableParamOrder:PowerMetrics.kt$PowerMetricsChart
- ComposableParamOrder:SatelliteCountInfo.kt$SatelliteCountInfo
- ComposableParamOrder:SignalMetrics.kt$SignalMetricsChart
- ComposableParamOrder:TracerouteButton.kt$TracerouteButton
- LambdaParameterEventTrailing:TracerouteLog.kt$onNavigateUp
LongMethod:EnvironmentMetrics.kt$@Composable fun EnvironmentMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigateUp: () -> Unit)
- LongMethod:NodeDetailsSection.kt$@Composable private fun MainNodeDetails(node: Node)
MagicNumber:MetricsViewModel.kt$MetricsViewModel$1000L
MagicNumber:MetricsViewModel.kt$MetricsViewModel$1e-5
MagicNumber:MetricsViewModel.kt$MetricsViewModel$1e-7
- ModifierMissing:CommonCharts.kt$ChartHeader
- ModifierMissing:CommonCharts.kt$Legend
- ModifierMissing:CommonCharts.kt$TimeLabels
- ModifierMissing:DeviceMetrics.kt$DeviceMetricsScreen
- ModifierMissing:EnvironmentMetrics.kt$EnvironmentMetricsScreen
- ModifierMissing:HostMetricsLog.kt$HostMetricsLogScreen
- ModifierMissing:NodeListScreen.kt$NodeListScreen
- ModifierMissing:NodeStatusIcons.kt$NodeStatusIcons
- ModifierMissing:PaxMetrics.kt$PaxMetricsItem
- ModifierMissing:PaxMetrics.kt$PaxMetricsScreen
- ModifierMissing:PositionLog.kt$PositionItem
- ModifierMissing:PositionLog.kt$PositionLogScreen
- ModifierMissing:PowerMetrics.kt$PowerMetricsScreen
- ModifierMissing:SignalMetrics.kt$SignalMetricsScreen
- ModifierNotUsedAtRoot:DeviceMetrics.kt$modifier = modifier.weight(weight = Y_AXIS_WEIGHT)
- ModifierNotUsedAtRoot:DeviceMetrics.kt$modifier = modifier.width(dp)
- ModifierNotUsedAtRoot:DeviceMetrics.kt$modifier.width(dp)
- ModifierNotUsedAtRoot:EnvironmentCharts.kt$modifier = modifier.width(dp)
- ModifierNotUsedAtRoot:EnvironmentCharts.kt$modifier.width(dp)
- ModifierNotUsedAtRoot:PaxMetrics.kt$modifier.width(dp)
- ModifierNotUsedAtRoot:PowerMetrics.kt$modifier = modifier.weight(weight = Y_AXIS_WEIGHT)
- ModifierNotUsedAtRoot:PowerMetrics.kt$modifier = modifier.width(dp)
- ModifierNotUsedAtRoot:PowerMetrics.kt$modifier.width(dp)
- ModifierNotUsedAtRoot:SignalMetrics.kt$modifier = modifier.weight(weight = Y_AXIS_WEIGHT)
- ModifierNotUsedAtRoot:SignalMetrics.kt$modifier = modifier.width(dp)
- ModifierNotUsedAtRoot:SignalMetrics.kt$modifier.width(dp)
- ModifierNotUsedAtRoot:TracerouteLog.kt$modifier = modifier.fillMaxSize().padding(innerPadding)
- 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), ) } }
- ModifierReused:DeviceMetrics.kt$HorizontalLinesOverlay( modifier.width(dp), lineColors = listOf(graphColor, Color.Yellow, Color.Red, graphColor, graphColor), )
- ModifierReused:DeviceMetrics.kt$TimeAxisOverlay(modifier.width(dp), oldest = oldest.time, newest = newest.time, selectedTime.lineInterval())
- 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, ) }
- ModifierReused:EnvironmentCharts.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor })
- ModifierReused:EnvironmentCharts.kt$MetricPlottingCanvas( modifier = modifier.width(dp), telemetries = telemetries, graphData = graphData, selectedTime = selectedTime, oldest = oldest, timeDiff = timeDiff, rightMin = rightMin, rightMax = rightMax, )
- ModifierReused:EnvironmentCharts.kt$TimeAxisOverlay(modifier = modifier.width(dp), oldest = oldest, newest = newest, selectedTime.lineInterval())
- ModifierReused:PaxMetrics.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { Color.LightGray })
- 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, ) }
- ModifierReused:PaxMetrics.kt$TimeAxisOverlay(modifier.width(dp), oldest = minTime, newest = maxTime, timeFrame.lineInterval())
- 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), ) } }
- ModifierReused:PowerMetrics.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor })
- ModifierReused:PowerMetrics.kt$TimeAxisOverlay(modifier.width(dp), oldest = oldest.time, newest = newest.time, selectedTime.lineInterval())
- ModifierReused:PowerMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Power.CURRENT.color, minValue = Power.CURRENT.min, maxValue = Power.CURRENT.max, )
- ModifierReused:PowerMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), VOLTAGE_COLOR, minValue = voltageMin, maxValue = voltageMax, )
- 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, ) } }
- ModifierReused:SignalMetrics.kt$HorizontalLinesOverlay(modifier.width(dp), lineColors = List(size = 5) { graphColor })
- ModifierReused:SignalMetrics.kt$TimeAxisOverlay( modifier.width(dp), oldest = oldest.rxTime, newest = newest.rxTime, selectedTime.lineInterval(), )
- ModifierReused:SignalMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Metric.RSSI.color, minValue = Metric.RSSI.min, maxValue = Metric.RSSI.max, )
- ModifierReused:SignalMetrics.kt$YAxisLabels( modifier = modifier.weight(weight = Y_AXIS_WEIGHT), Metric.SNR.color, minValue = Metric.SNR.min, maxValue = Metric.SNR.max, )
- ModifierWithoutDefault:CommonCharts.kt$modifier
- ModifierWithoutDefault:EnvironmentCharts.kt$modifier
- MultipleEmitters:CommonCharts.kt$LegendLabel
- MultipleEmitters:DeviceMetrics.kt$DeviceMetricsChart
- MultipleEmitters:EnvironmentCharts.kt$EnvironmentMetricsChart
- MultipleEmitters:NodeDetailsSection.kt$MainNodeDetails
- MultipleEmitters:PaxMetrics.kt$PaxMetricsChart
- MultipleEmitters:PowerMetrics.kt$PowerMetricsChart
- MultipleEmitters:RemoteDeviceActions.kt$RemoteDeviceActions
- MultipleEmitters:SignalMetrics.kt$SignalMetricsChart
- ParameterNaming:NodeFilterTextField.kt$onToggleShowIgnored
- PreviewPublic:NodeItem.kt$NodeInfoPreview
- PreviewPublic:NodeItem.kt$NodeInfoSimplePreview
diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt
index ca6feba6d..a909bd4bc 100644
--- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt
+++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/FirmwareReleaseSheetContent.kt
@@ -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),
diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LinkedCoordinatesItem.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LinkedCoordinatesItem.kt
index f3640e868..edc0e5a0d 100644
--- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LinkedCoordinatesItem.kt
+++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/component/LinkedCoordinatesItem.kt
@@ -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 = {
diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt
index a9a28055b..f0816eea7 100644
--- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt
+++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/detail/NodeDetailViewModel.kt
@@ -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}" }
}
}
}
diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeActions.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeActions.kt
index 85b7dfbd2..a5a91601e 100644
--- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeActions.kt
+++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/list/NodeActions.kt
@@ -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}" }
}
}
}
diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/HardwareModelExtensions.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/HardwareModelExtensions.kt
index 754191ffd..3dde4ef13 100644
--- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/HardwareModelExtensions.kt
+++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/HardwareModelExtensions.kt
@@ -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
}
diff --git a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/MetricsViewModel.kt b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/MetricsViewModel.kt
index fc07fe74f..5e0713f00 100644
--- a/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/MetricsViewModel.kt
+++ b/feature/node/src/main/kotlin/org/meshtastic/feature/node/metrics/MetricsViewModel.kt
@@ -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" }
}
}
}
diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts
index 7246ee0d4..06d56edd0 100644
--- a/feature/settings/build.gradle.kts
+++ b/feature/settings/build.gradle.kts
@@ -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)
diff --git a/feature/settings/detekt-baseline.xml b/feature/settings/detekt-baseline.xml
index 809b837cf..59df3d54a 100644
--- a/feature/settings/detekt-baseline.xml
+++ b/feature/settings/detekt-baseline.xml
@@ -2,28 +2,18 @@
- ComposableParamOrder:ChannelConfigScreen.kt$ChannelConfigScreen
- ComposableParamOrder:Debug.kt$DecodedPayloadBlock
- ComposableParamOrder:DebugSearch.kt$DebugSearchState
- ComposableParamOrder:DebugSearch.kt$DebugSearchStateviewModelDefaults
- ComposableParamOrder:MapReportingPreference.kt$MapReportingPreference
- ComposableParamOrder:NodeActionButton.kt$NodeActionButton
- ComposableParamOrder:WarningDialog.kt$WarningDialog
CyclomaticComplexMethod:NetworkConfigItemList.kt$@Composable fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
CyclomaticComplexMethod:PositionConfigItemList.kt$@OptIn(ExperimentalPermissionsApi::class) @Composable fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
CyclomaticComplexMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)
- LambdaParameterEventTrailing:NodeActionButton.kt$onClick
LongMethod:AudioConfigItemList.kt$@Composable fun AudioConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
LongMethod:CannedMessageConfigItemList.kt$@Composable fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
LongMethod:DetectionSensorConfigItemList.kt$@Composable fun DetectionSensorConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
LongMethod:DeviceConfigItemList.kt$@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun DeviceConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
LongMethod:DisplayConfigItemList.kt$@Composable fun DisplayConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
- LongMethod:ExternalNotificationConfigItemList.kt$@Composable fun ExternalNotificationConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
LongMethod:LoRaConfigItemList.kt$@Composable fun LoRaConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit)
LongMethod:NetworkConfigItemList.kt$@Composable fun NetworkConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
LongMethod:PositionConfigItemList.kt$@OptIn(ExperimentalPermissionsApi::class) @Composable fun PositionConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
LongMethod:PowerConfigItemList.kt$@Composable fun PowerConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
- 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, )
LongMethod:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)
LongMethod:SecurityConfigItemList.kt$@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun SecurityConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
LongMethod:SerialConfigItemList.kt$@Composable fun SerialConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel(), onBack: () -> Unit)
@@ -34,35 +24,10 @@
MagicNumber:EditChannelDialog.kt$16
MagicNumber:EditChannelDialog.kt$32
MagicNumber:PacketResponseStateDialog.kt$100
- ModifierMissing:CleanNodeDatabaseScreen.kt$CleanNodeDatabaseScreen
- ModifierMissing:Debug.kt$DebugScreen
- ModifierMissing:DeviceConfigItemList.kt$DeviceConfigScreen
- ModifierMissing:MapReportingPreference.kt$MapReportingPreference
- ModifierMissing:NetworkConfigItemList.kt$NetworkConfigScreen
- ModifierMissing:PositionConfigItemList.kt$PositionConfigScreen
- ModifierMissing:RadioConfig.kt$RadioConfigItemList
- ModifierMissing:RadioConfigScreenList.kt$RadioConfigScreenList
- ModifierMissing:SecurityConfigItemList.kt$SecurityConfigScreen
- ModifierMissing:SettingsScreen.kt$SettingsScreen
- ModifierNotUsedAtRoot:EditChannelDialog.kt$modifier = modifier.weight(1f)
- ModifierNotUsedAtRoot:EditDeviceProfileDialog.kt$modifier = modifier.weight(1f)
- MultipleEmitters:CleanNodeDatabaseScreen.kt$NodesDeletionPreview
- MultipleEmitters:RadioConfig.kt$RadioConfigItemList
NestedBlockDepth:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)
- ParameterNaming:ChannelConfigScreen.kt$onPositiveClicked
- ParameterNaming:CleanNodeDatabaseScreen.kt$onCheckedChanged
- ParameterNaming:CleanNodeDatabaseScreen.kt$onDaysChanged
- ParameterNaming:MapReportingPreference.kt$onMapReportingEnabledChanged
- ParameterNaming:MapReportingPreference.kt$onPositionPrecisionChanged
- ParameterNaming:MapReportingPreference.kt$onPublishIntervalSecsChanged
- ParameterNaming:MapReportingPreference.kt$onShouldReportLocationChanged
- PreviewPublic:MapReportingPreference.kt$MapReportingPreview
ReturnCount:RadioConfigViewModel.kt$RadioConfigViewModel$private fun processPacketResponse(packet: MeshProtos.MeshPacket)
TooGenericExceptionCaught:LanguageUtils.kt$LanguageUtils$e: Exception
TooGenericExceptionCaught:RadioConfigViewModel.kt$RadioConfigViewModel$ex: Exception
TooManyFunctions:RadioConfigViewModel.kt$RadioConfigViewModel : ViewModel
- UnusedParameter:ChannelConfigScreen.kt$onBack: () -> Unit
- UnusedParameter:ChannelConfigScreen.kt$title: String
- ViewModelInjection:DebugSearch.kt$viewModel
diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsViewModel.kt
index 51fdc435e..c03d9afb7 100644
--- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsViewModel.kt
+++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/SettingsViewModel.kt
@@ -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}" }
}
}
}
diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt
index ac0126f43..509041c9d 100644
--- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt
+++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt
@@ -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" }
}
}
diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModel.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModel.kt
index b7a93c690..48050b229 100644
--- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModel.kt
+++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/debugging/DebugViewModel.kt
@@ -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) = databaseLogs
diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt
index 3b2bfeb4c..a1c520bfc 100644
--- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt
+++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/RadioConfigViewModel.kt
@@ -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
@@ -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 }) {
diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ExternalNotificationConfigItemList.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ExternalNotificationConfigItemList.kt
index e02fa7b4c..39b736aa0 100644
--- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ExternalNotificationConfigItemList.kt
+++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/radio/component/ExternalNotificationConfigItemList.kt
@@ -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,
diff --git a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/LanguageUtils.kt b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/LanguageUtils.kt
index c8822ea88..445044ffc 100644
--- a/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/LanguageUtils.kt
+++ b/feature/settings/src/main/kotlin/org/meshtastic/feature/settings/util/LanguageUtils.kt
@@ -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}" }
}
}
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index ac0e4acbd..128b69366 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -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" }