kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
refactor(R): Move R file imports to core UI module (#3159)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>pull/3171/head
rodzic
e8e7608e52
commit
d2db37e0d4
|
@ -183,6 +183,7 @@ dependencies {
|
|||
implementation(projects.core.network)
|
||||
implementation(projects.core.prefs)
|
||||
implementation(projects.core.proto)
|
||||
implementation(projects.core.strings)
|
||||
implementation(projects.feature.map)
|
||||
|
||||
// Bundles
|
||||
|
|
|
@ -31,11 +31,11 @@ import androidx.compose.ui.test.performTextInput
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.debug.FilterMode
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DebugFiltersTest {
|
||||
|
|
|
@ -32,13 +32,13 @@ import androidx.compose.ui.test.performTextInput
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.LogSearchManager.SearchState
|
||||
import com.geeksville.mesh.ui.debug.DebugSearchBar
|
||||
import com.geeksville.mesh.ui.debug.FilterMode
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DebugSearchTest {
|
||||
|
|
|
@ -24,7 +24,6 @@ import androidx.compose.ui.test.performClick
|
|||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.deviceProfile
|
||||
import com.geeksville.mesh.position
|
||||
import com.geeksville.mesh.ui.settings.radio.components.EditDeviceProfileDialog
|
||||
|
@ -32,6 +31,7 @@ import org.junit.Assert
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class EditDeviceProfileDialogTest {
|
||||
|
|
|
@ -26,12 +26,12 @@ import androidx.compose.ui.test.onNodeWithText
|
|||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.settings.radio.components.MapReportingPreference
|
||||
import org.junit.Assert
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MapReportingPreferenceTest {
|
||||
|
|
|
@ -22,9 +22,7 @@ import androidx.compose.ui.test.junit4.createComposeRule
|
|||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.geeksville.mesh.MessageStatus
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.Message
|
||||
import com.geeksville.mesh.model.Node
|
||||
import com.geeksville.mesh.ui.common.preview.NodePreviewParameterProvider
|
||||
import com.geeksville.mesh.ui.message.components.MessageItem
|
||||
import org.junit.Rule
|
||||
|
@ -34,30 +32,30 @@ import org.junit.runner.RunWith
|
|||
@RunWith(AndroidJUnit4::class)
|
||||
class MessageItemTest {
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
@get:Rule val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun mqttIconIsDisplayedWhenViaMqttIsTrue() {
|
||||
val testNode = NodePreviewParameterProvider().minnieMouse
|
||||
val messageWithMqtt = Message(
|
||||
text = "Test message via MQTT",
|
||||
time = "10:00",
|
||||
fromLocal = false,
|
||||
status = MessageStatus.RECEIVED,
|
||||
snr = 2.5f,
|
||||
rssi = 90,
|
||||
hopsAway = 0,
|
||||
uuid = 1L,
|
||||
receivedTime = System.currentTimeMillis(),
|
||||
node = testNode,
|
||||
read = false,
|
||||
routingError = 0,
|
||||
packetId = 1234,
|
||||
emojis = listOf(),
|
||||
replyId = null,
|
||||
viaMqtt = true
|
||||
)
|
||||
val messageWithMqtt =
|
||||
Message(
|
||||
text = "Test message via MQTT",
|
||||
time = "10:00",
|
||||
fromLocal = false,
|
||||
status = MessageStatus.RECEIVED,
|
||||
snr = 2.5f,
|
||||
rssi = 90,
|
||||
hopsAway = 0,
|
||||
uuid = 1L,
|
||||
receivedTime = System.currentTimeMillis(),
|
||||
node = testNode,
|
||||
read = false,
|
||||
routingError = 0,
|
||||
packetId = 1234,
|
||||
emojis = listOf(),
|
||||
replyId = null,
|
||||
viaMqtt = true,
|
||||
)
|
||||
|
||||
composeTestRule.setContent {
|
||||
MessageItem(
|
||||
|
@ -79,24 +77,25 @@ class MessageItemTest {
|
|||
@Test
|
||||
fun mqttIconIsNotDisplayedWhenViaMqttIsFalse() {
|
||||
val testNode = NodePreviewParameterProvider().minnieMouse
|
||||
val messageWithoutMqtt = Message(
|
||||
text = "Test message not via MQTT",
|
||||
time = "10:00",
|
||||
fromLocal = false,
|
||||
status = MessageStatus.RECEIVED,
|
||||
snr = 2.5f,
|
||||
rssi = 90,
|
||||
hopsAway = 0,
|
||||
uuid = 1L,
|
||||
receivedTime = System.currentTimeMillis(),
|
||||
node = testNode,
|
||||
read = false,
|
||||
routingError = 0,
|
||||
packetId = 1234,
|
||||
emojis = listOf(),
|
||||
replyId = null,
|
||||
viaMqtt = false
|
||||
)
|
||||
val messageWithoutMqtt =
|
||||
Message(
|
||||
text = "Test message not via MQTT",
|
||||
time = "10:00",
|
||||
fromLocal = false,
|
||||
status = MessageStatus.RECEIVED,
|
||||
snr = 2.5f,
|
||||
rssi = 90,
|
||||
hopsAway = 0,
|
||||
uuid = 1L,
|
||||
receivedTime = System.currentTimeMillis(),
|
||||
node = testNode,
|
||||
read = false,
|
||||
routingError = 0,
|
||||
packetId = 1234,
|
||||
emojis = listOf(),
|
||||
replyId = null,
|
||||
viaMqtt = false,
|
||||
)
|
||||
|
||||
composeTestRule.setContent {
|
||||
MessageItem(
|
||||
|
@ -114,4 +113,4 @@ class MessageItemTest {
|
|||
// Check that the MQTT icon is not displayed
|
||||
composeTestRule.onNodeWithContentDescription("via MQTT").assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.channelSet
|
||||
import com.geeksville.mesh.channelSettings
|
||||
import com.geeksville.mesh.copy
|
||||
|
@ -35,6 +34,7 @@ import org.junit.Rule
|
|||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.meshtastic.core.model.Channel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ScannedQrCodeDialogTest {
|
||||
|
|
|
@ -64,7 +64,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.geeksville.mesh.DataPacket
|
||||
import com.geeksville.mesh.MeshProtos.Waypoint
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.BuildUtils.debug
|
||||
import com.geeksville.mesh.android.gpsDisabled
|
||||
import com.geeksville.mesh.android.hasGps
|
||||
|
@ -85,6 +84,7 @@ 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.strings.R
|
||||
import org.meshtastic.feature.map.cluster.RadiusMarkerClusterer
|
||||
import org.meshtastic.feature.map.model.CustomTileSource
|
||||
import org.meshtastic.feature.map.model.MarkerWithLabel
|
||||
|
@ -276,11 +276,11 @@ fun MapView(
|
|||
MyLocationNewOverlay(this).apply {
|
||||
enableMyLocation()
|
||||
enableFollowLocation()
|
||||
getBitmapFromVectorDrawable(context, R.drawable.ic_map_location_dot_24)?.let {
|
||||
getBitmapFromVectorDrawable(context, com.geeksville.mesh.R.drawable.ic_map_location_dot_24)?.let {
|
||||
setPersonIcon(it)
|
||||
setPersonAnchor(0.5f, 0.5f)
|
||||
}
|
||||
getBitmapFromVectorDrawable(context, R.drawable.ic_map_navigation_24)?.let {
|
||||
getBitmapFromVectorDrawable(context, com.geeksville.mesh.R.drawable.ic_map_navigation_24)?.let {
|
||||
setDirectionIcon(it)
|
||||
setDirectionAnchor(0.5f, 0.5f)
|
||||
}
|
||||
|
@ -307,7 +307,9 @@ fun MapView(
|
|||
val nodes by mapViewModel.nodes.collectAsStateWithLifecycle()
|
||||
val waypoints by mapViewModel.waypoints.collectAsStateWithLifecycle(emptyMap())
|
||||
|
||||
val markerIcon = remember { AppCompatResources.getDrawable(context, R.drawable.ic_baseline_location_on_24) }
|
||||
val markerIcon = remember {
|
||||
AppCompatResources.getDrawable(context, com.geeksville.mesh.R.drawable.ic_baseline_location_on_24)
|
||||
}
|
||||
|
||||
fun MapView.onNodesChanged(nodes: Collection<Node>): List<MarkerWithLabel> {
|
||||
val nodesWithPosition = nodes.filter { it.validPosition != null }
|
||||
|
|
|
@ -36,7 +36,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
|
|
|
@ -31,7 +31,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
internal fun DownloadButton(enabled: Boolean, onClick: () -> Unit) {
|
||||
|
|
|
@ -63,12 +63,12 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.geeksville.mesh.MeshProtos.Waypoint
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.ui.common.components.EditTextPreference
|
||||
import com.geeksville.mesh.ui.common.components.EmojiPickerDialog
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.waypoint
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
|
|
@ -29,8 +29,8 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun MapButton(
|
||||
|
|
|
@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.CopyrightOverlay
|
||||
|
@ -126,7 +126,7 @@ fun MapView.addPolyline(density: Density, geoPoints: List<GeoPoint>, onClick: ()
|
|||
}
|
||||
|
||||
fun MapView.addPositionMarkers(positions: List<MeshProtos.Position>, onClick: () -> Unit): List<Marker> {
|
||||
val navIcon = ContextCompat.getDrawable(context, R.drawable.ic_map_navigation_24)
|
||||
val navIcon = ContextCompat.getDrawable(context, com.geeksville.mesh.R.drawable.ic_map_navigation_24)
|
||||
val markers =
|
||||
positions.map {
|
||||
Marker(this).apply {
|
||||
|
|
|
@ -68,7 +68,6 @@ import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig.DisplayUnits
|
|||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.MeshProtos.Position
|
||||
import com.geeksville.mesh.MeshProtos.Waypoint
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.BuildUtils.debug
|
||||
import com.geeksville.mesh.android.BuildUtils.warn
|
||||
import com.geeksville.mesh.copy
|
||||
|
@ -121,6 +120,7 @@ import com.google.maps.android.compose.rememberUpdatedMarkerState
|
|||
import com.google.maps.android.compose.widgets.ScaleBar
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.strings.R
|
||||
import timber.log.Timber
|
||||
import java.text.DateFormat
|
||||
|
||||
|
|
|
@ -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.R
|
||||
import com.geeksville.mesh.ui.map.NodeClusterItem
|
||||
import com.geeksville.mesh.ui.node.components.NodeChip
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun ClusterItemsListDialog(
|
||||
|
|
|
@ -39,8 +39,8 @@ 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.R
|
||||
import com.geeksville.mesh.ui.map.MapLayerItem
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
|
|
|
@ -51,10 +51,10 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.map.CustomTileProviderConfig
|
||||
import com.geeksville.mesh.ui.map.MapViewModel
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
|
|
|
@ -63,9 +63,9 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.geeksville.mesh.MeshProtos.Waypoint
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.ui.common.components.EmojiPickerDialog
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
|
|
|
@ -33,9 +33,9 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.map.MapViewModel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
|
|
|
@ -30,8 +30,8 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.map.MapViewModel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
internal fun MapFilterDropdown(expanded: Boolean, onDismissRequest: () -> Unit, mapViewModel: MapViewModel) {
|
||||
|
|
|
@ -28,9 +28,9 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.map.MapViewModel
|
||||
import com.google.maps.android.compose.MapType
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
|
|
|
@ -21,13 +21,13 @@ import android.widget.Toast
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.map.BaseMapViewModel
|
||||
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
|
||||
import com.google.maps.android.compose.rememberUpdatedMarkerState
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun WaypointMarkers(
|
||||
|
|
|
@ -26,7 +26,6 @@ import android.os.RemoteException
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.repository.bluetooth.BluetoothRepository
|
||||
import com.geeksville.mesh.repository.datastore.recentaddresses.RecentAddress
|
||||
|
@ -53,6 +52,7 @@ import kotlinx.coroutines.flow.map
|
|||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.strings.R
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,8 +20,8 @@ package com.geeksville.mesh.model
|
|||
import androidx.annotation.StringRes
|
||||
import com.geeksville.mesh.MeshProtos.Routing
|
||||
import com.geeksville.mesh.MessageStatus
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.Reaction
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
@StringRes
|
||||
|
@ -67,12 +67,13 @@ data class Message(
|
|||
) {
|
||||
fun getStatusStringRes(): Pair<Int, Int> {
|
||||
val title = if (routingError > 0) R.string.error else R.string.message_delivery_status
|
||||
val text = when (status) {
|
||||
MessageStatus.RECEIVED -> R.string.delivery_confirmed
|
||||
MessageStatus.QUEUED -> R.string.message_status_queued
|
||||
MessageStatus.ENROUTE -> R.string.message_status_enroute
|
||||
else -> getStringResFrom(routingError)
|
||||
}
|
||||
val text =
|
||||
when (status) {
|
||||
MessageStatus.RECEIVED -> R.string.delivery_confirmed
|
||||
MessageStatus.QUEUED -> R.string.message_status_queued
|
||||
MessageStatus.ENROUTE -> R.string.message_status_enroute
|
||||
else -> getStringResFrom(routingError)
|
||||
}
|
||||
return title to text
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ import com.geeksville.mesh.MeshProtos.MeshPacket
|
|||
import com.geeksville.mesh.MeshProtos.Position
|
||||
import com.geeksville.mesh.Portnums
|
||||
import com.geeksville.mesh.Portnums.PortNum
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.android.prefs.MapPrefs
|
||||
|
@ -63,6 +62,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import org.meshtastic.core.model.DeviceHardware
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.feature.map.model.CustomTileSource
|
||||
import java.io.BufferedWriter
|
||||
import java.io.FileNotFoundException
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
package com.geeksville.mesh.model
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
enum class NodeSortOption(val sqlValue: String, @StringRes val stringRes: Int) {
|
||||
LAST_HEARD("last_heard", R.string.node_sort_last_heard),
|
|
@ -38,7 +38,6 @@ import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
|
|||
import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.Position
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.android.prefs.UiPrefs
|
||||
import com.geeksville.mesh.channel
|
||||
|
@ -84,6 +83,7 @@ import kotlinx.coroutines.flow.stateIn
|
|||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.model.DeviceHardware
|
||||
import org.meshtastic.core.strings.R
|
||||
import javax.inject.Inject
|
||||
|
||||
// Given a human name, strip out the first letter of the first three words and return that as the
|
||||
|
|
|
@ -39,7 +39,6 @@ import androidx.navigation.NavHostController
|
|||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.navigation
|
||||
import androidx.navigation.navDeepLink
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.metrics.DeviceMetricsScreen
|
||||
|
@ -58,6 +57,7 @@ import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
|
|||
import org.meshtastic.core.navigation.NodeDetailRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
fun NavGraphBuilder.nodesGraph(navController: NavHostController, uiViewModel: UIViewModel) {
|
||||
navigation<NodesRoutes.NodesGraph>(startDestination = NodesRoutes.Nodes) {
|
||||
|
|
|
@ -56,7 +56,6 @@ import androidx.navigation.navDeepLink
|
|||
import androidx.navigation.navigation
|
||||
import com.geeksville.mesh.AdminProtos
|
||||
import com.geeksville.mesh.MeshProtos.DeviceMetadata
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.debug.DebugScreen
|
||||
import com.geeksville.mesh.ui.settings.SettingsScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.CleanNodeDatabaseScreen
|
||||
|
@ -88,6 +87,7 @@ import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
|
|||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
fun getNavRouteFrom(routeName: String): Route? =
|
||||
ConfigRoute.entries.find { it.name == routeName }?.route ?: ModuleRoute.entries.find { it.name == routeName }?.route
|
||||
|
|
|
@ -22,11 +22,8 @@ import androidx.datastore.core.DataStore
|
|||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
@ -34,6 +31,9 @@ import kotlinx.serialization.SerializationException
|
|||
import kotlinx.serialization.json.Json
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.meshtastic.core.strings.R
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RecentAddressesRepository
|
||||
|
@ -53,15 +53,11 @@ constructor(
|
|||
try {
|
||||
Json.decodeFromString<List<RecentAddress>>(jsonString)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
warn(
|
||||
"Could not parse recent addresses, falling back to legacy parsing: ${e.message}"
|
||||
)
|
||||
warn("Could not parse recent addresses, falling back to legacy parsing: ${e.message}")
|
||||
// Fallback to legacy parsing
|
||||
parseLegacyRecentAddresses(jsonString)
|
||||
} catch (e: SerializationException) {
|
||||
warn(
|
||||
"Could not parse recent addresses, falling back to legacy parsing: ${e.message}"
|
||||
)
|
||||
warn("Could not parse recent addresses, falling back to legacy parsing: ${e.message}")
|
||||
// Fallback to legacy parsing
|
||||
parseLegacyRecentAddresses(jsonString)
|
||||
}
|
||||
|
@ -76,10 +72,7 @@ constructor(
|
|||
when (val item = jsonArray.get(i)) {
|
||||
is JSONObject -> {
|
||||
// Modern format: JSONObject with address and name
|
||||
RecentAddress(
|
||||
address = item.getString("address"),
|
||||
name = item.getString("name"),
|
||||
)
|
||||
RecentAddress(address = item.getString("address"), name = item.getString("name"))
|
||||
}
|
||||
is String -> {
|
||||
// Old format: just the address string
|
||||
|
|
|
@ -50,7 +50,6 @@ import com.geeksville.mesh.NodeInfo
|
|||
import com.geeksville.mesh.PaxcountProtos
|
||||
import com.geeksville.mesh.Portnums
|
||||
import com.geeksville.mesh.Position
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.StoreAndForwardProtos
|
||||
import com.geeksville.mesh.TelemetryProtos
|
||||
import com.geeksville.mesh.TelemetryProtos.LocalStats
|
||||
|
@ -104,6 +103,7 @@ import kotlinx.coroutines.flow.flowOf
|
|||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.meshtastic.core.model.getFullTracerouteResponse
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.util.Random
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
|
|
@ -22,7 +22,7 @@ import android.app.NotificationChannel
|
|||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.TaskStackBuilder
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentResolver.SCHEME_ANDROID_RESOURCE
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
|
@ -36,12 +36,13 @@ import androidx.core.content.getSystemService
|
|||
import androidx.core.net.toUri
|
||||
import com.geeksville.mesh.MainActivity
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.R.raw
|
||||
import com.geeksville.mesh.TelemetryProtos.LocalStats
|
||||
import com.geeksville.mesh.database.entity.NodeEntity
|
||||
import com.geeksville.mesh.service.ReplyReceiver.Companion.KEY_TEXT_REPLY
|
||||
import com.geeksville.mesh.util.formatUptime
|
||||
import org.meshtastic.core.navigation.DEEP_LINK_BASE_URI
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
/**
|
||||
* Manages the creation and display of all app notifications.
|
||||
|
@ -184,8 +185,7 @@ class MeshServiceNotifications(private val context: Context) {
|
|||
enableLights(true)
|
||||
enableVibration(true)
|
||||
setBypassDnd(true)
|
||||
val alertSoundUri =
|
||||
"${ContentResolver.SCHEME_ANDROID_RESOURCE}://${context.packageName}/${R.raw.alert}".toUri()
|
||||
val alertSoundUri = "${SCHEME_ANDROID_RESOURCE}://${context.packageName}/${raw.alert}".toUri()
|
||||
setSound(
|
||||
alertSoundUri,
|
||||
AudioAttributes.Builder()
|
||||
|
@ -415,7 +415,7 @@ class MeshServiceNotifications(private val context: Context) {
|
|||
type: NotificationType,
|
||||
contentIntent: PendingIntent? = null,
|
||||
): NotificationCompat.Builder {
|
||||
val smallIcon = R.drawable.app_icon
|
||||
val smallIcon = com.geeksville.mesh.R.drawable.app_icon
|
||||
|
||||
return NotificationCompat.Builder(context, type.channelId)
|
||||
.setSmallIcon(smallIcon)
|
||||
|
|
|
@ -76,7 +76,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.R
|
||||
import com.geeksville.mesh.android.AddNavigationTracking
|
||||
import com.geeksville.mesh.android.BuildUtils.debug
|
||||
import com.geeksville.mesh.android.setAttributes
|
||||
|
@ -120,6 +119,7 @@ import org.meshtastic.core.navigation.NodeDetailRoutes
|
|||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.strings.R
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector, val route: Route) {
|
||||
|
|
|
@ -37,7 +37,7 @@ import androidx.compose.ui.text.font.FontStyle
|
|||
import androidx.compose.ui.text.fromHtml
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun SimpleAlertDialog(
|
||||
|
@ -45,40 +45,34 @@ fun SimpleAlertDialog(
|
|||
message: String?,
|
||||
html: String? = null,
|
||||
onDismissRequest: () -> Unit,
|
||||
onConfirmRequest: () -> Unit = onDismissRequest // Default confirm to dismiss
|
||||
onConfirmRequest: () -> Unit = onDismissRequest, // Default confirm to dismiss
|
||||
) {
|
||||
|
||||
val annotatedString = html?.let {
|
||||
AnnotatedString.fromHtml(
|
||||
html,
|
||||
linkStyles = TextLinkStyles(
|
||||
style = SpanStyle(
|
||||
textDecoration = TextDecoration.Underline,
|
||||
fontStyle = FontStyle.Italic,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
val annotatedString =
|
||||
html?.let {
|
||||
AnnotatedString.fromHtml(
|
||||
html,
|
||||
linkStyles =
|
||||
TextLinkStyles(
|
||||
style =
|
||||
SpanStyle(
|
||||
textDecoration = TextDecoration.Underline,
|
||||
fontStyle = FontStyle.Italic,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
title = { Text(text = title) },
|
||||
text = {
|
||||
if (annotatedString != null) {
|
||||
Text(
|
||||
text = annotatedString,
|
||||
)
|
||||
Text(text = annotatedString)
|
||||
} else {
|
||||
Text(
|
||||
text = message.orEmpty()
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onConfirmRequest) {
|
||||
Text(stringResource(id = R.string.okay))
|
||||
Text(text = message.orEmpty())
|
||||
}
|
||||
},
|
||||
confirmButton = { TextButton(onClick = onConfirmRequest) { Text(stringResource(id = R.string.okay)) } },
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -94,15 +88,8 @@ fun MultipleChoiceAlertDialog(
|
|||
onDismissRequest = onDismissRequest,
|
||||
title = { Text(text = title) },
|
||||
text = {
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
message?.let {
|
||||
Text(
|
||||
text = it,
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||
message?.let { Text(text = it, modifier = Modifier.padding(bottom = 8.dp)) }
|
||||
choices.forEach { (choice, action) ->
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth().padding(8.dp),
|
||||
|
@ -116,7 +103,6 @@ fun MultipleChoiceAlertDialog(
|
|||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
}
|
||||
confirmButton = {},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -36,26 +36,20 @@ import com.geeksville.mesh.R
|
|||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
fun BatteryInfo(
|
||||
modifier: Modifier = Modifier,
|
||||
batteryLevel: Int?,
|
||||
voltage: Float?
|
||||
) {
|
||||
fun BatteryInfo(modifier: Modifier = Modifier, batteryLevel: Int?, voltage: Float?) {
|
||||
val infoString = "%d%% %.2fV".format(batteryLevel, voltage)
|
||||
val (image, level) = when (batteryLevel) {
|
||||
in 0 .. 4 -> R.drawable.ic_battery_alert to " $infoString"
|
||||
in 5 .. 14 -> R.drawable.ic_battery_outline to infoString
|
||||
in 15..34 -> R.drawable.ic_battery_low to infoString
|
||||
in 35..79 -> R.drawable.ic_battery_medium to infoString
|
||||
in 80..100 -> R.drawable.ic_battery_high to infoString
|
||||
101 -> R.drawable.ic_power_plug_24 to "%.2fV".format(voltage)
|
||||
else -> R.drawable.ic_battery_unknown to (voltage?.let { "%.2fV".format(it) } ?: "")
|
||||
}
|
||||
val (image, level) =
|
||||
when (batteryLevel) {
|
||||
in 0..4 -> R.drawable.ic_battery_alert to " $infoString"
|
||||
in 5..14 -> R.drawable.ic_battery_outline to infoString
|
||||
in 15..34 -> R.drawable.ic_battery_low to infoString
|
||||
in 35..79 -> R.drawable.ic_battery_medium to infoString
|
||||
in 80..100 -> R.drawable.ic_battery_high to infoString
|
||||
101 -> R.drawable.ic_power_plug_24 to "%.2fV".format(voltage)
|
||||
else -> R.drawable.ic_battery_unknown to (voltage?.let { "%.2fV".format(it) } ?: "")
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
modifier = Modifier.height(18.dp),
|
||||
imageVector = ImageVector.vectorResource(id = image),
|
||||
|
@ -65,46 +59,34 @@ fun BatteryInfo(
|
|||
Text(
|
||||
text = level,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontSize = MaterialTheme.typography.labelLarge.fontSize
|
||||
fontSize = MaterialTheme.typography.labelLarge.fontSize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
fun BatteryInfoPreview(
|
||||
@PreviewParameter(BatteryInfoPreviewParameterProvider::class)
|
||||
batteryInfo: Pair<Int?, Float?>
|
||||
) {
|
||||
AppTheme {
|
||||
BatteryInfo(
|
||||
batteryLevel = batteryInfo.first,
|
||||
voltage = batteryInfo.second
|
||||
)
|
||||
}
|
||||
fun BatteryInfoPreview(@PreviewParameter(BatteryInfoPreviewParameterProvider::class) batteryInfo: Pair<Int?, Float?>) {
|
||||
AppTheme { BatteryInfo(batteryLevel = batteryInfo.first, voltage = batteryInfo.second) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun BatteryInfoPreviewSimple() {
|
||||
AppTheme {
|
||||
BatteryInfo(
|
||||
batteryLevel = 85,
|
||||
voltage = 3.7F
|
||||
)
|
||||
}
|
||||
AppTheme { BatteryInfo(batteryLevel = 85, voltage = 3.7F) }
|
||||
}
|
||||
|
||||
class BatteryInfoPreviewParameterProvider : PreviewParameterProvider<Pair<Int?, Float?>> {
|
||||
override val values: Sequence<Pair<Int?, Float?>>
|
||||
get() = sequenceOf(
|
||||
85 to 3.7F,
|
||||
2 to 3.7F,
|
||||
12 to 3.7F,
|
||||
28 to 3.7F,
|
||||
50 to 3.7F,
|
||||
101 to 4.9F,
|
||||
null to 4.5F,
|
||||
null to null
|
||||
)
|
||||
get() =
|
||||
sequenceOf(
|
||||
85 to 3.7F,
|
||||
2 to 3.7F,
|
||||
12 to 3.7F,
|
||||
28 to 3.7F,
|
||||
50 to 3.7F,
|
||||
101 to 4.9F,
|
||||
null to 4.5F,
|
||||
null to null,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
|
@ -28,8 +28,8 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.ClipEntry
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.geeksville.mesh.R
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun CopyIconButton(
|
||||
|
@ -47,11 +47,8 @@ fun CopyIconButton(
|
|||
val clipEntry = ClipEntry(clipData)
|
||||
clipboardManager.setClipEntry(clipEntry)
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.ContentCopy,
|
||||
contentDescription = label
|
||||
)
|
||||
Icon(imageVector = Icons.TwoTone.ContentCopy, contentDescription = label)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,11 +44,11 @@ import androidx.compose.ui.text.input.ImeAction
|
|||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.util.encodeToString
|
||||
import com.geeksville.mesh.util.toByteString
|
||||
import com.google.protobuf.ByteString
|
||||
import org.meshtastic.core.model.Channel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
|
|
|
@ -42,10 +42,10 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.ModuleConfigProtos.RemoteHardwarePin
|
||||
import com.geeksville.mesh.ModuleConfigProtos.RemoteHardwarePinType
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.remoteHardwarePin
|
||||
import com.google.protobuf.ByteString
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
|
|
|
@ -35,7 +35,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun EditPasswordPreference(
|
||||
|
@ -55,20 +55,18 @@ fun EditPasswordPreference(
|
|||
maxSize = maxSize,
|
||||
enabled = enabled,
|
||||
isError = false,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Password, imeAction = ImeAction.Done
|
||||
),
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
|
||||
keyboardActions = keyboardActions,
|
||||
onValueChanged = {
|
||||
onValueChanged(it)
|
||||
},
|
||||
onValueChanged = { onValueChanged(it) },
|
||||
onFocusChanged = {},
|
||||
visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(),
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { isPasswordVisible = !isPasswordVisible }) {
|
||||
Icon(
|
||||
imageVector = if (isPasswordVisible) Icons.TwoTone.VisibilityOff else Icons.TwoTone.VisibilityOff,
|
||||
contentDescription = if (isPasswordVisible) {
|
||||
contentDescription =
|
||||
if (isPasswordVisible) {
|
||||
stringResource(R.string.hide_password)
|
||||
} else {
|
||||
stringResource(R.string.show_password)
|
||||
|
@ -76,7 +74,7 @@ fun EditPasswordPreference(
|
|||
)
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -89,6 +87,6 @@ private fun EditPasswordPreferencePreview() {
|
|||
maxSize = 63,
|
||||
enabled = true,
|
||||
keyboardActions = KeyboardActions {},
|
||||
onValueChanged = {}
|
||||
onValueChanged = {},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun SignedIntegerEditTextPreference(
|
||||
|
|
|
@ -57,7 +57,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.theme.IAQColors.IAQDangerouslyPolluted
|
||||
import com.geeksville.mesh.ui.common.theme.IAQColors.IAQExcellent
|
||||
import com.geeksville.mesh.ui.common.theme.IAQColors.IAQExtremelyPolluted
|
||||
|
@ -66,6 +65,7 @@ import com.geeksville.mesh.ui.common.theme.IAQColors.IAQHeavilyPolluted
|
|||
import com.geeksville.mesh.ui.common.theme.IAQColors.IAQLightlyPolluted
|
||||
import com.geeksville.mesh.ui.common.theme.IAQColors.IAQModeratelyPolluted
|
||||
import com.geeksville.mesh.ui.common.theme.IAQColors.IAQSeverelyPolluted
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
enum class Iaq(val color: Color, val description: String, val range: IntRange) {
|
||||
|
|
|
@ -47,11 +47,11 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusOrange
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
private const val SNR_GOOD_THRESHOLD = -7f
|
||||
private const val SNR_FAIR_THRESHOLD = -15f
|
||||
|
|
|
@ -46,7 +46,6 @@ import androidx.navigation.NavDestination
|
|||
import androidx.navigation.NavDestination.Companion.hasRoute
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.Node
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.navigation.isConfigRoute
|
||||
|
@ -59,6 +58,7 @@ import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
|||
import org.meshtastic.core.navigation.ContactsRoutes
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
@Composable
|
||||
|
@ -153,7 +153,7 @@ fun MainAppBar(
|
|||
{
|
||||
IconButton(enabled = false, onClick = {}) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.app_icon),
|
||||
imageVector = ImageVector.vectorResource(id = com.geeksville.mesh.R.drawable.app_icon),
|
||||
contentDescription = stringResource(id = R.string.application_icon),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -31,9 +31,9 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.util.DistanceUnit
|
||||
import com.geeksville.mesh.util.toDistanceString
|
||||
import org.meshtastic.core.strings.R
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun PreferenceFooter(
|
||||
|
@ -59,32 +59,15 @@ fun PreferenceFooter(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(64.dp),
|
||||
modifier = modifier.fillMaxWidth().height(64.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
OutlinedButton(
|
||||
modifier = modifier
|
||||
.height(48.dp)
|
||||
.weight(1f),
|
||||
onClick = onNegativeClicked,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = negativeText),
|
||||
)
|
||||
OutlinedButton(modifier = Modifier.height(48.dp).weight(1f), onClick = onNegativeClicked) {
|
||||
Text(text = stringResource(id = negativeText))
|
||||
}
|
||||
OutlinedButton(
|
||||
modifier = modifier
|
||||
.height(48.dp)
|
||||
.weight(1f),
|
||||
enabled = enabled,
|
||||
onClick = onPositiveClicked,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = positiveText),
|
||||
)
|
||||
OutlinedButton(modifier = Modifier.height(48.dp).weight(1f), enabled = enabled, onClick = onPositiveClicked) {
|
||||
Text(text = stringResource(id = positiveText))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,12 +52,12 @@ import androidx.compose.ui.window.DialogProperties
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
|
||||
import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig.ModemPreset
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.channelSet
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.settings.radio.components.ChannelSelection
|
||||
import org.meshtastic.core.model.Channel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun ScannedQrCodeDialog(viewModel: UIViewModel, incoming: ChannelSet) {
|
||||
|
|
|
@ -63,12 +63,12 @@ import androidx.compose.ui.unit.dp
|
|||
import com.geeksville.mesh.AppOnlyProtos
|
||||
import com.geeksville.mesh.ChannelProtos.ChannelSettings
|
||||
import com.geeksville.mesh.ConfigProtos.Config.LoRaConfig
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.getChannel
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
|
||||
import org.meshtastic.core.model.Channel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
private const val PRECISE_POSITION_BITS = 32
|
||||
|
||||
|
|
|
@ -26,48 +26,46 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.Node
|
||||
import com.geeksville.mesh.ui.common.preview.NodePreviewParameterProvider
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
const val MAX_VALID_SNR = 100F
|
||||
const val MAX_VALID_RSSI = 0
|
||||
|
||||
@Composable
|
||||
fun SignalInfo(
|
||||
modifier: Modifier = Modifier,
|
||||
node: Node,
|
||||
isThisNode: Boolean
|
||||
) {
|
||||
val text = if (isThisNode) {
|
||||
stringResource(R.string.channel_air_util).format(
|
||||
node.deviceMetrics.channelUtilization,
|
||||
node.deviceMetrics.airUtilTx
|
||||
)
|
||||
} else {
|
||||
buildList {
|
||||
val hopsString = "%s: %s".format(
|
||||
stringResource(R.string.hops_away),
|
||||
if (node.hopsAway == -1) {
|
||||
"?"
|
||||
} else {
|
||||
node.hopsAway.toString()
|
||||
fun SignalInfo(modifier: Modifier = Modifier, node: Node, isThisNode: Boolean) {
|
||||
val text =
|
||||
if (isThisNode) {
|
||||
stringResource(R.string.channel_air_util)
|
||||
.format(node.deviceMetrics.channelUtilization, node.deviceMetrics.airUtilTx)
|
||||
} else {
|
||||
buildList {
|
||||
val hopsString =
|
||||
"%s: %s"
|
||||
.format(
|
||||
stringResource(R.string.hops_away),
|
||||
if (node.hopsAway == -1) {
|
||||
"?"
|
||||
} else {
|
||||
node.hopsAway.toString()
|
||||
},
|
||||
)
|
||||
if (node.channel > 0) {
|
||||
add("ch:${node.channel}")
|
||||
}
|
||||
)
|
||||
if (node.channel > 0) {
|
||||
add("ch:${node.channel}")
|
||||
if (node.hopsAway != 0) add(hopsString)
|
||||
}
|
||||
if (node.hopsAway != 0) add(hopsString)
|
||||
}.joinToString(" ")
|
||||
}
|
||||
.joinToString(" ")
|
||||
}
|
||||
Column {
|
||||
if (text.isNotEmpty()) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = text,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
)
|
||||
}
|
||||
/* We only know the Signal Quality from direct nodes aka 0 hop. */
|
||||
|
@ -84,43 +82,20 @@ fun SignalInfo(
|
|||
fun SignalInfoSimplePreview() {
|
||||
AppTheme {
|
||||
SignalInfo(
|
||||
node = Node(
|
||||
num = 1,
|
||||
lastHeard = 0,
|
||||
channel = 0,
|
||||
snr = 12.5F,
|
||||
rssi = -42,
|
||||
hopsAway = 0
|
||||
),
|
||||
isThisNode = false
|
||||
node = Node(num = 1, lastHeard = 0, channel = 0, snr = 12.5F, rssi = -42, hopsAway = 0),
|
||||
isThisNode = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
fun SignalInfoPreview(
|
||||
@PreviewParameter(NodePreviewParameterProvider::class)
|
||||
node: Node
|
||||
) {
|
||||
AppTheme {
|
||||
SignalInfo(
|
||||
node = node,
|
||||
isThisNode = false
|
||||
)
|
||||
}
|
||||
fun SignalInfoPreview(@PreviewParameter(NodePreviewParameterProvider::class) node: Node) {
|
||||
AppTheme { SignalInfo(node = node, isThisNode = false) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewLightDark
|
||||
fun SignalInfoSelfPreview(
|
||||
@PreviewParameter(NodePreviewParameterProvider::class)
|
||||
node: Node
|
||||
) {
|
||||
AppTheme {
|
||||
SignalInfo(
|
||||
node = node,
|
||||
isThisNode = true
|
||||
)
|
||||
}
|
||||
fun SignalInfoSelfPreview(@PreviewParameter(NodePreviewParameterProvider::class) node: Node) {
|
||||
AppTheme { SignalInfo(node = node, isThisNode = true) }
|
||||
}
|
||||
|
|
|
@ -32,8 +32,8 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun SimpleAlertDialog(
|
||||
|
@ -48,31 +48,25 @@ fun SimpleAlertDialog(
|
|||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp),
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
) { Text(text = dismissText ?: stringResource(id = R.string.cancel)) }
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.onSurface),
|
||||
) {
|
||||
Text(text = dismissText ?: stringResource(id = R.string.cancel))
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
onConfirm?.let {
|
||||
TextButton(
|
||||
onClick = onConfirm,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp),
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
) { Text(text = confirmText ?: stringResource(id = R.string.okay)) }
|
||||
TextButton(
|
||||
onClick = onConfirm,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.onSurface),
|
||||
) {
|
||||
Text(text = confirmText ?: stringResource(id = R.string.okay))
|
||||
}
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(id = title),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Text(text = stringResource(id = title), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
|
||||
},
|
||||
text = text,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
|
@ -89,11 +83,7 @@ fun SimpleAlertDialog(
|
|||
onDismiss = onDismiss,
|
||||
title = title,
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(id = text),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Text(text = stringResource(id = text), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -107,22 +97,11 @@ fun SimpleAlertDialog(
|
|||
onConfirm = onConfirm,
|
||||
onDismiss = onDismiss,
|
||||
title = title,
|
||||
text = {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
},
|
||||
text = { Text(text = text, modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center) },
|
||||
)
|
||||
|
||||
@PreviewLightDark
|
||||
@Composable
|
||||
private fun SimpleAlertDialogPreview() {
|
||||
AppTheme {
|
||||
SimpleAlertDialog(
|
||||
title = R.string.message,
|
||||
text = R.string.sample_message,
|
||||
)
|
||||
}
|
||||
AppTheme { SimpleAlertDialog(title = R.string.message, text = R.string.sample_message) }
|
||||
}
|
||||
|
|
|
@ -60,7 +60,6 @@ import androidx.compose.ui.window.Dialog
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.BTScanModel
|
||||
import com.geeksville.mesh.model.DeviceListEntry
|
||||
import com.geeksville.mesh.model.Node
|
||||
|
@ -83,6 +82,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
|||
import kotlinx.coroutines.delay
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
fun String?.isIPAddress(): Boolean = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
@Suppress("DEPRECATION")
|
||||
|
|
|
@ -46,7 +46,6 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.BTScanModel
|
||||
import com.geeksville.mesh.model.DeviceListEntry
|
||||
import com.geeksville.mesh.service.ConnectionState
|
||||
|
@ -54,6 +53,7 @@ import com.geeksville.mesh.ui.common.components.TitledCard
|
|||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.MultiplePermissionsState
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
/**
|
||||
* Composable that displays a list of Bluetooth Low Energy (BLE) devices and allows scanning. It handles Bluetooth
|
||||
|
|
|
@ -33,9 +33,9 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.connections.DeviceType
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LambdaParameterEventTrailing")
|
||||
@Composable
|
||||
|
|
|
@ -42,7 +42,6 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.PaxcountProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos
|
||||
import com.geeksville.mesh.model.Node
|
||||
import com.geeksville.mesh.ui.common.components.MaterialBatteryInfo
|
||||
|
@ -50,6 +49,7 @@ import com.geeksville.mesh.ui.common.theme.AppTheme
|
|||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.node.components.NodeChip
|
||||
import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun CurrentlyConnectedInfo(
|
||||
|
|
|
@ -34,8 +34,8 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.DeviceListEntry
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
|
|
|
@ -51,7 +51,6 @@ import androidx.compose.ui.text.input.ImeAction
|
|||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.BTScanModel
|
||||
import com.geeksville.mesh.model.DeviceListEntry
|
||||
import com.geeksville.mesh.repository.network.NetworkRepository
|
||||
|
@ -60,6 +59,7 @@ import com.geeksville.mesh.ui.common.components.TitledCard
|
|||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.connections.isIPAddress
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Suppress("MagicNumber", "LongMethod")
|
||||
|
|
|
@ -23,12 +23,12 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.BTScanModel
|
||||
import com.geeksville.mesh.model.DeviceListEntry
|
||||
import com.geeksville.mesh.service.ConnectionState
|
||||
import com.geeksville.mesh.ui.common.components.TitledCard
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun UsbDevices(
|
||||
|
|
|
@ -50,10 +50,10 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.AppOnlyProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.Contact
|
||||
import com.geeksville.mesh.ui.common.components.SecurityIcon
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
|
|
|
@ -62,11 +62,11 @@ import androidx.compose.ui.window.DialogProperties
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.AppOnlyProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.Contact
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.common.components.MainAppBar
|
||||
import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
|
|
|
@ -78,7 +78,6 @@ import androidx.compose.ui.unit.sp
|
|||
import androidx.datastore.core.IOException
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.BuildUtils.warn
|
||||
import com.geeksville.mesh.model.DebugViewModel
|
||||
import com.geeksville.mesh.model.DebugViewModel.UiMeshLog
|
||||
|
@ -90,6 +89,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.io.OutputStreamWriter
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.text.SimpleDateFormat
|
||||
|
|
|
@ -21,6 +21,7 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
@ -28,7 +29,6 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
|
@ -56,8 +56,8 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.DebugViewModel.UiMeshLog
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun DebugCustomFilterInput(
|
||||
|
@ -65,14 +65,9 @@ fun DebugCustomFilterInput(
|
|||
onCustomFilterTextChange: (String) -> Unit,
|
||||
filterTexts: List<String>,
|
||||
onFilterTextsChange: (List<String>) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(modifier = modifier.fillMaxWidth().padding(bottom = 4.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
OutlinedTextField(
|
||||
value = customFilterText,
|
||||
onValueChange = onCustomFilterTextChange,
|
||||
|
@ -80,14 +75,15 @@ fun DebugCustomFilterInput(
|
|||
placeholder = { Text("Add custom filter") },
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(
|
||||
keyboardActions =
|
||||
KeyboardActions(
|
||||
onDone = {
|
||||
if (customFilterText.isNotBlank()) {
|
||||
onFilterTextsChange(filterTexts + customFilterText)
|
||||
onCustomFilterTextChange("")
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(horizontal = 8.dp))
|
||||
IconButton(
|
||||
|
@ -97,12 +93,9 @@ fun DebugCustomFilterInput(
|
|||
onCustomFilterTextChange("")
|
||||
}
|
||||
},
|
||||
enabled = customFilterText.isNotBlank()
|
||||
enabled = customFilterText.isNotBlank(),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = stringResource(id = R.string.debug_filter_add)
|
||||
)
|
||||
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(id = R.string.debug_filter_add))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,27 +106,26 @@ internal fun DebugPresetFilters(
|
|||
filterTexts: List<String>,
|
||||
logs: List<UiMeshLog>,
|
||||
onFilterTextsChange: (List<String>) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val availableFilters = presetFilters.filter { filter ->
|
||||
logs.any { log ->
|
||||
log.logMessage.contains(filter, ignoreCase = true) ||
|
||||
log.messageType.contains(filter, ignoreCase = true) ||
|
||||
log.formattedReceivedDate.contains(filter, ignoreCase = true)
|
||||
val availableFilters =
|
||||
presetFilters.filter { filter ->
|
||||
logs.any { log ->
|
||||
log.logMessage.contains(filter, ignoreCase = true) ||
|
||||
log.messageType.contains(filter, ignoreCase = true) ||
|
||||
log.formattedReceivedDate.contains(filter, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
Column(modifier = modifier) {
|
||||
Text(
|
||||
text = "Preset Filters",
|
||||
style = TextStyle(fontWeight = FontWeight.Bold),
|
||||
modifier = Modifier.padding(vertical = 4.dp)
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
)
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 0.dp),
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 0.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(0.dp)
|
||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||
) {
|
||||
for (filter in availableFilters) {
|
||||
FilterChip(
|
||||
|
@ -144,17 +136,18 @@ internal fun DebugPresetFilters(
|
|||
filterTexts - filter
|
||||
} else {
|
||||
filterTexts + filter
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
label = { Text(filter) },
|
||||
leadingIcon = { if (filter in filterTexts) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Done,
|
||||
contentDescription = stringResource(id = R.string.debug_filter_included),
|
||||
)
|
||||
leadingIcon = {
|
||||
if (filter in filterTexts) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Done,
|
||||
contentDescription = stringResource(id = R.string.debug_filter_included),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -169,56 +162,46 @@ internal fun DebugFilterBar(
|
|||
onCustomFilterTextChange: (String) -> Unit,
|
||||
presetFilters: List<String>,
|
||||
logs: List<UiMeshLog>,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var showFilterMenu by remember { mutableStateOf(false) }
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box {
|
||||
TextButton(
|
||||
onClick = { showFilterMenu = !showFilterMenu }
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.debug_filters),
|
||||
style = TextStyle(fontWeight = FontWeight.Bold)
|
||||
)
|
||||
TextButton(onClick = { showFilterMenu = !showFilterMenu }) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(text = stringResource(R.string.debug_filters), style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
Icon(
|
||||
imageVector = if (filterTexts.isNotEmpty()) {
|
||||
imageVector =
|
||||
if (filterTexts.isNotEmpty()) {
|
||||
Icons.TwoTone.FilterAlt
|
||||
} else {
|
||||
Icons.TwoTone.FilterAltOff
|
||||
},
|
||||
contentDescription = stringResource(id = R.string.debug_filters)
|
||||
contentDescription = stringResource(id = R.string.debug_filters),
|
||||
)
|
||||
}
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showFilterMenu,
|
||||
onDismissRequest = { showFilterMenu = false },
|
||||
offset = DpOffset(0.dp, 8.dp)
|
||||
offset = DpOffset(0.dp, 8.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.width(300.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(8.dp).width(300.dp)) {
|
||||
DebugCustomFilterInput(
|
||||
customFilterText = customFilterText,
|
||||
onCustomFilterTextChange = onCustomFilterTextChange,
|
||||
filterTexts = filterTexts,
|
||||
onFilterTextsChange = onFilterTextsChange
|
||||
onFilterTextsChange = onFilterTextsChange,
|
||||
)
|
||||
DebugPresetFilters(
|
||||
presetFilters = presetFilters,
|
||||
filterTexts = filterTexts,
|
||||
logs = logs,
|
||||
onFilterTextsChange = onFilterTextsChange
|
||||
onFilterTextsChange = onFilterTextsChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -233,73 +216,62 @@ internal fun DebugActiveFilters(
|
|||
onFilterTextsChange: (List<String>) -> Unit,
|
||||
filterMode: FilterMode,
|
||||
onFilterModeChange: (FilterMode) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
|
||||
if (filterTexts.isNotEmpty()) {
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.padding(vertical = 2.dp)
|
||||
.background(colorScheme.background.copy(alpha = 1.0f)),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.debug_active_filters),
|
||||
style = TextStyle(fontWeight = FontWeight.Bold)
|
||||
style = TextStyle(fontWeight = FontWeight.Bold),
|
||||
)
|
||||
TextButton(
|
||||
onClick = {
|
||||
onFilterModeChange(
|
||||
if (filterMode == FilterMode.OR) FilterMode.AND else FilterMode.OR
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(if (filterMode == FilterMode.OR) {
|
||||
stringResource(R.string.match_any)
|
||||
if (filterMode == FilterMode.OR) {
|
||||
FilterMode.AND
|
||||
} else {
|
||||
stringResource(R.string.match_all)
|
||||
}
|
||||
FilterMode.OR
|
||||
},
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { onFilterTextsChange(emptyList()) }
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
if (filterMode == FilterMode.OR) {
|
||||
stringResource(R.string.match_any)
|
||||
} else {
|
||||
stringResource(R.string.match_all)
|
||||
},
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { onFilterTextsChange(emptyList()) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Clear,
|
||||
contentDescription = stringResource(id = R.string.debug_filter_clear)
|
||||
contentDescription = stringResource(id = R.string.debug_filter_clear),
|
||||
)
|
||||
}
|
||||
}
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 0.dp),
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 0.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(0.dp)
|
||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||
) {
|
||||
for (filter in filterTexts) {
|
||||
FilterChip(
|
||||
selected = true,
|
||||
onClick = {
|
||||
onFilterTextsChange(filterTexts - filter)
|
||||
},
|
||||
onClick = { onFilterTextsChange(filterTexts - filter) },
|
||||
label = { Text(filter) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.FilterAlt,
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Clear,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
leadingIcon = { Icon(imageVector = Icons.TwoTone.FilterAlt, contentDescription = null) },
|
||||
trailingIcon = { Icon(imageVector = Icons.Filled.Clear, contentDescription = null) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -307,4 +279,7 @@ internal fun DebugActiveFilters(
|
|||
}
|
||||
}
|
||||
|
||||
enum class FilterMode { OR, AND }
|
||||
enum class FilterMode {
|
||||
OR,
|
||||
AND,
|
||||
}
|
||||
|
|
|
@ -53,12 +53,12 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.DebugViewModel
|
||||
import com.geeksville.mesh.model.DebugViewModel.UiMeshLog
|
||||
import com.geeksville.mesh.model.LogSearchManager.SearchMatch
|
||||
import com.geeksville.mesh.model.LogSearchManager.SearchState
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
internal fun DebugSearchNavigation(
|
||||
|
@ -189,7 +189,7 @@ internal fun DebugSearchState(
|
|||
IconButton(onClick = onExport, modifier = Modifier) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.FileDownload,
|
||||
contentDescription = stringResource(id = com.geeksville.mesh.R.string.debug_logs_export),
|
||||
contentDescription = stringResource(id = R.string.debug_logs_export),
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
/**
|
||||
* Screen for explaining and guiding the user to configure critical alert settings. This screen is part of the app
|
||||
|
|
|
@ -26,7 +26,7 @@ import androidx.compose.material.icons.outlined.Router
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
/**
|
||||
* Screen for configuring location permissions during the app introduction. It explains why location permissions are
|
||||
|
|
|
@ -37,7 +37,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
/**
|
||||
* Screen for configuring notification permissions during the app introduction. It explains why notification permissions
|
||||
|
|
|
@ -43,6 +43,7 @@ import androidx.compose.ui.text.TextLayoutResult
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
/**
|
||||
* A generic layout for screens within the app introduction flow. It typically presents a headline, a descriptive text
|
||||
|
@ -92,7 +93,7 @@ internal fun PermissionScreenLayout(
|
|||
onSkip = onSkip,
|
||||
onConfigure = onConfigure,
|
||||
configureButtonText = stringResource(id = configureButtonTextRes),
|
||||
skipButtonText = stringResource(id = com.geeksville.mesh.R.string.skip),
|
||||
skipButtonText = stringResource(id = R.string.skip),
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
|
|
|
@ -39,7 +39,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
/**
|
||||
* The initial welcome screen for the app introduction flow. It displays a brief overview of the app's key features.
|
||||
|
|
|
@ -26,10 +26,10 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.common.components.MainAppBar
|
||||
import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun MapScreen(
|
||||
|
|
|
@ -96,7 +96,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.AppOnlyProtos
|
||||
import com.geeksville.mesh.DataPacket
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.QuickChatAction
|
||||
import com.geeksville.mesh.model.Message
|
||||
import com.geeksville.mesh.model.Node
|
||||
|
@ -109,6 +108,7 @@ import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
|||
import com.geeksville.mesh.ui.sharing.SharedContactDialog
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
private const val MESSAGE_CHARACTER_LIMIT_BYTES = 200
|
||||
|
|
|
@ -48,7 +48,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.MessageStatus
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.Reaction
|
||||
import com.geeksville.mesh.model.Message
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
|
@ -59,6 +58,7 @@ import kotlinx.coroutines.FlowPreview
|
|||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.launch
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun DeliveryInfo(
|
||||
|
|
|
@ -70,13 +70,13 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.QuickChatAction
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.common.components.dragContainer
|
||||
import com.geeksville.mesh.ui.common.components.dragDropItemsIndexed
|
||||
import com.geeksville.mesh.ui.common.components.rememberDragDropState
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
internal fun QuickChatScreen(modifier: Modifier = Modifier, viewModel: UIViewModel = hiltViewModel()) {
|
||||
|
|
|
@ -40,13 +40,11 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.geeksville.mesh.MessageStatus
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.components.EmojiPickerDialog
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun ReactionButton(
|
||||
onSendReaction: (String) -> Unit = {},
|
||||
) {
|
||||
fun ReactionButton(onSendReaction: (String) -> Unit = {}) {
|
||||
var showEmojiPickerDialog by remember { mutableStateOf(false) }
|
||||
if (showEmojiPickerDialog) {
|
||||
EmojiPickerDialog(
|
||||
|
@ -54,54 +52,40 @@ fun ReactionButton(
|
|||
showEmojiPickerDialog = false
|
||||
onSendReaction(selectedEmoji)
|
||||
},
|
||||
onDismiss = { showEmojiPickerDialog = false }
|
||||
onDismiss = { showEmojiPickerDialog = false },
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { showEmojiPickerDialog = true },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.EmojiEmotions,
|
||||
contentDescription = stringResource(R.string.react),
|
||||
)
|
||||
IconButton(onClick = { showEmojiPickerDialog = true }) {
|
||||
Icon(imageVector = Icons.Default.EmojiEmotions, contentDescription = stringResource(R.string.react))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ReplyButton(
|
||||
onClick: () -> Unit = {},
|
||||
) = IconButton(
|
||||
fun ReplyButton(onClick: () -> Unit = {}) = IconButton(
|
||||
onClick = onClick,
|
||||
content = {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.Reply,
|
||||
contentDescription = stringResource(R.string.reply),
|
||||
)
|
||||
}
|
||||
Icon(imageVector = Icons.AutoMirrored.Filled.Reply, contentDescription = stringResource(R.string.reply))
|
||||
},
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun MessageStatusButton(
|
||||
onStatusClick: () -> Unit = {},
|
||||
status: MessageStatus,
|
||||
fromLocal: Boolean,
|
||||
) = AnimatedVisibility(visible = fromLocal) {
|
||||
IconButton(
|
||||
onClick = onStatusClick
|
||||
) {
|
||||
Icon(
|
||||
imageVector = when (status) {
|
||||
MessageStatus.RECEIVED -> Icons.TwoTone.HowToReg
|
||||
MessageStatus.QUEUED -> Icons.TwoTone.CloudUpload
|
||||
MessageStatus.DELIVERED -> Icons.TwoTone.CloudDone
|
||||
MessageStatus.ENROUTE -> Icons.TwoTone.Cloud
|
||||
MessageStatus.ERROR -> Icons.TwoTone.CloudOff
|
||||
else -> Icons.TwoTone.Warning
|
||||
},
|
||||
contentDescription = stringResource(R.string.message_delivery_status),
|
||||
)
|
||||
fun MessageStatusButton(onStatusClick: () -> Unit = {}, status: MessageStatus, fromLocal: Boolean) =
|
||||
AnimatedVisibility(visible = fromLocal) {
|
||||
IconButton(onClick = onStatusClick) {
|
||||
Icon(
|
||||
imageVector =
|
||||
when (status) {
|
||||
MessageStatus.RECEIVED -> Icons.TwoTone.HowToReg
|
||||
MessageStatus.QUEUED -> Icons.TwoTone.CloudUpload
|
||||
MessageStatus.DELIVERED -> Icons.TwoTone.CloudDone
|
||||
MessageStatus.ENROUTE -> Icons.TwoTone.Cloud
|
||||
MessageStatus.ERROR -> Icons.TwoTone.CloudOff
|
||||
else -> Icons.TwoTone.Warning
|
||||
},
|
||||
contentDescription = stringResource(R.string.message_delivery_status),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
|
@ -113,16 +97,13 @@ fun MessageActions(
|
|||
onSendReply: () -> Unit = {},
|
||||
onStatusClick: () -> Unit = {},
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.wrapContentSize(),
|
||||
) {
|
||||
|
||||
Row(modifier = modifier.wrapContentSize()) {
|
||||
ReactionButton { onSendReaction(it) }
|
||||
ReplyButton { onSendReply() }
|
||||
MessageStatusButton(
|
||||
onStatusClick = onStatusClick,
|
||||
status = status ?: MessageStatus.UNKNOWN,
|
||||
fromLocal = isLocal
|
||||
fromLocal = isLocal,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.MessageStatus
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.Reaction
|
||||
import com.geeksville.mesh.model.Message
|
||||
import com.geeksville.mesh.model.Node
|
||||
|
@ -62,6 +61,7 @@ import com.geeksville.mesh.ui.common.theme.AppTheme
|
|||
import com.geeksville.mesh.ui.common.theme.MessageItemColors
|
||||
import com.geeksville.mesh.ui.node.components.NodeChip
|
||||
import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
|
|
|
@ -56,11 +56,11 @@ import androidx.compose.ui.text.style.TextDecoration
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.Environment
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_MINUTE_FORMAT
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.MAX_PERCENT_VALUE
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.text.DateFormat
|
||||
|
||||
object CommonCharts {
|
||||
|
|
|
@ -59,7 +59,6 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
|
@ -78,6 +77,7 @@ import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC
|
|||
import com.geeksville.mesh.util.GraphUtil
|
||||
import com.geeksville.mesh.util.GraphUtil.createPath
|
||||
import com.geeksville.mesh.util.GraphUtil.plotPoint
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
private const val CHART_WEIGHT = 1f
|
||||
private const val Y_AXIS_WEIGHT = 0.1f
|
||||
|
|
|
@ -40,13 +40,13 @@ import androidx.compose.ui.platform.LocalDensity
|
|||
import androidx.compose.ui.platform.LocalWindowInfo
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.model.Environment
|
||||
import com.geeksville.mesh.model.EnvironmentGraphingData
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
import com.geeksville.mesh.util.GraphUtil.createPath
|
||||
import com.geeksville.mesh.util.GraphUtil.drawPathWithGradient
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
private const val CHART_WEIGHT = 1f
|
||||
private const val Y_AXIS_WEIGHT = 0.1f
|
||||
|
|
|
@ -49,7 +49,6 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.EnvironmentMetrics
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.copy
|
||||
|
@ -62,6 +61,7 @@ import com.geeksville.mesh.ui.common.components.SlidingSelector
|
|||
import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC
|
||||
import com.geeksville.mesh.util.UnitConversions.celsiusToFahrenheit
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun EnvironmentMetricsScreen(viewModel: MetricsViewModel = hiltViewModel()) {
|
||||
|
|
|
@ -53,12 +53,12 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_FORMAT
|
||||
import com.geeksville.mesh.util.formatUptime
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.text.DecimalFormat
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
|
|
|
@ -55,13 +55,13 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.PaxcountProtos
|
||||
import com.geeksville.mesh.Portnums.PortNum
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.MeshLog
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
import com.geeksville.mesh.ui.common.components.OptionLabel
|
||||
import com.geeksville.mesh.ui.common.components.SlidingSelector
|
||||
import com.geeksville.mesh.util.formatUptime
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
|
||||
|
|
|
@ -63,11 +63,11 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig.DisplayUnits
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.util.metersIn
|
||||
import com.geeksville.mesh.util.toString
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.text.DateFormat
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
|
@ -71,6 +70,7 @@ import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_FORMAT
|
|||
import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC
|
||||
import com.geeksville.mesh.util.GraphUtil
|
||||
import com.geeksville.mesh.util.GraphUtil.createPath
|
||||
import org.meshtastic.core.strings.R
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.floor
|
||||
|
||||
|
|
|
@ -57,7 +57,6 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.MeshProtos.MeshPacket
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
import com.geeksville.mesh.ui.common.components.LoraSignalIndicator
|
||||
|
@ -67,6 +66,7 @@ import com.geeksville.mesh.ui.common.components.SnrAndRssi
|
|||
import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC
|
||||
import com.geeksville.mesh.util.GraphUtil.plotPoint
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private enum class Metric(val color: Color, val min: Float, val max: Float) {
|
||||
|
|
|
@ -57,13 +57,13 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.ui.common.components.SimpleAlertDialog
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC
|
||||
import org.meshtastic.core.model.fullRouteDiscovery
|
||||
import org.meshtastic.core.model.getTracerouteResponse
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.text.DateFormat
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
|
|
|
@ -124,7 +124,6 @@ import coil3.request.ImageRequest
|
|||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.DataPacket
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.FirmwareRelease
|
||||
import com.geeksville.mesh.database.entity.asDeviceVersion
|
||||
import com.geeksville.mesh.model.DeviceVersion
|
||||
|
@ -162,6 +161,7 @@ import org.meshtastic.core.model.DeviceHardware
|
|||
import org.meshtastic.core.navigation.NodeDetailRoutes
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
private data class VectorMetricInfo(
|
||||
@StringRes val label: Int,
|
||||
|
@ -717,7 +717,12 @@ private fun ColumnScope.DeviceDetailsContent(state: MetricsState) {
|
|||
} else {
|
||||
stringResource(R.string.supported_by_community)
|
||||
},
|
||||
icon = if (isSupported) Icons.TwoTone.Verified else ImageVector.vectorResource(R.drawable.unverified),
|
||||
icon =
|
||||
if (isSupported) {
|
||||
Icons.TwoTone.Verified
|
||||
} else {
|
||||
ImageVector.vectorResource(com.geeksville.mesh.R.drawable.unverified)
|
||||
},
|
||||
trailingText = "",
|
||||
iconTint = if (isSupported) colorScheme.StatusGreen else colorScheme.StatusRed,
|
||||
)
|
||||
|
@ -731,9 +736,9 @@ fun DeviceHardwareImage(deviceHardware: DeviceHardware, modifier: Modifier = Mod
|
|||
model = ImageRequest.Builder(LocalContext.current).data(imageUrl).build(),
|
||||
contentScale = ContentScale.Inside,
|
||||
contentDescription = deviceHardware.displayName,
|
||||
placeholder = painterResource(R.drawable.hw_unknown),
|
||||
error = painterResource(R.drawable.hw_unknown),
|
||||
fallback = painterResource(R.drawable.hw_unknown),
|
||||
placeholder = painterResource(com.geeksville.mesh.R.drawable.hw_unknown),
|
||||
error = painterResource(com.geeksville.mesh.R.drawable.hw_unknown),
|
||||
fallback = painterResource(com.geeksville.mesh.R.drawable.hw_unknown),
|
||||
modifier = modifier.padding(16.dp),
|
||||
)
|
||||
}
|
||||
|
@ -982,7 +987,7 @@ private fun EnvironmentMetrics(
|
|||
DrawableMetricInfo(
|
||||
R.string.dew_point,
|
||||
dewPoint.toTempString(isFahrenheit),
|
||||
R.drawable.ic_outlined_dew_point_24,
|
||||
com.geeksville.mesh.R.drawable.ic_outlined_dew_point_24,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -991,7 +996,7 @@ private fun EnvironmentMetrics(
|
|||
DrawableMetricInfo(
|
||||
R.string.soil_temperature,
|
||||
soilTemperature.toTempString(isFahrenheit),
|
||||
R.drawable.soil_temperature,
|
||||
com.geeksville.mesh.R.drawable.soil_temperature,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -1000,7 +1005,7 @@ private fun EnvironmentMetrics(
|
|||
DrawableMetricInfo(
|
||||
R.string.soil_moisture,
|
||||
"%d%%".format(soilMoisture),
|
||||
R.drawable.soil_moisture,
|
||||
com.geeksville.mesh.R.drawable.soil_moisture,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -1009,7 +1014,7 @@ private fun EnvironmentMetrics(
|
|||
DrawableMetricInfo(
|
||||
R.string.radiation,
|
||||
"%.1f µR/h".format(radiation),
|
||||
R.drawable.ic_filled_radioactive_24,
|
||||
com.geeksville.mesh.R.drawable.ic_filled_radioactive_24,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.DataPacket
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.DeviceVersion
|
||||
import com.geeksville.mesh.model.Node
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
|
@ -60,6 +59,7 @@ import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
|||
import com.geeksville.mesh.ui.sharing.AddContactFAB
|
||||
import com.geeksville.mesh.ui.sharing.SharedContactDialog
|
||||
import com.geeksville.mesh.ui.sharing.supportsQrCodeSharing
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
|
|
|
@ -35,26 +35,18 @@ import com.geeksville.mesh.ui.common.theme.AppTheme
|
|||
import com.geeksville.mesh.util.formatAgo
|
||||
|
||||
@Composable
|
||||
fun LastHeardInfo(
|
||||
modifier: Modifier = Modifier,
|
||||
lastHeard: Int,
|
||||
currentTimeMillis: Long,
|
||||
) {
|
||||
|
||||
fun LastHeardInfo(modifier: Modifier = Modifier, lastHeard: Int, currentTimeMillis: Long) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp)
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.height(18.dp),
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_antenna_24),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = formatAgo(lastHeard, currentTimeMillis),
|
||||
fontSize = MaterialTheme.typography.labelLarge.fontSize
|
||||
)
|
||||
Text(text = formatAgo(lastHeard, currentTimeMillis), fontSize = MaterialTheme.typography.labelLarge.fontSize)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,7 +56,7 @@ fun LastHeardInfoPreview() {
|
|||
AppTheme {
|
||||
LastHeardInfo(
|
||||
lastHeard = (System.currentTimeMillis() / 1000).toInt() - 8600,
|
||||
currentTimeMillis = System.currentTimeMillis()
|
||||
currentTimeMillis = System.currentTimeMillis(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,10 +56,10 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.NodeSortOption
|
||||
import com.geeksville.mesh.ui.common.preview.LargeFontPreview
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
|
|
|
@ -51,7 +51,6 @@ import androidx.compose.ui.unit.dp
|
|||
import com.geeksville.mesh.ConfigProtos.Config.DeviceConfig
|
||||
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.Node
|
||||
import com.geeksville.mesh.model.isUnmessageableRole
|
||||
import com.geeksville.mesh.ui.common.components.BatteryInfo
|
||||
|
@ -59,6 +58,7 @@ import com.geeksville.mesh.ui.common.components.SignalInfo
|
|||
import com.geeksville.mesh.ui.common.preview.NodePreviewParameterProvider
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.util.toDistanceString
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
|
|
|
@ -54,7 +54,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.components.CopyIconButton
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
|
||||
|
@ -62,6 +61,7 @@ import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
|||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
|
||||
import com.google.protobuf.ByteString
|
||||
import org.meshtastic.core.model.Channel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
private fun KeyStatusDialog(@StringRes title: Int, @StringRes text: Int, key: ByteString?, onDismiss: () -> Unit = {}) =
|
||||
|
@ -129,7 +129,9 @@ fun NodeKeyStatusIcon(
|
|||
when {
|
||||
mismatchKey -> Icons.Default.KeyOff to colorScheme.StatusRed
|
||||
hasPKC -> Icons.Default.Lock to colorScheme.StatusGreen
|
||||
else -> ImageVector.vectorResource(R.drawable.ic_lock_open_right_24) to colorScheme.StatusYellow
|
||||
else ->
|
||||
ImageVector.vectorResource(com.geeksville.mesh.R.drawable.ic_lock_open_right_24) to
|
||||
colorScheme.StatusYellow
|
||||
}
|
||||
|
||||
IconButton(onClick = { showEncryptionDialog = true }, modifier = modifier) {
|
||||
|
|
|
@ -38,10 +38,10 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.Node
|
||||
import com.geeksville.mesh.model.isUnmessageableRole
|
||||
import com.geeksville.mesh.ui.common.components.SimpleAlertDialog
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
|
@ -52,12 +52,13 @@ fun NodeMenu(
|
|||
onDismissMenuRequest: () -> Unit,
|
||||
onAction: (NodeMenuAction) -> Unit,
|
||||
) {
|
||||
val isUnmessageable = if (node.user.hasIsUnmessagable()) {
|
||||
node.user.isUnmessagable
|
||||
} else {
|
||||
// for older firmwares
|
||||
node.user.role?.isUnmessageableRole() == true
|
||||
}
|
||||
val isUnmessageable =
|
||||
if (node.user.hasIsUnmessagable()) {
|
||||
node.user.isUnmessagable
|
||||
} else {
|
||||
// for older firmwares
|
||||
node.user.role?.isUnmessageableRole() == true
|
||||
}
|
||||
|
||||
var displayFavoriteDialog by remember { mutableStateOf(false) }
|
||||
var displayIgnoreDialog by remember { mutableStateOf(false) }
|
||||
|
@ -79,14 +80,13 @@ fun NodeMenu(
|
|||
displayIgnoreDialog = displayIgnoreDialog,
|
||||
displayRemoveDialog = displayRemoveDialog,
|
||||
onDismissMenuRequest = dialogDismissRequest,
|
||||
onAction = onMenuAction
|
||||
onAction = onMenuAction,
|
||||
)
|
||||
DropdownMenu(
|
||||
modifier = Modifier.background(MaterialTheme.colorScheme.background.copy(alpha = 1f)),
|
||||
expanded = expanded,
|
||||
onDismissRequest = onDismissMenuRequest,
|
||||
) {
|
||||
|
||||
if (showFullMenu) {
|
||||
if (!isUnmessageable) {
|
||||
DropdownMenuItem(
|
||||
|
@ -94,7 +94,7 @@ fun NodeMenu(
|
|||
dialogDismissRequest()
|
||||
onMenuAction(NodeMenuAction.DirectMessage(node))
|
||||
},
|
||||
text = { Text(stringResource(R.string.direct_message)) }
|
||||
text = { Text(stringResource(R.string.direct_message)) },
|
||||
)
|
||||
}
|
||||
DropdownMenuItem(
|
||||
|
@ -102,21 +102,21 @@ fun NodeMenu(
|
|||
dialogDismissRequest()
|
||||
onMenuAction(NodeMenuAction.RequestUserInfo(node))
|
||||
},
|
||||
text = { Text(stringResource(R.string.exchange_userinfo)) }
|
||||
text = { Text(stringResource(R.string.exchange_userinfo)) },
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
dialogDismissRequest()
|
||||
onMenuAction(NodeMenuAction.RequestPosition(node))
|
||||
},
|
||||
text = { Text(stringResource(R.string.exchange_position)) }
|
||||
text = { Text(stringResource(R.string.exchange_position)) },
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
dialogDismissRequest()
|
||||
onMenuAction(NodeMenuAction.TraceRoute(node))
|
||||
},
|
||||
text = { Text(stringResource(R.string.traceroute)) }
|
||||
text = { Text(stringResource(R.string.traceroute)) },
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
|
@ -124,24 +124,20 @@ fun NodeMenu(
|
|||
displayFavoriteDialog = true
|
||||
},
|
||||
enabled = !node.isIgnored,
|
||||
text = {
|
||||
Text(stringResource(R.string.favorite))
|
||||
},
|
||||
text = { Text(stringResource(R.string.favorite)) },
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
imageVector = if (node.isFavorite) Icons.Filled.Star else Icons.TwoTone.StarBorder,
|
||||
contentDescription = stringResource(R.string.favorite),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
dialogDismissRequest()
|
||||
displayIgnoreDialog = true
|
||||
},
|
||||
text = {
|
||||
Text(stringResource(R.string.ignore))
|
||||
},
|
||||
text = { Text(stringResource(R.string.ignore)) },
|
||||
trailingIcon = {
|
||||
Checkbox(
|
||||
checked = node.isIgnored,
|
||||
|
@ -151,7 +147,7 @@ fun NodeMenu(
|
|||
},
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
|
@ -159,7 +155,7 @@ fun NodeMenu(
|
|||
displayRemoveDialog = true
|
||||
},
|
||||
enabled = !node.isIgnored,
|
||||
text = { Text(stringResource(R.string.remove)) }
|
||||
text = { Text(stringResource(R.string.remove)) },
|
||||
)
|
||||
HorizontalDivider(Modifier.padding(vertical = 8.dp))
|
||||
}
|
||||
|
@ -168,7 +164,7 @@ fun NodeMenu(
|
|||
dialogDismissRequest()
|
||||
onMenuAction(NodeMenuAction.Share(node))
|
||||
},
|
||||
text = { Text(stringResource(R.string.share_contact)) }
|
||||
text = { Text(stringResource(R.string.share_contact)) },
|
||||
)
|
||||
|
||||
DropdownMenuItem(
|
||||
|
@ -176,7 +172,7 @@ fun NodeMenu(
|
|||
dialogDismissRequest()
|
||||
onMenuAction(NodeMenuAction.MoreDetails(node))
|
||||
},
|
||||
text = { Text(stringResource(R.string.more_details)) }
|
||||
text = { Text(stringResource(R.string.more_details)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -188,34 +184,36 @@ fun NodeActionDialogs(
|
|||
displayIgnoreDialog: Boolean,
|
||||
displayRemoveDialog: Boolean,
|
||||
onDismissMenuRequest: () -> Unit,
|
||||
onAction: (NodeMenuAction) -> Unit
|
||||
onAction: (NodeMenuAction) -> Unit,
|
||||
) {
|
||||
if (displayFavoriteDialog) {
|
||||
SimpleAlertDialog(
|
||||
title = R.string.favorite,
|
||||
text = stringResource(
|
||||
text =
|
||||
stringResource(
|
||||
id = if (node.isFavorite) R.string.favorite_remove else R.string.favorite_add,
|
||||
node.user.longName
|
||||
node.user.longName,
|
||||
),
|
||||
onConfirm = {
|
||||
onDismissMenuRequest()
|
||||
onAction(NodeMenuAction.Favorite(node))
|
||||
},
|
||||
onDismiss = onDismissMenuRequest
|
||||
onDismiss = onDismissMenuRequest,
|
||||
)
|
||||
}
|
||||
if (displayIgnoreDialog) {
|
||||
SimpleAlertDialog(
|
||||
title = R.string.ignore,
|
||||
text = stringResource(
|
||||
text =
|
||||
stringResource(
|
||||
id = if (node.isIgnored) R.string.ignore_remove else R.string.ignore_add,
|
||||
node.user.longName
|
||||
node.user.longName,
|
||||
),
|
||||
onConfirm = {
|
||||
onDismissMenuRequest()
|
||||
onAction(NodeMenuAction.Ignore(node))
|
||||
},
|
||||
onDismiss = onDismissMenuRequest
|
||||
onDismiss = onDismissMenuRequest,
|
||||
)
|
||||
}
|
||||
if (displayRemoveDialog) {
|
||||
|
@ -226,19 +224,27 @@ fun NodeActionDialogs(
|
|||
onDismissMenuRequest()
|
||||
onAction(NodeMenuAction.Remove(node))
|
||||
},
|
||||
onDismiss = onDismissMenuRequest
|
||||
onDismiss = onDismissMenuRequest,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class NodeMenuAction {
|
||||
data class Remove(val node: Node) : NodeMenuAction()
|
||||
|
||||
data class Ignore(val node: Node) : NodeMenuAction()
|
||||
|
||||
data class Favorite(val node: Node) : NodeMenuAction()
|
||||
|
||||
data class DirectMessage(val node: Node) : NodeMenuAction()
|
||||
|
||||
data class RequestUserInfo(val node: Node) : NodeMenuAction()
|
||||
|
||||
data class RequestPosition(val node: Node) : NodeMenuAction()
|
||||
|
||||
data class TraceRoute(val node: Node) : NodeMenuAction()
|
||||
|
||||
data class MoreDetails(val node: Node) : NodeMenuAction()
|
||||
|
||||
data class Share(val node: Node) : NodeMenuAction()
|
||||
}
|
||||
|
|
|
@ -39,10 +39,10 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusGreen
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
|
@ -34,9 +34,9 @@ import androidx.compose.ui.platform.LocalDensity
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.settings.components.SettingsItem
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
private const val COOL_DOWN_TIME_MS = 30000L
|
||||
|
||||
|
|
|
@ -57,7 +57,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.R
|
||||
import com.geeksville.mesh.android.gpsDisabled
|
||||
import com.geeksville.mesh.navigation.getNavRouteFrom
|
||||
import com.geeksville.mesh.ui.common.components.MainAppBar
|
||||
|
@ -78,6 +77,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
|||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
import kotlinx.coroutines.delay
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
|
|
@ -46,9 +46,9 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.NodeEntity
|
||||
import com.geeksville.mesh.ui.node.components.NodeChip
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
/**
|
||||
* Composable screen for cleaning the node database. Allows users to specify criteria for deleting nodes. The list of
|
||||
|
|
|
@ -42,7 +42,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.navigation.ConfigRoute
|
||||
import com.geeksville.mesh.navigation.ModuleRoute
|
||||
import com.geeksville.mesh.ui.common.components.TitledCard
|
||||
|
@ -52,6 +51,7 @@ import com.geeksville.mesh.ui.settings.components.SettingsItem
|
|||
import com.geeksville.mesh.ui.settings.radio.components.WarningDialog
|
||||
import org.meshtastic.core.navigation.Route
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
|
|
|
@ -42,7 +42,6 @@ import com.geeksville.mesh.MeshProtos
|
|||
import com.geeksville.mesh.ModuleConfigProtos
|
||||
import com.geeksville.mesh.Portnums
|
||||
import com.geeksville.mesh.Position
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.android.GeeksvilleApplication
|
||||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.android.isAnalyticsAvailable
|
||||
|
@ -78,6 +77,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.strings.R
|
||||
import java.io.FileOutputStream
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.moduleConfig
|
||||
import com.geeksville.mesh.ui.common.components.EditTextPreference
|
||||
|
@ -41,6 +40,7 @@ import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun AmbientLightingConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
|
|
|
@ -34,7 +34,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.AudioConfig
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.moduleConfig
|
||||
import com.geeksville.mesh.ui.common.components.DropDownPreference
|
||||
|
@ -43,6 +42,7 @@ import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun AudioConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
|
|
|
@ -33,7 +33,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ConfigProtos.Config.BluetoothConfig
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.config
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.ui.common.components.DropDownPreference
|
||||
|
@ -42,6 +41,7 @@ import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun BluetoothConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
|
|
|
@ -36,7 +36,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig.CannedMessageConfig
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.moduleConfig
|
||||
import com.geeksville.mesh.ui.common.components.DropDownPreference
|
||||
|
@ -45,6 +44,7 @@ import com.geeksville.mesh.ui.common.components.PreferenceCategory
|
|||
import com.geeksville.mesh.ui.common.components.PreferenceFooter
|
||||
import com.geeksville.mesh.ui.common.components.SwitchPreference
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
import org.meshtastic.core.strings.R
|
||||
|
||||
@Composable
|
||||
fun CannedMessageConfigScreen(viewModel: RadioConfigViewModel = hiltViewModel()) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Ładowanie…
Reference in New Issue