kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
Merge 414e1ae7f1
into 53946ee785
commit
1ca7cea350
|
@ -331,7 +331,6 @@
|
||||||
<ID>UnusedParameter:ChannelSettingsItemList.kt$onBack: () -> Unit</ID>
|
<ID>UnusedParameter:ChannelSettingsItemList.kt$onBack: () -> Unit</ID>
|
||||||
<ID>UnusedParameter:ChannelSettingsItemList.kt$title: String</ID>
|
<ID>UnusedParameter:ChannelSettingsItemList.kt$title: String</ID>
|
||||||
<ID>UtilityClassWithPublicConstructor:NetworkRepositoryModule.kt$NetworkRepositoryModule</ID>
|
<ID>UtilityClassWithPublicConstructor:NetworkRepositoryModule.kt$NetworkRepositoryModule</ID>
|
||||||
<ID>ViewModelForwarding:Main.kt$ScannedQrCodeDialog(uIViewModel, newChannelSet)</ID>
|
|
||||||
<ID>ViewModelForwarding:Main.kt$VersionChecks(uIViewModel)</ID>
|
<ID>ViewModelForwarding:Main.kt$VersionChecks(uIViewModel)</ID>
|
||||||
<ID>ViewModelInjection:DebugSearch.kt$viewModel</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>
|
<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>
|
||||||
|
|
|
@ -27,12 +27,12 @@ import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.geeksville.mesh.model.MetricsViewModel
|
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.addCopyright
|
||||||
import org.meshtastic.feature.map.addPolyline
|
import org.meshtastic.feature.map.addPolyline
|
||||||
import org.meshtastic.feature.map.addPositionMarkers
|
import org.meshtastic.feature.map.addPositionMarkers
|
||||||
import org.meshtastic.feature.map.addScaleBarOverlay
|
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.BoundingBox
|
||||||
import org.osmdroid.util.GeoPoint
|
import org.osmdroid.util.GeoPoint
|
||||||
|
|
||||||
|
|
|
@ -27,11 +27,9 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.geeksville.mesh.model.MetricsViewModel
|
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
|
import org.meshtastic.core.ui.component.MainAppBar
|
||||||
|
import org.meshtastic.feature.map.MapView
|
||||||
const val DEG_D = 1e-7
|
import org.meshtastic.feature.map.node.NodeMapViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NodeMapScreen(
|
fun NodeMapScreen(
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,10 +21,10 @@ import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.navDeepLink
|
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.DEEP_LINK_BASE_URI
|
||||||
import org.meshtastic.core.navigation.MapRoutes
|
import org.meshtastic.core.navigation.MapRoutes
|
||||||
import org.meshtastic.core.navigation.NodesRoutes
|
import org.meshtastic.core.navigation.NodesRoutes
|
||||||
|
import org.meshtastic.feature.map.MapScreen
|
||||||
|
|
||||||
fun NavGraphBuilder.mapGraph(navController: NavHostController) {
|
fun NavGraphBuilder.mapGraph(navController: NavHostController) {
|
||||||
composable<MapRoutes.Map>(deepLinks = listOf(navDeepLink<MapRoutes.Map>(basePath = "$DEEP_LINK_BASE_URI/map"))) {
|
composable<MapRoutes.Map>(deepLinks = listOf(navDeepLink<MapRoutes.Map>(basePath = "$DEEP_LINK_BASE_URI/map"))) {
|
||||||
|
|
|
@ -28,7 +28,6 @@ import androidx.annotation.RequiresPermission
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.coroutineScope
|
import androidx.lifecycle.coroutineScope
|
||||||
import com.geeksville.mesh.CoroutineDispatchers
|
import com.geeksville.mesh.CoroutineDispatchers
|
||||||
import com.geeksville.mesh.android.hasBluetoothPermission
|
|
||||||
import com.geeksville.mesh.util.registerReceiverCompat
|
import com.geeksville.mesh.util.registerReceiverCompat
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
@ -37,6 +36,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.meshtastic.core.common.hasBluetoothPermission
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
|
@ -49,7 +49,6 @@ import com.geeksville.mesh.StoreAndForwardProtos
|
||||||
import com.geeksville.mesh.TelemetryProtos
|
import com.geeksville.mesh.TelemetryProtos
|
||||||
import com.geeksville.mesh.TelemetryProtos.LocalStats
|
import com.geeksville.mesh.TelemetryProtos.LocalStats
|
||||||
import com.geeksville.mesh.XmodemProtos
|
import com.geeksville.mesh.XmodemProtos
|
||||||
import com.geeksville.mesh.android.hasLocationPermission
|
|
||||||
import com.geeksville.mesh.concurrent.handledLaunch
|
import com.geeksville.mesh.concurrent.handledLaunch
|
||||||
import com.geeksville.mesh.copy
|
import com.geeksville.mesh.copy
|
||||||
import com.geeksville.mesh.fromRadio
|
import com.geeksville.mesh.fromRadio
|
||||||
|
@ -78,6 +77,7 @@ import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.meshtastic.core.analytics.DataPair
|
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.MeshLogRepository
|
||||||
import org.meshtastic.core.data.repository.NodeRepository
|
import org.meshtastic.core.data.repository.NodeRepository
|
||||||
import org.meshtastic.core.data.repository.PacketRepository
|
import org.meshtastic.core.data.repository.PacketRepository
|
||||||
|
|
|
@ -67,11 +67,11 @@ import com.geeksville.mesh.MeshProtos
|
||||||
import com.geeksville.mesh.model.MetricsViewModel
|
import com.geeksville.mesh.model.MetricsViewModel
|
||||||
import org.meshtastic.core.model.util.metersIn
|
import org.meshtastic.core.model.util.metersIn
|
||||||
import org.meshtastic.core.model.util.toString
|
import org.meshtastic.core.model.util.toString
|
||||||
|
import org.meshtastic.core.proto.formatPositionTime
|
||||||
import org.meshtastic.core.strings.R
|
import org.meshtastic.core.strings.R
|
||||||
import org.meshtastic.core.ui.component.MainAppBar
|
import org.meshtastic.core.ui.component.MainAppBar
|
||||||
import org.meshtastic.core.ui.theme.AppTheme
|
import org.meshtastic.core.ui.theme.AppTheme
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import kotlin.time.Duration.Companion.days
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RowScope.PositionText(text: String, weight: Float) {
|
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 DEG_D = 1e-7
|
||||||
const val HEADING_DEG = 1e-5
|
const val HEADING_DEG = 1e-5
|
||||||
private const val SECONDS_TO_MILLIS = 1000L
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PositionItem(compactWidth: Boolean, position: MeshProtos.Position, dateFormat: DateFormat, system: DisplayUnits) {
|
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("${position.groundSpeed} Km/h", WEIGHT_15)
|
||||||
PositionText("%.0f°".format(position.groundTrack * HEADING_DEG), 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
|
@Composable
|
||||||
private fun ActionButtons(
|
private fun ActionButtons(
|
||||||
clearButtonEnabled: Boolean,
|
clearButtonEnabled: Boolean,
|
||||||
|
|
|
@ -60,7 +60,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.geeksville.mesh.BuildConfig
|
import com.geeksville.mesh.BuildConfig
|
||||||
import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
|
import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
|
||||||
import com.geeksville.mesh.android.gpsDisabled
|
|
||||||
import com.geeksville.mesh.navigation.getNavRouteFrom
|
import com.geeksville.mesh.navigation.getNavRouteFrom
|
||||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigItemList
|
import com.geeksville.mesh.ui.settings.radio.RadioConfigItemList
|
||||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
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.ExperimentalPermissionsApi
|
||||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import org.meshtastic.core.common.gpsDisabled
|
||||||
import org.meshtastic.core.navigation.Route
|
import org.meshtastic.core.navigation.Route
|
||||||
import org.meshtastic.core.strings.R
|
import org.meshtastic.core.strings.R
|
||||||
import org.meshtastic.core.ui.component.MainAppBar
|
import org.meshtastic.core.ui.component.MainAppBar
|
||||||
|
|
|
@ -22,4 +22,4 @@ plugins {
|
||||||
|
|
||||||
android { namespace = "org.meshtastic.core.common" }
|
android { namespace = "org.meshtastic.core.common" }
|
||||||
|
|
||||||
dependencies {}
|
dependencies { implementation(libs.core.ktx) }
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.meshtastic.android.library)
|
alias(libs.plugins.meshtastic.android.library)
|
||||||
|
alias(libs.plugins.meshtastic.android.library.compose)
|
||||||
alias(libs.plugins.protobuf)
|
alias(libs.plugins.protobuf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +54,8 @@ protobuf {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(projects.core.strings)
|
||||||
|
|
||||||
// This needs to be API for consuming modules
|
// This needs to be API for consuming modules
|
||||||
api(libs.protobuf.kotlin)
|
api(libs.protobuf.kotlin)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ dependencies {
|
||||||
implementation(libs.bundles.osm)
|
implementation(libs.bundles.osm)
|
||||||
googleImplementation(libs.bundles.maps.compose)
|
googleImplementation(libs.bundles.maps.compose)
|
||||||
|
|
||||||
|
implementation(libs.accompanist.permissions)
|
||||||
implementation(libs.annotation)
|
implementation(libs.annotation)
|
||||||
implementation(libs.timber)
|
implementation(libs.timber)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.Manifest // Added for Accompanist
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
@ -63,31 +63,25 @@ import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.geeksville.mesh.MeshProtos.Waypoint
|
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.copy
|
||||||
import com.geeksville.mesh.ui.map.components.EditWaypointDialog
|
|
||||||
import com.geeksville.mesh.util.SqlTileWriterExt
|
|
||||||
import com.geeksville.mesh.waypoint
|
import com.geeksville.mesh.waypoint
|
||||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi // Added for Accompanist
|
import com.google.accompanist.permissions.ExperimentalPermissionsApi // Added for Accompanist
|
||||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState // Added for Accompanist
|
import com.google.accompanist.permissions.rememberMultiplePermissionsState // Added for Accompanist
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
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.entity.Packet
|
||||||
import org.meshtastic.core.database.model.Node
|
import org.meshtastic.core.database.model.Node
|
||||||
import org.meshtastic.core.model.DataPacket
|
import org.meshtastic.core.model.DataPacket
|
||||||
import org.meshtastic.core.model.util.formatAgo
|
import org.meshtastic.core.model.util.formatAgo
|
||||||
import org.meshtastic.core.strings.R
|
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.cluster.RadiusMarkerClusterer
|
||||||
import org.meshtastic.feature.map.component.CacheLayout
|
import org.meshtastic.feature.map.component.CacheLayout
|
||||||
import org.meshtastic.feature.map.component.DownloadButton
|
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.component.MapButton
|
||||||
import org.meshtastic.feature.map.createLatLongGrid
|
|
||||||
import org.meshtastic.feature.map.model.CustomTileSource
|
import org.meshtastic.feature.map.model.CustomTileSource
|
||||||
import org.meshtastic.feature.map.model.MarkerWithLabel
|
import org.meshtastic.feature.map.model.MarkerWithLabel
|
||||||
import org.meshtastic.feature.map.zoomIn
|
|
||||||
import org.osmdroid.bonuspack.utils.BonusPackHelper.getBitmapFromVectorDrawable
|
import org.osmdroid.bonuspack.utils.BonusPackHelper.getBitmapFromVectorDrawable
|
||||||
import org.osmdroid.config.Configuration
|
import org.osmdroid.config.Configuration
|
||||||
import org.osmdroid.events.MapEventsReceiver
|
import org.osmdroid.events.MapEventsReceiver
|
||||||
|
@ -277,10 +271,11 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
|
||||||
MyLocationNewOverlay(this).apply {
|
MyLocationNewOverlay(this).apply {
|
||||||
enableMyLocation()
|
enableMyLocation()
|
||||||
enableFollowLocation()
|
enableFollowLocation()
|
||||||
getBitmapFromVectorDrawable(context, com.geeksville.mesh.R.drawable.ic_map_location_dot_24)?.let {
|
getBitmapFromVectorDrawable(context, org.meshtastic.core.ui.R.drawable.ic_map_location_dot_24)
|
||||||
setPersonIcon(it)
|
?.let {
|
||||||
setPersonAnchor(0.5f, 0.5f)
|
setPersonIcon(it)
|
||||||
}
|
setPersonAnchor(0.5f, 0.5f)
|
||||||
|
}
|
||||||
getBitmapFromVectorDrawable(context, org.meshtastic.core.ui.R.drawable.ic_map_navigation_24)?.let {
|
getBitmapFromVectorDrawable(context, org.meshtastic.core.ui.R.drawable.ic_map_navigation_24)?.let {
|
||||||
setDirectionIcon(it)
|
setDirectionIcon(it)
|
||||||
setDirectionAnchor(0.5f, 0.5f)
|
setDirectionAnchor(0.5f, 0.5f)
|
||||||
|
@ -309,7 +304,7 @@ fun MapView(mapViewModel: MapViewModel = hiltViewModel(), navigateToNodeDetails:
|
||||||
val waypoints by mapViewModel.waypoints.collectAsStateWithLifecycle(emptyMap())
|
val waypoints by mapViewModel.waypoints.collectAsStateWithLifecycle(emptyMap())
|
||||||
|
|
||||||
val markerIcon = remember {
|
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> {
|
fun MapView.onNodesChanged(nodes: Collection<Node>): List<MarkerWithLabel> {
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
@ -33,7 +33,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleEventObserver
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import org.meshtastic.feature.map.requiredZoomLevel
|
|
||||||
import org.osmdroid.config.Configuration
|
import org.osmdroid.config.Configuration
|
||||||
import org.osmdroid.tileprovider.tilesource.ITileSource
|
import org.osmdroid.tileprovider.tilesource.ITileSource
|
||||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||||
|
@ -72,7 +71,7 @@ private const val DEFAULT_ZOOM_LEVEL = 15.0
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
@Composable
|
@Composable
|
||||||
internal fun rememberMapViewWithLifecycle(
|
fun rememberMapViewWithLifecycle(
|
||||||
applicationId: String,
|
applicationId: String,
|
||||||
box: BoundingBox,
|
box: BoundingBox,
|
||||||
tileSource: ITileSource = TileSourceFactory.DEFAULT_TILE_SOURCE,
|
tileSource: ITileSource = TileSourceFactory.DEFAULT_TILE_SOURCE,
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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 android.database.Cursor
|
||||||
import org.osmdroid.tileprovider.modules.DatabaseFileArchive
|
import org.osmdroid.tileprovider.modules.DatabaseFileArchive
|
|
@ -15,9 +15,11 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.DatePickerDialog
|
||||||
|
import android.app.TimePickerDialog
|
||||||
|
import android.text.format.DateFormat
|
||||||
import android.widget.DatePicker
|
import android.widget.DatePicker
|
||||||
import android.widget.TimePicker
|
import android.widget.TimePicker
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
|
@ -76,7 +78,7 @@ import java.util.Locale
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
internal fun EditWaypointDialog(
|
fun EditWaypointDialog(
|
||||||
waypoint: Waypoint,
|
waypoint: Waypoint,
|
||||||
onSendClicked: (Waypoint) -> Unit,
|
onSendClicked: (Waypoint) -> Unit,
|
||||||
onDeleteClicked: (Waypoint) -> Unit,
|
onDeleteClicked: (Waypoint) -> Unit,
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
@file:Suppress("MagicNumber")
|
@file:Suppress("MagicNumber")
|
||||||
|
|
||||||
package com.geeksville.mesh.ui.map
|
package org.meshtastic.feature.map
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
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.Position
|
||||||
import com.geeksville.mesh.MeshProtos.Waypoint
|
import com.geeksville.mesh.MeshProtos.Waypoint
|
||||||
import com.geeksville.mesh.copy
|
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.geeksville.mesh.waypoint
|
||||||
import com.google.android.gms.location.LocationCallback
|
import com.google.android.gms.location.LocationCallback
|
||||||
import com.google.android.gms.location.LocationRequest
|
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.JointType
|
||||||
import com.google.android.gms.maps.model.LatLng
|
import com.google.android.gms.maps.model.LatLng
|
||||||
import com.google.android.gms.maps.model.LatLngBounds
|
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.ComposeMapColorScheme
|
||||||
import com.google.maps.android.compose.GoogleMap
|
import com.google.maps.android.compose.GoogleMap
|
||||||
import com.google.maps.android.compose.MapEffect
|
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.mpsToKmph
|
||||||
import org.meshtastic.core.model.util.mpsToMph
|
import org.meshtastic.core.model.util.mpsToMph
|
||||||
import org.meshtastic.core.model.util.toString
|
import org.meshtastic.core.model.util.toString
|
||||||
|
import org.meshtastic.core.proto.formatPositionTime
|
||||||
import org.meshtastic.core.strings.R
|
import org.meshtastic.core.strings.R
|
||||||
import org.meshtastic.core.ui.component.NodeChip
|
import org.meshtastic.core.ui.component.NodeChip
|
||||||
import org.meshtastic.feature.map.LastHeardFilter
|
import org.meshtastic.feature.map.component.ClusterItemsListDialog
|
||||||
import org.meshtastic.feature.map.LayerType
|
|
||||||
import org.meshtastic.feature.map.LocationPermissionsHandler
|
|
||||||
import org.meshtastic.feature.map.MapViewModel
|
|
||||||
import org.meshtastic.feature.map.component.CustomMapLayersSheet
|
import org.meshtastic.feature.map.component.CustomMapLayersSheet
|
||||||
import org.meshtastic.feature.map.component.CustomTileProviderManagerSheet
|
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.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 timber.log.Timber
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
|
|
||||||
private const val MIN_TRACK_POINT_DISTANCE_METERS = 20f
|
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")
|
@Suppress("CyclomaticComplexMethod", "LongMethod")
|
||||||
@OptIn(MapsComposeExperimentalApi::class, ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(MapsComposeExperimentalApi::class, ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@ -651,34 +647,6 @@ fun Uri.getFileName(context: android.content.Context): String {
|
||||||
return name
|
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)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
|
@ -698,15 +666,9 @@ private fun PositionInfoWindowContent(
|
||||||
|
|
||||||
Card {
|
Card {
|
||||||
Column(modifier = Modifier.padding(8.dp)) {
|
Column(modifier = Modifier.padding(8.dp)) {
|
||||||
PositionRow(
|
PositionRow(label = stringResource(R.string.latitude), value = "%.5f".format(position.latitudeI * DEG_D))
|
||||||
label = stringResource(R.string.latitude),
|
|
||||||
value = "%.5f".format(position.latitudeI * com.geeksville.mesh.ui.metrics.DEG_D),
|
|
||||||
)
|
|
||||||
|
|
||||||
PositionRow(
|
PositionRow(label = stringResource(R.string.longitude), value = "%.5f".format(position.longitudeI * DEG_D))
|
||||||
label = stringResource(R.string.longitude),
|
|
||||||
value = "%.5f".format(position.longitudeI * com.geeksville.mesh.ui.metrics.DEG_D),
|
|
||||||
)
|
|
||||||
|
|
||||||
PositionRow(label = stringResource(R.string.sats), value = position.satsInView.toString())
|
PositionRow(label = stringResource(R.string.sats), value = position.satsInView.toString())
|
||||||
|
|
||||||
|
@ -722,7 +684,7 @@ private fun PositionInfoWindowContent(
|
||||||
value = "%.0f°".format(position.groundTrack * HEADING_DEG),
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.clickable
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
@ -31,9 +31,9 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.geeksville.mesh.ui.map.NodeClusterItem
|
|
||||||
import org.meshtastic.core.strings.R
|
import org.meshtastic.core.strings.R
|
||||||
import org.meshtastic.core.ui.component.NodeChip
|
import org.meshtastic.core.ui.component.NodeChip
|
||||||
|
import org.meshtastic.feature.map.model.NodeClusterItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ClusterItemsListDialog(
|
fun ClusterItemsListDialog(
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.DatePickerDialog
|
||||||
import android.app.TimePickerDialog
|
import android.app.TimePickerDialog
|
|
@ -15,12 +15,11 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.Composable
|
||||||
import androidx.compose.runtime.key
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.ui.graphics.Color
|
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.Cluster
|
||||||
import com.google.maps.android.clustering.view.DefaultClusterRenderer
|
import com.google.maps.android.clustering.view.DefaultClusterRenderer
|
||||||
import com.google.maps.android.compose.Circle
|
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 com.google.maps.android.compose.clustering.Clustering
|
||||||
import org.meshtastic.core.ui.component.NodeChip
|
import org.meshtastic.core.ui.component.NodeChip
|
||||||
import org.meshtastic.feature.map.BaseMapViewModel
|
import org.meshtastic.feature.map.BaseMapViewModel
|
||||||
|
import org.meshtastic.feature.map.model.NodeClusterItem
|
||||||
|
|
||||||
@OptIn(MapsComposeExperimentalApi::class)
|
@OptIn(MapsComposeExperimentalApi::class)
|
||||||
@Suppress("NestedBlockDepth")
|
@Suppress("NestedBlockDepth")
|
|
@ -15,13 +15,12 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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 android.widget.Toast
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import com.geeksville.mesh.MeshProtos
|
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.BitmapDescriptor
|
||||||
import com.google.android.gms.maps.model.LatLng
|
import com.google.android.gms.maps.model.LatLng
|
||||||
import com.google.maps.android.compose.Marker
|
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.core.strings.R
|
||||||
import org.meshtastic.feature.map.BaseMapViewModel
|
import org.meshtastic.feature.map.BaseMapViewModel
|
||||||
|
|
||||||
|
private const val DEG_D = 1e-7
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun WaypointMarkers(
|
fun WaypointMarkers(
|
||||||
displayableWaypoints: List<MeshProtos.Waypoint>,
|
displayableWaypoints: List<MeshProtos.Waypoint>,
|
|
@ -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]
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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.Box
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
@ -28,7 +28,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import org.meshtastic.core.strings.R
|
import org.meshtastic.core.strings.R
|
||||||
import org.meshtastic.core.ui.component.MainAppBar
|
import org.meshtastic.core.ui.component.MainAppBar
|
||||||
import org.meshtastic.feature.map.MapViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MapScreen(
|
fun MapScreen(
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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 androidx.lifecycle.ViewModel
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
Ładowanie…
Reference in New Issue