chore: remove orphaned app/ discovery files from merge

These files were duplicated in androidApp/ where they belong. The app/
module no longer exists (removed in AIDL removal PR).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
James Rich
2026-05-30 12:09:33 -05:00
parent 1e46d9b06b
commit b4518e4705
8 changed files with 1 additions and 450 deletions
@@ -1,32 +0,0 @@
/*
* Copyright (c) 2026 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.app.map.discovery
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import org.meshtastic.core.ui.util.DiscoveryMapNode
/** Flavor-unified entry point for the discovery map. OSMDroid implementation. */
@Composable
fun DiscoveryMap(
userLatitude: Double,
userLongitude: Double,
nodes: List<DiscoveryMapNode>,
modifier: Modifier = Modifier,
) {
DiscoveryOsmMap(userLatitude = userLatitude, userLongitude = userLongitude, nodes = nodes, modifier = modifier)
}
@@ -1,167 +0,0 @@
/*
* Copyright (c) 2026 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/>.
*/
@file:Suppress("MagicNumber")
package org.meshtastic.app.map.discovery
import android.graphics.Paint
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import org.meshtastic.app.map.addCopyright
import org.meshtastic.app.map.addScaleBarOverlay
import org.meshtastic.app.map.model.CustomTileSource
import org.meshtastic.app.map.rememberMapViewWithLifecycle
import org.meshtastic.app.map.zoomIn
import org.meshtastic.core.ui.theme.DiscoveryMapColors
import org.meshtastic.core.ui.util.DiscoveryMapNode
import org.meshtastic.core.ui.util.DiscoveryNeighborType
import org.osmdroid.util.BoundingBox
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.overlay.Marker
import org.osmdroid.views.overlay.Polyline
private const val SINGLE_POINT_ZOOM = 14.0
private const val ZOOM_OUT_LEVELS = 0.5
/**
* OSMDroid implementation of the discovery map. Renders discovered node markers color-coded by neighbor type (green =
* direct, blue = mesh) with polylines from the user position to direct neighbors. Auto-zooms to fit all markers.
*/
@Composable
fun DiscoveryOsmMap(
userLatitude: Double,
userLongitude: Double,
nodes: List<DiscoveryMapNode>,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val density = LocalDensity.current
val hasValidUserPosition = userLatitude != 0.0 || userLongitude != 0.0
val userGeoPoint = remember(userLatitude, userLongitude) { GeoPoint(userLatitude, userLongitude) }
val validNodes = remember(nodes) { nodes.filter { it.latitude != 0.0 || it.longitude != 0.0 } }
// Build bounding box from all points
val allGeoPoints =
remember(validNodes, hasValidUserPosition) {
buildList {
if (hasValidUserPosition) add(userGeoPoint)
validNodes.forEach { add(GeoPoint(it.latitude, it.longitude)) }
}
}
val initialBounds =
remember(allGeoPoints) {
if (allGeoPoints.isEmpty()) BoundingBox() else BoundingBox.fromGeoPoints(allGeoPoints)
}
var hasCentered by remember { mutableStateOf(false) }
val mapView =
rememberMapViewWithLifecycle(
applicationId = context.packageName,
box = initialBounds,
tileSource = CustomTileSource.getTileSource(0),
)
// Camera auto-center once
LaunchedEffect(allGeoPoints) {
if (hasCentered || allGeoPoints.isEmpty()) return@LaunchedEffect
if (allGeoPoints.size == 1) {
mapView.controller.setCenter(allGeoPoints.first())
mapView.controller.setZoom(SINGLE_POINT_ZOOM)
} else {
mapView.zoomToBoundingBox(BoundingBox.fromGeoPoints(allGeoPoints).zoomIn(-ZOOM_OUT_LEVELS), true)
}
hasCentered = true
}
AndroidView(
modifier = modifier,
factory = { mapView.apply { setDestroyMode(false) } },
update = { map ->
map.overlays.clear()
map.addCopyright()
map.addScaleBarOverlay(density)
// User position marker
if (hasValidUserPosition) {
val userMarker =
Marker(map).apply {
position = userGeoPoint
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
title = "Your Position"
icon = context.getDrawable(android.R.drawable.ic_menu_mylocation)
}
map.overlays.add(userMarker)
}
// Node markers
validNodes.forEach { node ->
val nodeGeoPoint = GeoPoint(node.latitude, node.longitude)
val marker =
Marker(map).apply {
position = nodeGeoPoint
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
title = node.longName ?: node.shortName ?: "Unknown"
snippet = "SNR: ${node.snr} dB / RSSI: ${node.rssi} dBm"
val drawableId =
if (node.isSensorNode) {
org.meshtastic.app.R.drawable.ic_thermostat
} else {
org.meshtastic.app.R.drawable.ic_person
}
icon = context.getDrawable(drawableId)
// Default OSM marker handles color tinting via icon overlay or custom drawables if needed,
// but setting the icon directly overrides the default teardrop pin.
}
map.overlays.add(marker)
}
// Polylines from user to direct neighbors
if (hasValidUserPosition) {
validNodes
.filter { it.neighborType == DiscoveryNeighborType.DIRECT }
.forEach { node ->
val polyline =
Polyline().apply {
setPoints(listOf(userGeoPoint, GeoPoint(node.latitude, node.longitude)))
outlinePaint.apply {
color = DiscoveryMapColors.DirectLine.toArgb()
strokeWidth = with(density) { 3.dp.toPx() }
strokeCap = Paint.Cap.ROUND
style = Paint.Style.STROKE
}
}
map.overlays.add(polyline)
}
}
map.invalidate()
},
)
}
@@ -1,147 +0,0 @@
/*
* Copyright (c) 2026 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/>.
*/
@file:Suppress("MagicNumber")
package org.meshtastic.app.map.discovery
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.LatLngBounds
import com.google.maps.android.compose.ComposeMapColorScheme
import com.google.maps.android.compose.GoogleMap
import com.google.maps.android.compose.MapUiSettings
import com.google.maps.android.compose.MapsComposeExperimentalApi
import com.google.maps.android.compose.MarkerComposable
import com.google.maps.android.compose.Polyline
import com.google.maps.android.compose.rememberCameraPositionState
import com.google.maps.android.compose.rememberUpdatedMarkerState
import org.meshtastic.core.ui.icon.MeshtasticIcons
import org.meshtastic.core.ui.icon.Person
import org.meshtastic.core.ui.icon.Temperature
import org.meshtastic.core.ui.util.DiscoveryMapNode
import org.meshtastic.core.ui.util.DiscoveryNeighborType
private const val DEFAULT_ZOOM = 12f
private const val BOUNDS_PADDING_PX = 100
private val DirectColor = Color(0xFF4CAF50)
private val MeshColor = Color(0xFF2196F3)
private val UserColor = Color(0xFFFF9800)
private val DirectLineColor = Color(0xFF4CAF50).copy(alpha = 0.5f)
/**
* Google Maps implementation of the discovery map. Renders discovered node markers color-coded by neighbor type (green
* = direct, blue = mesh) with polylines from the user position to direct neighbors. Auto-zooms to fit all markers.
*/
@OptIn(MapsComposeExperimentalApi::class)
@Composable
fun DiscoveryGoogleMap(
userLatitude: Double,
userLongitude: Double,
nodes: List<DiscoveryMapNode>,
modifier: Modifier = Modifier,
) {
val dark = isSystemInDarkTheme()
val mapColorScheme = if (dark) ComposeMapColorScheme.DARK else ComposeMapColorScheme.LIGHT
val userLatLng = remember(userLatitude, userLongitude) { LatLng(userLatitude, userLongitude) }
val hasValidUserPosition = userLatitude != 0.0 || userLongitude != 0.0
val validNodes = remember(nodes) { nodes.filter { it.latitude != 0.0 || it.longitude != 0.0 } }
val cameraState = rememberCameraPositionState {
position =
CameraPosition.fromLatLngZoom(if (hasValidUserPosition) userLatLng else LatLng(0.0, 0.0), DEFAULT_ZOOM)
}
// Auto-fit bounds on first composition
LaunchedEffect(validNodes, hasValidUserPosition) {
val allPoints = buildList {
if (hasValidUserPosition) add(userLatLng)
validNodes.forEach { add(LatLng(it.latitude, it.longitude)) }
}
if (allPoints.size >= 2) {
val boundsBuilder = LatLngBounds.builder()
allPoints.forEach { boundsBuilder.include(it) }
cameraState.animate(CameraUpdateFactory.newLatLngBounds(boundsBuilder.build(), BOUNDS_PADDING_PX))
} else if (allPoints.size == 1) {
cameraState.animate(CameraUpdateFactory.newLatLngZoom(allPoints.first(), DEFAULT_ZOOM))
}
}
GoogleMap(
mapColorScheme = mapColorScheme,
modifier = modifier,
uiSettings =
MapUiSettings(
zoomControlsEnabled = true,
mapToolbarEnabled = false,
compassEnabled = true,
myLocationButtonEnabled = false,
),
cameraPositionState = cameraState,
) {
// User position marker
if (hasValidUserPosition) {
MarkerComposable(state = rememberUpdatedMarkerState(position = userLatLng), title = "Your Position") {
DiscoveryMarkerChip(label = "You", color = UserColor)
}
}
// Node markers
validNodes.forEach { node ->
val nodeLatLng = LatLng(node.latitude, node.longitude)
val markerColor =
when (node.neighborType) {
DiscoveryNeighborType.DIRECT -> DirectColor
DiscoveryNeighborType.MESH -> MeshColor
}
val nodeIcon =
if (node.isSensorNode) {
MeshtasticIcons.Temperature
} else {
MeshtasticIcons.Person
}
MarkerComposable(
state = rememberUpdatedMarkerState(position = nodeLatLng),
title = node.longName ?: node.shortName ?: "Unknown",
snippet = "SNR: ${node.snr} dB / RSSI: ${node.rssi} dBm",
) {
DiscoveryMarkerChip(label = node.shortName ?: "?", color = markerColor, icon = nodeIcon)
}
}
// Polylines from user to direct neighbors
if (hasValidUserPosition) {
validNodes
.filter { it.neighborType == DiscoveryNeighborType.DIRECT }
.forEach { node ->
Polyline(
points = listOf(userLatLng, LatLng(node.latitude, node.longitude)),
color = DirectLineColor,
width = 4f,
)
}
}
}
}
@@ -1,32 +0,0 @@
/*
* Copyright (c) 2026 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.app.map.discovery
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import org.meshtastic.core.ui.util.DiscoveryMapNode
/** Flavor-unified entry point for the discovery map. Google Maps implementation. */
@Composable
fun DiscoveryMap(
userLatitude: Double,
userLongitude: Double,
nodes: List<DiscoveryMapNode>,
modifier: Modifier = Modifier,
) {
DiscoveryGoogleMap(userLatitude = userLatitude, userLongitude = userLongitude, nodes = nodes, modifier = modifier)
}
@@ -1,54 +0,0 @@
/*
* Copyright (c) 2026 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/>.
*/
@file:Suppress("MagicNumber")
package org.meshtastic.app.map.discovery
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
/** Compact chip rendered as a Google Maps marker icon for discovery nodes. */
@Composable
fun DiscoveryMarkerChip(label: String, color: Color, modifier: Modifier = Modifier, icon: ImageVector? = null) {
Box(
modifier =
modifier
.background(color = color, shape = RoundedCornerShape(12.dp))
.border(width = 1.dp, color = Color.White, shape = RoundedCornerShape(12.dp))
.padding(horizontal = 8.dp, vertical = 4.dp),
contentAlignment = Alignment.Center,
) {
if (icon != null) {
Icon(imageVector = icon, contentDescription = label, tint = Color.White, modifier = Modifier.size(16.dp))
} else {
Text(text = label, style = MaterialTheme.typography.labelSmall, color = Color.White)
}
}
}
-9
View File
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M480,480Q414,480 367,433Q320,386 320,320Q320,254 367,207Q414,160 480,160Q546,160 593,207Q640,254 640,320Q640,386 593,433Q546,480 480,480ZM160,720L160,688Q160,654 177.5,625.5Q195,597 224,582Q286,551 350,535.5Q414,520 480,520Q546,520 610,535.5Q674,551 736,582Q765,597 782.5,625.5Q800,654 800,688L800,720Q800,753 776.5,776.5Q753,800 720,800L240,800Q207,800 183.5,776.5Q160,753 160,720Z"/>
</vector>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M560,440Q543,440 531.5,428.5Q520,417 520,400Q520,383 531.5,371.5Q543,360 560,360L680,360Q697,360 708.5,371.5Q720,383 720,400Q720,417 708.5,428.5Q697,440 680,440L560,440ZM560,280Q543,280 531.5,268.5Q520,257 520,240Q520,223 531.5,211.5Q543,200 560,200L800,200Q817,200 828.5,211.5Q840,223 840,240Q840,257 828.5,268.5Q817,280 800,280L560,280ZM320,840Q237,840 178.5,781.5Q120,723 120,640Q120,592 141,550.5Q162,509 200,480L200,240Q200,190 235,155Q270,120 320,120Q370,120 405,155Q440,190 440,240L440,480Q478,509 499,550.5Q520,592 520,640Q520,723 461.5,781.5Q403,840 320,840ZM200,640L440,640Q440,611 427.5,586Q415,561 392,544L360,520L360,240Q360,223 348.5,211.5Q337,200 320,200Q303,200 291.5,211.5Q280,223 280,240L280,520L248,544Q225,561 212.5,586Q200,611 200,640Z"/>
</vector>