Phil Oliver 2025-10-03 20:57:07 +00:00 zatwierdzone przez GitHub
commit 1ca7cea350
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
28 zmienionych plików z 145 dodań i 146 usunięć

Wyświetl plik

@ -331,7 +331,6 @@
<ID>UnusedParameter:ChannelSettingsItemList.kt$onBack: () -> Unit</ID>
<ID>UnusedParameter:ChannelSettingsItemList.kt$title: String</ID>
<ID>UtilityClassWithPublicConstructor:NetworkRepositoryModule.kt$NetworkRepositoryModule</ID>
<ID>ViewModelForwarding:Main.kt$ScannedQrCodeDialog(uIViewModel, newChannelSet)</ID>
<ID>ViewModelForwarding:Main.kt$VersionChecks(uIViewModel)</ID>
<ID>ViewModelInjection:DebugSearch.kt$viewModel</ID>
<ID>Wrapping:Message.kt${ event -> when (event) { is MessageScreenEvent.SendMessage -> { viewModel.sendMessage(event.text, contactKey, event.replyingToPacketId) if (event.replyingToPacketId != null) replyingToPacketId = null messageInputState.clearText() } is MessageScreenEvent.SendReaction -> viewModel.sendReaction(event.emoji, event.messageId, contactKey) is MessageScreenEvent.DeleteMessages -> { viewModel.deleteMessages(event.ids) selectedMessageIds.value = emptySet() showDeleteDialog = false } is MessageScreenEvent.ClearUnreadCount -> viewModel.clearUnreadCount(contactKey, event.lastReadMessageId) is MessageScreenEvent.NodeDetails -> navigateToNodeDetails(event.node.num) is MessageScreenEvent.SetTitle -> viewModel.setTitle(event.title) is MessageScreenEvent.NavigateToMessages -> navigateToMessages(event.contactKey) is MessageScreenEvent.NavigateToNodeDetails -> navigateToNodeDetails(event.nodeNum) MessageScreenEvent.NavigateBack -> onNavigateBack() is MessageScreenEvent.CopyToClipboard -> { clipboardManager.nativeClipboard.setPrimaryClip(ClipData.newPlainText(event.text, event.text)) selectedMessageIds.value = emptySet() } } }</ID>

Wyświetl plik

@ -27,12 +27,12 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.ui.map.NodeMapViewModel
import com.geeksville.mesh.ui.map.rememberMapViewWithLifecycle
import org.meshtastic.feature.map.addCopyright
import org.meshtastic.feature.map.addPolyline
import org.meshtastic.feature.map.addPositionMarkers
import org.meshtastic.feature.map.addScaleBarOverlay
import org.meshtastic.feature.map.node.NodeMapViewModel
import org.meshtastic.feature.map.rememberMapViewWithLifecycle
import org.osmdroid.util.BoundingBox
import org.osmdroid.util.GeoPoint

Wyświetl plik

