Inject PlatformAnalytics directly (#3358)

This commit is contained in:
Phil Oliver
2025-10-06 14:30:18 -04:00
committed by GitHub
parent 95ec4877df
commit 1fb79d8887
7 changed files with 29 additions and 30 deletions

View File

@@ -8,6 +8,7 @@
<ID>CommentWrapping:SignalMetrics.kt$Metric.SNR$/* Selected 12 as the max to get 4 equal vertical sections. */</ID>
<ID>ComposableNaming:NodeDetailScreen.kt$notesSection</ID>
<ID>ComposableParamOrder:Channel.kt$ChannelScreen</ID>
<ID>ComposableParamOrder:Channel.kt$EditChannelUrl</ID>
<ID>ComposableParamOrder:DeviceMetrics.kt$DeviceMetricsChart</ID>
<ID>ComposableParamOrder:EmptyStateContent.kt$EmptyStateContent</ID>
<ID>ComposableParamOrder:EnvironmentCharts.kt$ChartContent</ID>

View File

@@ -19,9 +19,7 @@ package com.geeksville.mesh
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
import org.meshtastic.core.analytics.platform.PlatformAnalytics
import timber.log.Timber
import javax.inject.Inject
/**
* The main application class for Meshtastic.
@@ -30,26 +28,7 @@ import javax.inject.Inject
* application components, including analytics and platform-specific helpers, and manages analytics consent based on
* user preferences.
*/
@HiltAndroidApp
class MeshUtilApplication : Application() {
@Inject lateinit var platformAnalytics: PlatformAnalytics
companion object {
/**
* Provides access to the platform-specific analytics provider. Initialized via the injected [PlatformAnalytics]
* during [onCreate].
*/
lateinit var analytics: PlatformAnalytics
private set
}
override fun onCreate() {
super.onCreate()
// Initialize platform-specific features (analytics, crash reporting, etc.)
analytics = platformAnalytics
}
}
@HiltAndroidApp class MeshUtilApplication : Application()
fun logAssert(executeReliableWrite: Boolean) {
if (!executeReliableWrite) {

View File

@@ -27,6 +27,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import com.geeksville.mesh.AdminProtos
import com.geeksville.mesh.AppOnlyProtos
import com.geeksville.mesh.ConfigProtos.Config
@@ -55,6 +56,7 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.meshtastic.core.analytics.platform.PlatformAnalytics
import org.meshtastic.core.data.repository.FirmwareReleaseRepository
import org.meshtastic.core.data.repository.MeshLogRepository
import org.meshtastic.core.data.repository.NodeRepository
@@ -130,6 +132,7 @@ constructor(
firmwareReleaseRepository: FirmwareReleaseRepository,
private val uiPreferencesDataSource: UiPreferencesDataSource,
private val meshServiceNotifications: MeshServiceNotifications,
private val analytics: PlatformAnalytics,
) : ViewModel() {
val theme: StateFlow<Int> = uiPreferencesDataSource.theme
@@ -351,4 +354,8 @@ constructor(
fun onAppIntroCompleted() {
uiPreferencesDataSource.setAppIntroCompleted(true)
}
fun addNavigationTrackingEffect(navController: NavHostController) {
analytics.addNavigationTrackingEffect(navController)
}
}

View File

@@ -24,7 +24,6 @@ import androidx.lifecycle.coroutineScope
import com.geeksville.mesh.BuildConfig
import com.geeksville.mesh.CoroutineDispatchers
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.MeshUtilApplication
import com.geeksville.mesh.android.BinaryLogFile
import com.geeksville.mesh.android.BuildUtils
import com.geeksville.mesh.concurrent.handledLaunch
@@ -46,6 +45,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.meshtastic.core.analytics.platform.PlatformAnalytics
import org.meshtastic.core.model.util.anonymize
import org.meshtastic.core.prefs.radio.RadioPrefs
import org.meshtastic.core.service.ConnectionState
@@ -74,6 +74,7 @@ constructor(
private val processLifecycle: Lifecycle,
private val radioPrefs: RadioPrefs,
private val interfaceFactory: InterfaceFactory,
private val analytics: PlatformAnalytics,
) {
private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED)
@@ -307,7 +308,7 @@ constructor(
false
} else {
// Record that this use has configured a new radio
MeshUtilApplication.analytics.track("mesh_bond")
analytics.track("mesh_bond")
// Ignore any errors that happen while closing old device
ignoreException { stopInterface() }

View File

@@ -75,7 +75,6 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.geeksville.mesh.BuildConfig
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.MeshUtilApplication
import com.geeksville.mesh.model.BTScanModel
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.navigation.channelsGraph
@@ -158,7 +157,7 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanMode
}
}
MeshUtilApplication.analytics.addNavigationTrackingEffect(navController = navController)
uIViewModel.addNavigationTrackingEffect(navController)
VersionChecks(uIViewModel)
val alertDialogState by uIViewModel.currentAlert.collectAsStateWithLifecycle()

View File

@@ -92,7 +92,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
import com.geeksville.mesh.ChannelProtos
import com.geeksville.mesh.ConfigProtos
import com.geeksville.mesh.MeshUtilApplication.Companion.analytics
import com.geeksville.mesh.channelSet
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.common.components.ScannedQrCodeDialog
@@ -102,7 +101,6 @@ import com.google.accompanist.permissions.rememberPermissionState
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions
import kotlinx.coroutines.launch
import org.meshtastic.core.analytics.DataPair
import org.meshtastic.core.model.Channel
import org.meshtastic.core.model.util.getChannelUrl
import org.meshtastic.core.model.util.qrCode
@@ -311,6 +309,7 @@ fun ChannelScreen(
EditChannelUrl(
enabled = enabled,
channelUrl = selectedChannelSet.getChannelUrl(shouldAdd = shouldAddChannelsState),
onTrackShare = viewModel::trackShare,
onConfirm = {
viewModel.requestChannelUrl(it) {
Toast.makeText(context, R.string.channel_invalid, Toast.LENGTH_SHORT).show()
@@ -368,7 +367,13 @@ fun ChannelScreen(
@Suppress("LongMethod")
@Composable
private fun EditChannelUrl(enabled: Boolean, channelUrl: Uri, modifier: Modifier = Modifier, onConfirm: (Uri) -> Unit) {
private fun EditChannelUrl(
enabled: Boolean,
channelUrl: Uri,
modifier: Modifier = Modifier,
onTrackShare: () -> Unit,
onConfirm: (Uri) -> Unit,
) {
val focusManager = LocalFocusManager.current
val clipboardManager = LocalClipboard.current
val coroutineScope = rememberCoroutineScope()
@@ -416,7 +421,7 @@ private fun EditChannelUrl(enabled: Boolean, channelUrl: Uri, modifier: Modifier
else -> {
// track how many times users share channels
analytics.track("share", DataPair("content_type", "channel"))
onTrackShare()
coroutineScope.launch {
clipboardManager.setClipEntry(
ClipEntry(ClipData.newPlainText(label, valueState.toString())),

View File

@@ -34,6 +34,8 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.meshtastic.core.analytics.DataPair
import org.meshtastic.core.analytics.platform.PlatformAnalytics
import org.meshtastic.core.data.repository.RadioConfigRepository
import org.meshtastic.core.model.util.toChannelSet
import org.meshtastic.core.proto.getChannelList
@@ -47,6 +49,7 @@ class ChannelViewModel
constructor(
private val serviceRepository: ServiceRepository,
private val radioConfigRepository: RadioConfigRepository,
private val analytics: PlatformAnalytics,
) : ViewModel() {
val connectionState = serviceRepository.connectionState
@@ -121,6 +124,10 @@ constructor(
}
}
fun trackShare() {
analytics.track("share", DataPair("content_type", "channel"))
}
private inline fun updateLoraConfig(crossinline body: (Config.LoRaConfig) -> Config.LoRaConfig) {
val data = body(localConfig.value.lora)
setConfig(config { lora = data })