@ -27,11 +27,9 @@ import androidx.compose.ui.Modifier
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.model.MetricsViewModel
import com.geeksville.mesh.ui.map.MapView
import com.geeksville.mesh.ui.map.NodeMapViewModel
import org.meshtastic.core.ui.component.MainAppBar
const val DEG_D = 1e-7
import org.meshtastic.feature.map.MapView
import org.meshtastic.feature.map.node.NodeMapViewModel
@Composable
fun NodeMapScreen(

Wyświetl plik

@ -1,35 +0,0 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.android
import android.app.Activity
import android.content.Context
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
// / show a toast
fun Context.toast(message: CharSequence) = Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
// / Utility function to hide the soft keyboard per stack overflow
fun Activity.hideKeyboard() {
// Check if no view has focus:
currentFocus?.let { v ->
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
imm?.hideSoftInputFromWindow(v.windowToken, 0)
}
}

Wyświetl plik

@ -21,10 +21,10 @@ import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import com.geeksville.mesh.ui.map.MapScreen
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
import org.meshtastic.core.navigation.MapRoutes
import org.meshtastic.core.navigation.NodesRoutes
import org.meshtastic.feature.map.MapScreen
fun NavGraphBuilder.mapGraph(navController: NavHostController) {
composable<MapRoutes.Map>(deepLinks = listOf(navDeepLink<MapRoutes.Map>(basePath = "$DEEP_LINK_BASE_URI/map"))) {

Wyświetl plik

@ -28,7 +28,6 @@ import androidx.annotation.RequiresPermission
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.coroutineScope
import com.geeksville.mesh.CoroutineDispatchers
import com.geeksville.mesh.android.hasBluetoothPermission
import com.geeksville.mesh.util.registerReceiverCompat
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@ -37,6 +36,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import org.meshtastic.core.common.hasBluetoothPermission
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton

Wyświetl plik

@ -49,7 +49,6 @@ import com.geeksville.mesh.StoreAndForwardProtos
import com.geeksville.mesh.TelemetryProtos
import com.geeksville.mesh.TelemetryProtos.LocalStats
import com.geeksville.mesh.XmodemProtos
import com.geeksville.mesh.android.hasLocationPermission
import com.geeksville.mesh.concurrent.handledLaunch
import com.geeksville.mesh.copy
import com.geeksville.mesh.fromRadio
@ -78,6 +77,7 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.meshtastic.core.analytics.DataPair
import org.meshtastic.core.common.hasLocationPermission
import org.meshtastic.core.data.repository.MeshLogRepository
import org.meshtastic.core.data.repository.NodeRepository
import org.meshtastic.core.data.repository.PacketRepository

Wyświetl plik

@ -67,11 +67,11 @@ import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.model.MetricsViewModel
import org.meshtastic.core.model.util.metersIn
import org.meshtastic.core.model.util.toString
import org.meshtastic.core.proto.formatPositionTime
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.theme.AppTheme
import java.text.DateFormat
import kotlin.time.Duration.Companion.days
@Composable
private fun RowScope.PositionText(text: String, weight: Float) {
@ -106,7 +106,6 @@ private fun HeaderItem(compactWidth: Boolean) {
const val DEG_D = 1e-7
const val HEADING_DEG = 1e-5
private const val SECONDS_TO_MILLIS = 1000L
@Composable
fun PositionItem(compactWidth: Boolean, position: MeshProtos.Position, dateFormat: DateFormat, system: DisplayUnits) {
@ -122,24 +121,10 @@ fun PositionItem(compactWidth: Boolean, position: MeshProtos.Position, dateForma
PositionText("${position.groundSpeed} Km/h", WEIGHT_15)
PositionText("%.0f°".format(position.groundTrack * HEADING_DEG), WEIGHT_15)
}
PositionText(formatPositionTime(position, dateFormat), WEIGHT_40)
PositionText(position.formatPositionTime(dateFormat), WEIGHT_40)
}
}
@Composable
fun formatPositionTime(position: MeshProtos.Position, dateFormat: DateFormat): String {
val currentTime = System.currentTimeMillis()
val sixMonthsAgo = currentTime - 180.days.inWholeMilliseconds
val isOlderThanSixMonths = position.time * SECONDS_TO_MILLIS < sixMonthsAgo
val timeText =
if (isOlderThanSixMonths) {
stringResource(id = R.string.unknown_age)
} else {
dateFormat.format(position.time * SECONDS_TO_MILLIS)
}
return timeText
}
@Composable
private fun ActionButtons(
clearButtonEnabled: Boolean,

Wyświetl plik

@ -60,7 +60,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.BuildConfig
import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
import com.geeksville.mesh.android.gpsDisabled
import com.geeksville.mesh.navigation.getNavRouteFrom
import com.geeksville.mesh.ui.settings.radio.RadioConfigItemList
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
@ -71,6 +70,7 @@ import com.geeksville.mesh.util.LanguageUtils.getLanguageMap
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberMultiplePermissionsState
import kotlinx.coroutines.delay
import org.meshtastic.core.common.gpsDisabled
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MainAppBar

Wyświetl plik

@ -22,4 +22,4 @@ plugins {
android { namespace = "org.meshtastic.core.common" }
dependencies {}
dependencies { implementation(libs.core.ktx) }

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.android
package org.meshtastic.core.common
import android.Manifest
import android.content.Context

Wyświetl plik

@ -34,6 +34,7 @@
plugins {
alias(libs.plugins.meshtastic.android.library)
alias(libs.plugins.meshtastic.android.library.compose)
alias(libs.plugins.protobuf)
}
@ -53,6 +54,8 @@ protobuf {
}
dependencies {
implementation(projects.core.strings)
// This needs to be API for consuming modules
api(libs.protobuf.kotlin)
}

Wyświetl plik

@ -0,0 +1,40 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.core.proto
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import com.geeksville.mesh.MeshProtos
import java.text.DateFormat
import kotlin.time.Duration.Companion.days
private const val SECONDS_TO_MILLIS = 1000L
@Composable
fun MeshProtos.Position.formatPositionTime(dateFormat: DateFormat): String {
val currentTime = System.currentTimeMillis()
val sixMonthsAgo = currentTime - 180.days.inWholeMilliseconds
val isOlderThanSixMonths = time * SECONDS_TO_MILLIS < sixMonthsAgo
val timeText =
if (isOlderThanSixMonths) {
stringResource(id = org.meshtastic.core.strings.R.string.unknown_age)
} else {
dateFormat.format(time * SECONDS_TO_MILLIS)
}
return timeText
}

Wyświetl plik

@ -41,6 +41,7 @@ dependencies {
implementation(libs.bundles.osm)
googleImplementation(libs.bundles.maps.compose)
implementation(libs.accompanist.permissions)
implementation(libs.annotation)
implementation(libs.timber)
}

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.map
package org.meshtastic.feature.map
import android.Manifest // Added for Accompanist
import android.content.Context
@ -63,31 +63,25 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.MeshProtos.Waypoint
import com.geeksville.mesh.android.gpsDisabled
import com.geeksville.mesh.android.hasGps
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.map.components.EditWaypointDialog
import com.geeksville.mesh.util.SqlTileWriterExt
import com.geeksville.mesh.waypoint
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
import org.meshtastic.core.common.gpsDisabled
import org.meshtastic.core.common.hasGps
import org.meshtastic.core.database.entity.Packet
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.DataPacket
import org.meshtastic.core.model.util.formatAgo
import org.meshtastic.core.strings.R
import org.meshtastic.feature.map.MapViewModel
import org.meshtastic.feature.map.addCopyright
import org.meshtastic.feature.map.addScaleBarOverlay
import org.meshtastic.feature.map.cluster.RadiusMarkerClusterer
import org.meshtastic.feature.map.component.CacheLayout
import org.meshtastic.feature.map.component.DownloadButton
import org.meshtastic.feature.map.component.EditWaypointDialog
import org.meshtastic.feature.map.component.MapButton
import org.meshtastic.feature.map.createLatLongGrid
import org.meshtastic.feature.map.model.CustomTileSource
import org.meshtastic.feature.map.model.MarkerWithLabel
import org.meshtastic.feature.map.zoomIn
import org.osmdroid.bonuspack.utils.BonusPackHelper.getBitmapFromVectorDrawable
import org.osmdroid.config.Configuration
import org.osmdroid.events.MapEventsReceiver
@ -277,10 +271,11 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
MyLocationNewOverlay(this).apply {
enableMyLocation()
enableFollowLocation()
getBitmapFromVectorDrawable(context, com.geeksville.mesh.R.drawable.ic_map_location_dot_24)?.let {
setPersonIcon(it)
setPersonAnchor(0.5f, 0.5f)
}
getBitmapFromVectorDrawable(context, org.meshtastic.core.ui.R.drawable.ic_map_location_dot_24)
?.let {
setPersonIcon(it)
setPersonAnchor(0.5f, 0.5f)
}
getBitmapFromVectorDrawable(context, org.meshtastic.core.ui.R.drawable.ic_map_navigation_24)?.let {
setDirectionIcon(it)
setDirectionAnchor(0.5f, 0.5f)
@ -309,7 +304,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
val waypoints by mapViewModel.waypoints.collectAsStateWithLifecycle(emptyMap())
val markerIcon = remember {
AppCompatResources.getDrawable(context, com.geeksville.mesh.R.drawable.ic_baseline_location_on_24)
AppCompatResources.getDrawable(context, org.meshtastic.core.ui.R.drawable.ic_baseline_location_on_24)
}
fun MapView.onNodesChanged(nodes: Collection<Node>): List<MarkerWithLabel> {

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.map
package org.meshtastic.feature.map
import android.annotation.SuppressLint
import android.content.Context
@ -33,7 +33,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import org.meshtastic.feature.map.requiredZoomLevel
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.tilesource.ITileSource
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
@ -72,7 +71,7 @@ private const val DEFAULT_ZOOM_LEVEL = 15.0
@Suppress("MagicNumber")
@Composable
internal fun rememberMapViewWithLifecycle(
fun rememberMapViewWithLifecycle(
applicationId: String,
box: BoundingBox,
tileSource: ITileSource = TileSourceFactory.DEFAULT_TILE_SOURCE,

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.util
package org.meshtastic.feature.map
import android.database.Cursor
import org.osmdroid.tileprovider.modules.DatabaseFileArchive

Wyświetl plik

@ -15,9 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.map.components
package org.meshtastic.feature.map.component
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.text.format.DateFormat
import android.widget.DatePicker
import android.widget.TimePicker
import androidx.compose.foundation.Image
@ -76,7 +78,7 @@ import java.util.Locale
@Suppress("LongMethod")
@OptIn(ExperimentalLayoutApi::class)
@Composable
internal fun EditWaypointDialog(
fun EditWaypointDialog(
waypoint: Waypoint,
onSendClicked: (Waypoint) -> Unit,
onDeleteClicked: (Waypoint) -> Unit,

Wyświetl plik

@ -17,7 +17,7 @@
@file:Suppress("MagicNumber")
package com.geeksville.mesh.ui.map
package org.meshtastic.feature.map
import android.app.Activity
import android.content.Intent
@ -67,13 +67,6 @@ import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig.DisplayUnits
import com.geeksville.mesh.MeshProtos.Position
import com.geeksville.mesh.MeshProtos.Waypoint
import com.geeksville.mesh.copy
import com.geeksville.mesh.ui.map.components.ClusterItemsListDialog
import com.geeksville.mesh.ui.map.components.EditWaypointDialog
import com.geeksville.mesh.ui.map.components.NodeClusterMarkers
import com.geeksville.mesh.ui.map.components.WaypointMarkers
import com.geeksville.mesh.ui.metrics.HEADING_DEG
import com.geeksville.mesh.ui.metrics.formatPositionTime
import com.geeksville.mesh.ui.node.DEG_D
import com.geeksville.mesh.waypoint
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
@ -87,7 +80,6 @@ import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.JointType
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.LatLngBounds
import com.google.maps.android.clustering.ClusterItem
import com.google.maps.android.compose.ComposeMapColorScheme
import com.google.maps.android.compose.GoogleMap
import com.google.maps.android.compose.MapEffect
@ -110,19 +102,23 @@ import org.meshtastic.core.model.util.metersIn
import org.meshtastic.core.model.util.mpsToKmph
import org.meshtastic.core.model.util.mpsToMph
import org.meshtastic.core.model.util.toString
import org.meshtastic.core.proto.formatPositionTime
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.feature.map.LastHeardFilter
import org.meshtastic.feature.map.LayerType
import org.meshtastic.feature.map.LocationPermissionsHandler
import org.meshtastic.feature.map.MapViewModel
import org.meshtastic.feature.map.component.ClusterItemsListDialog
import org.meshtastic.feature.map.component.CustomMapLayersSheet
import org.meshtastic.feature.map.component.CustomTileProviderManagerSheet
import org.meshtastic.feature.map.component.EditWaypointDialog
import org.meshtastic.feature.map.component.MapControlsOverlay
import org.meshtastic.feature.map.component.NodeClusterMarkers
import org.meshtastic.feature.map.component.WaypointMarkers
import org.meshtastic.feature.map.model.NodeClusterItem
import timber.log.Timber
import java.text.DateFormat
private const val MIN_TRACK_POINT_DISTANCE_METERS = 20f
private const val DEG_D = 1e-7
private const val HEADING_DEG = 1e-5
@Suppress("CyclomaticComplexMethod", "LongMethod")
@OptIn(MapsComposeExperimentalApi::class, ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@ -651,34 +647,6 @@ fun Uri.getFileName(context: android.content.Context): String {
return name
}
data class NodeClusterItem(val node: Node, val nodePosition: LatLng, val nodeTitle: String, val nodeSnippet: String) :
ClusterItem {
override fun getPosition(): LatLng = nodePosition
override fun getTitle(): String = nodeTitle
override fun getSnippet(): String = nodeSnippet
override fun getZIndex(): Float? = null
fun getPrecisionMeters(): Double? {
val precisionMap =
mapOf(
10 to 23345.484932,
11 to 11672.7369,
12 to 5836.36288,
13 to 2918.175876,
14 to 1459.0823719999053,
15 to 729.53562,
16 to 364.7622,
17 to 182.375556,
18 to 91.182212,
19 to 45.58554,
)
return precisionMap[this.node.position.precisionBits]
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
@Suppress("LongMethod")
@ -698,15 +666,9 @@ private fun PositionInfoWindowContent(
Card {
Column(modifier = Modifier.padding(8.dp)) {
PositionRow(
label = stringResource(R.string.latitude),
value = "%.5f".format(position.latitudeI * com.geeksville.mesh.ui.metrics.DEG_D),
)
PositionRow(label = stringResource(R.string.latitude), value = "%.5f".format(position.latitudeI * DEG_D))
PositionRow(
label = stringResource(R.string.longitude),
value = "%.5f".format(position.longitudeI * com.geeksville.mesh.ui.metrics.DEG_D),
)
PositionRow(label = stringResource(R.string.longitude), value = "%.5f".format(position.longitudeI * DEG_D))
PositionRow(label = stringResource(R.string.sats), value = position.satsInView.toString())
@ -722,7 +684,7 @@ private fun PositionInfoWindowContent(
value = "%.0f°".format(position.groundTrack * HEADING_DEG),
)
PositionRow(label = stringResource(R.string.timestamp), value = formatPositionTime(position, dateFormat))
PositionRow(label = stringResource(R.string.timestamp), value = position.formatPositionTime(dateFormat))
}
}
}

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.map.components
package org.meshtastic.feature.map.component
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.PaddingValues
@ -31,9 +31,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.ui.map.NodeClusterItem
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.feature.map.model.NodeClusterItem
@Composable
fun ClusterItemsListDialog(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.map.components
package org.meshtastic.feature.map.component
import android.app.DatePickerDialog
import android.app.TimePickerDialog

Wyświetl plik

@ -15,12 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.map.components
package org.meshtastic.feature.map.component
import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.ui.graphics.Color
import com.geeksville.mesh.ui.map.NodeClusterItem
import com.google.maps.android.clustering.Cluster
import com.google.maps.android.clustering.view.DefaultClusterRenderer
import com.google.maps.android.compose.Circle
@ -28,6 +27,7 @@ import com.google.maps.android.compose.MapsComposeExperimentalApi
import com.google.maps.android.compose.clustering.Clustering
import org.meshtastic.core.ui.component.NodeChip
import org.meshtastic.feature.map.BaseMapViewModel
import org.meshtastic.feature.map.model.NodeClusterItem
@OptIn(MapsComposeExperimentalApi::class)
@Suppress("NestedBlockDepth")

Wyświetl plik

@ -15,13 +15,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.map.components
package org.meshtastic.feature.map.component
import android.widget.Toast
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.ui.node.DEG_D
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.LatLng
import com.google.maps.android.compose.Marker
@ -29,6 +28,8 @@ import com.google.maps.android.compose.rememberUpdatedMarkerState
import org.meshtastic.core.strings.R
import org.meshtastic.feature.map.BaseMapViewModel
private const val DEG_D = 1e-7
@Composable
fun WaypointMarkers(
displayableWaypoints: List<MeshProtos.Waypoint>,

Wyświetl plik

@ -0,0 +1,50 @@
/*
* Copyright (c) 2025 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.feature.map.model
import com.google.android.gms.maps.model.LatLng
import com.google.maps.android.clustering.ClusterItem
import org.meshtastic.core.database.model.Node
data class NodeClusterItem(val node: Node, val nodePosition: LatLng, val nodeTitle: String, val nodeSnippet: String) :
ClusterItem {
override fun getPosition(): LatLng = nodePosition
override fun getTitle(): String = nodeTitle
override fun getSnippet(): String = nodeSnippet
override fun getZIndex(): Float? = null
fun getPrecisionMeters(): Double? {
val precisionMap =
mapOf(
10 to 23345.484932,
11 to 11672.7369,
12 to 5836.36288,
13 to 2918.175876,
14 to 1459.0823719999053,
15 to 729.53562,
16 to 364.7622,
17 to 182.375556,
18 to 91.182212,
19 to 45.58554,
)
return precisionMap[this.node.position.precisionBits]
}
}

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.map
package org.meshtastic.feature.map
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
@ -28,7 +28,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.feature.map.MapViewModel
@Composable
fun MapScreen(

Wyświetl plik

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.geeksville.mesh.ui.map
package org.meshtastic.feature.map.node
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel