mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat(ui): Refactor node position details into separate section (#3382)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
b2ff4483c8
commit
8baf8714d0
42 changed files with 1967 additions and 1193 deletions
|
|
@ -29,6 +29,7 @@ import org.meshtastic.core.ui.theme.GraphColors.Pink
|
|||
import org.meshtastic.core.ui.theme.GraphColors.Purple
|
||||
import org.meshtastic.core.ui.theme.GraphColors.Red
|
||||
import org.meshtastic.core.ui.theme.GraphColors.Yellow
|
||||
import org.meshtastic.feature.node.model.TimeFrame
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
enum class Environment(val color: Color) {
|
||||
|
|
@ -83,7 +84,7 @@ data class EnvironmentMetricsState(val environmentMetrics: List<TelemetryProtos.
|
|||
fun hasEnvironmentMetrics() = environmentMetrics.isNotEmpty()
|
||||
|
||||
/**
|
||||
* Filters [environmentMetrics] based on a [TimeFrame].
|
||||
* Filters [environmentMetrics] based on a [org.meshtastic.feature.node.model.TimeFrame].
|
||||
*
|
||||
* @param timeFrame used to filter
|
||||
* @return [EnvironmentGraphingData]
|
||||
|
|
|
|||
|
|
@ -19,21 +19,15 @@ package com.geeksville.mesh.model
|
|||
|
||||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.toRoute
|
||||
import com.geeksville.mesh.ConfigProtos.Config.DisplayConfig.DisplayUnits
|
||||
import com.geeksville.mesh.CoroutineDispatchers
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
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.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.util.safeNumber
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -55,11 +49,9 @@ import org.meshtastic.core.data.repository.FirmwareReleaseRepository
|
|||
import org.meshtastic.core.data.repository.MeshLogRepository
|
||||
import org.meshtastic.core.data.repository.NodeRepository
|
||||
import org.meshtastic.core.data.repository.RadioConfigRepository
|
||||
import org.meshtastic.core.database.entity.FirmwareRelease
|
||||
import org.meshtastic.core.database.entity.MeshLog
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.DeviceHardware
|
||||
import org.meshtastic.core.navigation.NodesRoutes
|
||||
import org.meshtastic.core.prefs.map.MapPrefs
|
||||
import org.meshtastic.core.proto.toPosition
|
||||
|
|
@ -67,127 +59,18 @@ import org.meshtastic.core.service.ServiceAction
|
|||
import org.meshtastic.core.service.ServiceRepository
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.feature.map.model.CustomTileSource
|
||||
import org.meshtastic.feature.node.model.MetricsState
|
||||
import org.meshtastic.feature.node.model.TimeFrame
|
||||
import timber.log.Timber
|
||||
import java.io.BufferedWriter
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.FileWriter
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val DEFAULT_ID_SUFFIX_LENGTH = 4
|
||||
|
||||
data class MetricsState(
|
||||
val isLocal: Boolean = false,
|
||||
val isManaged: Boolean = true,
|
||||
val isFahrenheit: Boolean = false,
|
||||
val displayUnits: DisplayUnits = DisplayUnits.METRIC,
|
||||
val node: Node? = null,
|
||||
val deviceMetrics: List<Telemetry> = emptyList(),
|
||||
val signalMetrics: List<MeshPacket> = emptyList(),
|
||||
val powerMetrics: List<Telemetry> = emptyList(),
|
||||
val hostMetrics: List<Telemetry> = emptyList(),
|
||||
val tracerouteRequests: List<MeshLog> = emptyList(),
|
||||
val tracerouteResults: List<MeshLog> = emptyList(),
|
||||
val positionLogs: List<Position> = emptyList(),
|
||||
val deviceHardware: DeviceHardware? = null,
|
||||
val isLocalDevice: Boolean = false,
|
||||
val firmwareEdition: MeshProtos.FirmwareEdition? = null,
|
||||
val latestStableFirmware: FirmwareRelease = FirmwareRelease(),
|
||||
val latestAlphaFirmware: FirmwareRelease = FirmwareRelease(),
|
||||
val paxMetrics: List<MeshLog> = emptyList(),
|
||||
) {
|
||||
fun hasDeviceMetrics() = deviceMetrics.isNotEmpty()
|
||||
|
||||
fun hasSignalMetrics() = signalMetrics.isNotEmpty()
|
||||
|
||||
fun hasPowerMetrics() = powerMetrics.isNotEmpty()
|
||||
|
||||
fun hasTracerouteLogs() = tracerouteRequests.isNotEmpty()
|
||||
|
||||
fun hasPositionLogs() = positionLogs.isNotEmpty()
|
||||
|
||||
fun hasHostMetrics() = hostMetrics.isNotEmpty()
|
||||
|
||||
fun hasPaxMetrics() = paxMetrics.isNotEmpty()
|
||||
|
||||
fun deviceMetricsFiltered(timeFrame: TimeFrame): List<Telemetry> {
|
||||
val oldestTime = timeFrame.calculateOldestTime()
|
||||
return deviceMetrics.filter { it.time >= oldestTime }
|
||||
}
|
||||
|
||||
fun signalMetricsFiltered(timeFrame: TimeFrame): List<MeshPacket> {
|
||||
val oldestTime = timeFrame.calculateOldestTime()
|
||||
return signalMetrics.filter { it.rxTime >= oldestTime }
|
||||
}
|
||||
|
||||
fun powerMetricsFiltered(timeFrame: TimeFrame): List<Telemetry> {
|
||||
val oldestTime = timeFrame.calculateOldestTime()
|
||||
return powerMetrics.filter { it.time >= oldestTime }
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Empty = MetricsState()
|
||||
}
|
||||
}
|
||||
|
||||
/** Supported time frames used to display data. */
|
||||
@Suppress("MagicNumber")
|
||||
enum class TimeFrame(val seconds: Long, @StringRes val strRes: Int) {
|
||||
TWENTY_FOUR_HOURS(TimeUnit.DAYS.toSeconds(1), R.string.twenty_four_hours),
|
||||
FORTY_EIGHT_HOURS(TimeUnit.DAYS.toSeconds(2), R.string.forty_eight_hours),
|
||||
ONE_WEEK(TimeUnit.DAYS.toSeconds(7), R.string.one_week),
|
||||
TWO_WEEKS(TimeUnit.DAYS.toSeconds(14), R.string.two_weeks),
|
||||
FOUR_WEEKS(TimeUnit.DAYS.toSeconds(28), R.string.four_weeks),
|
||||
MAX(0L, R.string.max),
|
||||
;
|
||||
|
||||
fun calculateOldestTime(): Long = if (this == MAX) {
|
||||
MAX.seconds
|
||||
} else {
|
||||
System.currentTimeMillis() / 1000 - this.seconds
|
||||
}
|
||||
|
||||
/**
|
||||
* The time interval to draw the vertical lines representing time on the x-axis.
|
||||
*
|
||||
* @return seconds epoch seconds
|
||||
*/
|
||||
fun lineInterval(): Long = when (this.ordinal) {
|
||||
TWENTY_FOUR_HOURS.ordinal -> TimeUnit.HOURS.toSeconds(6)
|
||||
|
||||
FORTY_EIGHT_HOURS.ordinal -> TimeUnit.HOURS.toSeconds(12)
|
||||
|
||||
ONE_WEEK.ordinal,
|
||||
TWO_WEEKS.ordinal,
|
||||
-> TimeUnit.DAYS.toSeconds(1)
|
||||
|
||||
else -> TimeUnit.DAYS.toSeconds(7)
|
||||
}
|
||||
|
||||
/** Used to detect a significant time separation between [Telemetry]s. */
|
||||
fun timeThreshold(): Long = when (this.ordinal) {
|
||||
TWENTY_FOUR_HOURS.ordinal -> TimeUnit.HOURS.toSeconds(6)
|
||||
|
||||
FORTY_EIGHT_HOURS.ordinal -> TimeUnit.HOURS.toSeconds(12)
|
||||
|
||||
else -> TimeUnit.DAYS.toSeconds(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the needed [Dp] depending on the amount of time being plotted.
|
||||
*
|
||||
* @param time in seconds
|
||||
*/
|
||||
fun dp(screenWidth: Int, time: Long): Dp {
|
||||
val timePerScreen = this.lineInterval()
|
||||
val multiplier = time / timePerScreen
|
||||
val dp = (screenWidth * multiplier).toInt().dp
|
||||
return dp.takeIf { it != 0.dp } ?: screenWidth.dp
|
||||
}
|
||||
}
|
||||
|
||||
private fun MeshPacket.hasValidSignal(): Boolean =
|
||||
rxTime > 0 && (rxSnr != 0f && rxRssi != 0) && (hopStart > 0 && hopStart - hopLimit == 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.geeksville.mesh.TelemetryProtos
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.MAX_PERCENT_VALUE
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC
|
||||
|
|
@ -79,6 +78,7 @@ import org.meshtastic.core.ui.theme.AppTheme
|
|||
import org.meshtastic.core.ui.theme.GraphColors.Cyan
|
||||
import org.meshtastic.core.ui.theme.GraphColors.Green
|
||||
import org.meshtastic.core.ui.theme.GraphColors.Magenta
|
||||
import org.meshtastic.feature.node.model.TimeFrame
|
||||
|
||||
private const val CHART_WEIGHT = 1f
|
||||
private const val Y_AXIS_WEIGHT = 0.1f
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@ import androidx.compose.ui.unit.dp
|
|||
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
|
||||
import org.meshtastic.feature.node.model.TimeFrame
|
||||
|
||||
private const val CHART_WEIGHT = 1f
|
||||
private const val Y_AXIS_WEIGHT = 0.1f
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ import com.geeksville.mesh.TelemetryProtos
|
|||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.DATE_TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.metrics.CommonCharts.MS_PER_SEC
|
||||
import org.meshtastic.core.model.util.UnitConversions.celsiusToFahrenheit
|
||||
|
|
@ -63,6 +62,7 @@ import org.meshtastic.core.ui.component.IndoorAirQuality
|
|||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.component.OptionLabel
|
||||
import org.meshtastic.core.ui.component.SlidingSelector
|
||||
import org.meshtastic.feature.node.model.TimeFrame
|
||||
|
||||
@Composable
|
||||
fun EnvironmentMetricsScreen(viewModel: MetricsViewModel = hiltViewModel(), onNavigateUp: () -> Unit) {
|
||||
|
|
|
|||
|
|
@ -57,13 +57,13 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.geeksville.mesh.PaxcountProtos
|
||||
import com.geeksville.mesh.Portnums.PortNum
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
import org.meshtastic.core.database.entity.MeshLog
|
||||
import org.meshtastic.core.model.util.formatUptime
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.MainAppBar
|
||||
import org.meshtastic.core.ui.component.OptionLabel
|
||||
import org.meshtastic.core.ui.component.SlidingSelector
|
||||
import org.meshtastic.feature.node.model.TimeFrame
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
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
|
||||
|
|
@ -73,6 +72,7 @@ import org.meshtastic.core.ui.component.OptionLabel
|
|||
import org.meshtastic.core.ui.component.SlidingSelector
|
||||
import org.meshtastic.core.ui.theme.GraphColors.InfantryBlue
|
||||
import org.meshtastic.core.ui.theme.GraphColors.Red
|
||||
import org.meshtastic.feature.node.model.TimeFrame
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.floor
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.geeksville.mesh.MeshProtos.MeshPacket
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
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
|
||||
|
|
@ -69,6 +68,7 @@ import org.meshtastic.core.ui.component.MainAppBar
|
|||
import org.meshtastic.core.ui.component.OptionLabel
|
||||
import org.meshtastic.core.ui.component.SlidingSelector
|
||||
import org.meshtastic.core.ui.component.SnrAndRssi
|
||||
import org.meshtastic.feature.node.model.TimeFrame
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private enum class Metric(val color: Color, val min: Float, val max: Float) {
|
||||
|
|
|
|||
170
app/src/main/java/com/geeksville/mesh/ui/node/NodeDetailList.kt
Normal file
170
app/src/main/java/com/geeksville/mesh/ui/node/NodeDetailList.kt
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.ui.node
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.geeksville.mesh.ui.sharing.SharedContactDialog
|
||||
import org.meshtastic.core.database.entity.FirmwareRelease
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.core.strings.R
|
||||
import org.meshtastic.core.ui.component.TitledCard
|
||||
import org.meshtastic.core.ui.component.preview.NodePreviewParameterProvider
|
||||
import org.meshtastic.core.ui.theme.AppTheme
|
||||
import org.meshtastic.feature.node.component.AdministrationSection
|
||||
import org.meshtastic.feature.node.component.DeviceActions
|
||||
import org.meshtastic.feature.node.component.DeviceDetailsSection
|
||||
import org.meshtastic.feature.node.component.FirmwareReleaseSheetContent
|
||||
import org.meshtastic.feature.node.component.MetricsSection
|
||||
import org.meshtastic.feature.node.component.NodeDetailsSection
|
||||
import org.meshtastic.feature.node.component.NotesSection
|
||||
import org.meshtastic.feature.node.component.PositionSection
|
||||
import org.meshtastic.feature.node.model.LogsType
|
||||
import org.meshtastic.feature.node.model.MetricsState
|
||||
import org.meshtastic.feature.node.model.NodeDetailAction
|
||||
|
||||
@Composable
|
||||
fun NodeDetailContent(
|
||||
node: Node,
|
||||
ourNode: Node?,
|
||||
metricsState: MetricsState,
|
||||
lastTracerouteTime: Long?,
|
||||
availableLogs: Set<LogsType>,
|
||||
onAction: (NodeDetailAction) -> Unit,
|
||||
onSaveNotes: (nodeNum: Int, notes: String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var showShareDialog by remember { mutableStateOf(false) }
|
||||
if (showShareDialog) {
|
||||
SharedContactDialog(node) { showShareDialog = false }
|
||||
}
|
||||
|
||||
NodeDetailList(
|
||||
node = node,
|
||||
lastTracerouteTime = lastTracerouteTime,
|
||||
ourNode = ourNode,
|
||||
metricsState = metricsState,
|
||||
onAction = { action ->
|
||||
if (action is NodeDetailAction.ShareContact) {
|
||||
showShareDialog = true
|
||||
} else {
|
||||
onAction(action)
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
availableLogs = availableLogs,
|
||||
onSaveNotes = onSaveNotes,
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NodeDetailList(
|
||||
node: Node,
|
||||
lastTracerouteTime: Long?,
|
||||
ourNode: Node?,
|
||||
metricsState: MetricsState,
|
||||
onAction: (NodeDetailAction) -> Unit,
|
||||
availableLogs: Set<LogsType>,
|
||||
onSaveNotes: (Int, String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var showFirmwareSheet by remember { mutableStateOf(false) }
|
||||
var selectedFirmware by remember { mutableStateOf<FirmwareRelease?>(null) }
|
||||
|
||||
if (showFirmwareSheet) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = false)
|
||||
ModalBottomSheet(onDismissRequest = { showFirmwareSheet = false }, sheetState = sheetState) {
|
||||
selectedFirmware?.let { FirmwareReleaseSheetContent(firmwareRelease = it) }
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
if (metricsState.deviceHardware != null) {
|
||||
TitledCard(title = stringResource(R.string.device)) { DeviceDetailsSection(metricsState) }
|
||||
}
|
||||
|
||||
NodeDetailsSection(node)
|
||||
NotesSection(node = node, onSaveNotes = onSaveNotes)
|
||||
|
||||
DeviceActions(
|
||||
isLocal = metricsState.isLocal,
|
||||
lastTracerouteTime = lastTracerouteTime,
|
||||
node = node,
|
||||
onAction = onAction,
|
||||
)
|
||||
|
||||
PositionSection(
|
||||
node = node,
|
||||
ourNode = ourNode,
|
||||
metricsState = metricsState,
|
||||
availableLogs = availableLogs,
|
||||
onAction = onAction,
|
||||
)
|
||||
|
||||
MetricsSection(node, metricsState, availableLogs, onAction)
|
||||
|
||||
if (!metricsState.isManaged) {
|
||||
AdministrationSection(
|
||||
node = node,
|
||||
metricsState = metricsState,
|
||||
onAction = onAction,
|
||||
onFirmwareSelect = { firmware ->
|
||||
selectedFirmware = firmware
|
||||
showFirmwareSheet = true
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun NodeDetailsPreview(@PreviewParameter(NodePreviewParameterProvider::class) node: Node) {
|
||||
AppTheme {
|
||||
NodeDetailList(
|
||||
node = node,
|
||||
ourNode = node,
|
||||
lastTracerouteTime = null,
|
||||
metricsState = MetricsState.Companion.Empty,
|
||||
availableLogs = emptySet(),
|
||||
onAction = {},
|
||||
onSaveNotes = { _, _ -> },
|
||||
)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,121 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="909.88dp"
|
||||
android:height="546.86dp"
|
||||
android:viewportWidth="909.88"
|
||||
android:viewportHeight="546.86">
|
||||
<path
|
||||
android:pathData="m898.52,135.44h4.69a5.67,5.67 0,0 1,5.67 5.67v84.65a5.67,5.67 0,0 1,-5.67 5.67h-4.69"
|
||||
android:fillColor="#9f9f9e"/>
|
||||
<path
|
||||
android:pathData="M12.7,104.75L886.82,104.75A11.7,11.7 0,0 1,898.52 116.45L898.52,534.16A11.7,11.7 0,0 1,886.82 545.86L12.7,545.86A11.7,11.7 0,0 1,1 534.16L1,116.45A11.7,11.7 0,0 1,12.7 104.75z"
|
||||
android:fillColor="#cbcccb"/>
|
||||
<path
|
||||
android:pathData="m34.47,104.75v113.48a3.67,3.67 0,0 0,3.67 3.67h41a2.35,2.35 0,0 1,2.35 2.35L81.49,545.86L870.95,545.86L870.95,104.75ZM845.99,520.86L106.53,520.86L106.53,213.96a17.06,17.06 0,0 0,-17.06 -17.06h-27.5a2.5,2.5 0,0 1,-2.5 -2.5v-62.15a2.5,2.5 0,0 1,2.5 -2.5h784z"
|
||||
android:fillColor="#9f9f9e"/>
|
||||
<path
|
||||
android:pathData="M845.99,129.75L845.99,520.86L106.53,520.86L106.53,213.96a17,17 0,0 0,-7.2 -13.92v-70.29z"
|
||||
android:fillColor="#cbcccb"/>
|
||||
<path
|
||||
android:pathData="m99.33,129.75v70.29a17,17 0,0 0,-9.86 -3.14h-27.5a2.5,2.5 0,0 1,-2.5 -2.5v-62.15a2.5,2.5 0,0 1,2.5 -2.5z"
|
||||
android:fillColor="#b7b7b7"/>
|
||||
<path
|
||||
android:pathData="M25.45,253.39h13.53v148.4h-13.53z"
|
||||
android:fillColor="#9f9f9e"/>
|
||||
<path
|
||||
android:pathData="m430.64,95.71h71.71a2.55,2.55 0,0 1,2.55 2.55v6.48h-76.8v-6.48a2.55,2.55 0,0 1,2.54 -2.55z"
|
||||
android:fillColor="#b1a368"/>
|
||||
<path
|
||||
android:pathData="m436.27,3.17h60.88a6.2,4.85 0,0 1,6.2 4.85v77.33h-73.28v-77.33a6.2,4.85 0,0 1,6.2 -4.85z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="M425.88,20.14L506.28,20.14A5.55,5.55 0,0 1,511.83 25.69L511.83,70.55A5.55,5.55 0,0 1,506.28 76.1L425.88,76.1A5.55,5.55 0,0 1,420.33 70.55L420.33,25.69A5.55,5.55 0,0 1,425.88 20.14z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="m511.8,24.48v47.25a5.52,4.31 0,0 1,-5.55 4.34h-80.37a5.55,4.34 0,0 1,-5.59 -4.34v-47.25a5.55,4.34 0,0 1,5.59 -4.34h80.51a5.52,4.31 0,0 1,5.41 4.34z"
|
||||
android:strokeWidth="3.16706"
|
||||
android:fillColor="#9f9f9e"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="M433.29,85.68h65.99v10.03h-65.99z"
|
||||
android:fillColor="#b1a368"/>
|
||||
<path
|
||||
android:pathData="M845.99,129.75L845.99,520.86L106.53,520.86L106.53,213.96a17.06,17.06 0,0 0,-17.06 -17.06h-27.5a2.5,2.5 0,0 1,-2.5 -2.5v-62.15a2.5,2.5 0,0 1,2.5 -2.5h784m25,-25L34.47,104.75v113.48a3.68,3.68 0,0 0,3.67 3.67h41a2.35,2.35 0,0 1,2.35 2.35L81.49,545.86L870.95,545.86L870.95,104.75Z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="M99.34,200.04L99.34,129.75"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="M25.45,253.39h13.53v148.4h-13.53z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="m898.52,135.44h4.69a5.67,5.67 0,0 1,5.67 5.67v84.65a5.67,5.67 0,0 1,-5.67 5.67h-4.69"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="m430.64,95.71h71.71a2.55,2.55 0,0 1,2.55 2.55v6.48h-76.8v-6.48a2.55,2.55 0,0 1,2.54 -2.55z"
|
||||
android:strokeWidth="2.04"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="M433.29,85.68h65.99v10.03h-65.99z"
|
||||
android:strokeWidth="1.99"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="m78.62,152.33a14,14 0,1 0,14 14,13.95 13.95,0 0,0 -14,-14zM78.62,173.83a7.55,7.55 0,1 1,7.54 -7.55,7.55 7.55,0 0,1 -7.54,7.55z"
|
||||
android:fillColor="#9f9f9e"/>
|
||||
<path
|
||||
android:pathData="M78.62,166.28m-7.55,0a7.55,7.55 0,1 1,15.1 0a7.55,7.55 0,1 1,-15.1 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="M78.62,166.28m-13.95,0a13.95,13.95 0,1 1,27.9 0a13.95,13.95 0,1 1,-27.9 0"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="m445.36,440.05c0,11.52 10.38,20.86 23.19,20.86 12.81,0 23.19,-9.34 23.19,-20.86 0,-11.52 -10.38,-20.86 -23.19,-20.86 -12.81,0 -23.19,9.34 -23.19,20.86z"
|
||||
android:strokeWidth="0.458227"
|
||||
android:fillColor="#4d4d4d"/>
|
||||
<path
|
||||
android:pathData="m469.4,538.4c-119.83,0 -217.32,-93.41 -217.32,-208.23 0,-114.82 97.48,-208.23 217.32,-208.23 119.83,0 217.32,93.41 217.32,208.23 0,114.82 -97.48,208.23 -217.32,208.23zM469.4,151.82c-102.64,0 -186.13,80.01 -186.13,178.35 0,98.33 83.5,178.35 186.13,178.35 102.62,0 186.13,-80.02 186.13,-178.35 0,-98.34 -83.51,-178.35 -186.13,-178.35z"
|
||||
android:strokeWidth="0.474832"
|
||||
android:fillColor="#4d4d4d"/>
|
||||
<path
|
||||
android:pathData="m468.56,391.97c-8.54,0 -15.46,-6.23 -15.46,-13.91v-23.51c0,-22.75 19.33,-40.13 36.4,-55.47 12.51,-11.26 25.45,-22.89 25.45,-32.16 0,-23.18 -20.81,-42.04 -46.39,-42.04 -26.01,0 -46.39,18.05 -46.39,41.09 0,7.68 -6.93,13.91 -15.46,13.91 -8.54,0 -15.46,-6.23 -15.46,-13.91 0,-37.99 34.68,-68.9 77.31,-68.9 42.63,0 77.31,31.33 77.31,69.85 0,20.82 -17.55,36.59 -34.51,51.84 -13.45,12.07 -27.34,24.56 -27.34,35.78v23.51c0,7.68 -6.93,13.92 -15.46,13.92z"
|
||||
android:strokeWidth="0.458227"
|
||||
android:fillColor="#4d4d4d"
|
||||
android:strokeColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M12.7,104.75L886.82,104.75A11.7,11.7 0,0 1,898.52 116.45L898.52,534.16A11.7,11.7 0,0 1,886.82 545.86L12.7,545.86A11.7,11.7 0,0 1,1 534.16L1,116.45A11.7,11.7 0,0 1,12.7 104.75z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#050606"/>
|
||||
<path
|
||||
android:pathData="m107.42,363.03 l-0.24,-156.31 -2.99,-3.72 -2.99,-3.72v-34.09,-34.09l150.65,0.05 150.65,0.05 -8.28,3.07c-19.32,7.16 -34.46,14.82 -50.22,25.41 -50.58,33.98 -84.36,88.87 -91.06,147.96 -1.43,12.63 -0.64,44.7 1.39,55.76 7.76,42.44 25.98,77.93 55.68,108.42 17.38,17.85 33.99,30.3 55.43,41.55l11.31,5.93 -134.54,0.02 -134.54,0.02z"
|
||||
android:strokeWidth="0.92"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:pathData="m107.42,363.03 l-0.24,-156.31 -2.99,-3.72 -2.99,-3.72v-34.09,-34.09l150.65,0.05 150.65,0.05 -8.28,3.07c-19.32,7.16 -34.46,14.82 -50.22,25.41 -50.58,33.98 -84.36,88.87 -91.06,147.96 -1.43,12.63 -0.64,44.7 1.39,55.76 7.76,42.44 25.98,77.93 55.68,108.42 17.38,17.85 33.99,30.3 55.43,41.55l11.31,5.93 -134.54,0.02 -134.54,0.02z"
|
||||
android:strokeWidth="0.92"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:pathData="m107.42,363.03 l-0.24,-156.31 -2.99,-3.72 -2.99,-3.72v-34.09,-34.09l150.65,0.05 150.65,0.05 -8.28,3.07c-19.32,7.16 -34.46,14.82 -50.22,25.41 -50.58,33.98 -84.36,88.87 -91.06,147.96 -1.43,12.63 -0.64,44.7 1.39,55.76 7.76,42.44 25.98,77.93 55.68,108.42 17.38,17.85 33.99,30.3 55.43,41.55l11.31,5.93 -134.54,0.02 -134.54,0.02z"
|
||||
android:strokeWidth="0.92"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillAlpha="0"/>
|
||||
</vector>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M21,11a1,1 0,0 1,1 1a10,10 0,0 1,-5 8.656a1,1 0,0 1,-1.302 -0.268l-0.064,-0.098l-3,-5.19a0.995,0.995 0,0 1,-0.133 -0.542l0.01,-0.11l0.023,-0.106l0.034,-0.106l0.046,-0.1l0.056,-0.094l0.067,-0.089a0.994,0.994 0,0 1,0.165 -0.155l0.098,-0.064a2,2 0,0 0,0.993 -1.57l0.007,-0.163a1,1 0,0 1,0.883 -0.994l0.117,-0.007h6z" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M7,3.344a10,10 0,0 1,10 0a1,1 0,0 1,0.418 1.262l-0.052,0.104l-3,5.19l-0.064,0.098a0.994,0.994 0,0 1,-0.155 0.165l-0.089,0.067a1,1 0,0 1,-0.195 0.102l-0.105,0.034l-0.107,0.022a1.003,1.003 0,0 1,-0.547 -0.07l-0.104,-0.052a2,2 0,0 0,-1.842 -0.082l-0.158,0.082a1,1 0,0 1,-1.302 -0.268l-0.064,-0.098l-3,-5.19a1,1 0,0 1,0.366 -1.366z" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,11a1,1 0,0 1,0.993 0.884l0.007,0.117a2,2 0,0 0,0.861 1.645l0.237,0.152a0.994,0.994 0,0 1,0.165 0.155l0.067,0.089l0.056,0.095l0.045,0.099c0.014,0.036 0.026,0.07 0.035,0.106l0.022,0.107l0.011,0.11a0.994,0.994 0,0 1,-0.08 0.437l-0.053,0.104l-3,5.19a1,1 0,0 1,-1.366 0.366a10,10 0,0 1,-5 -8.656a1,1 0,0 1,0.883 -0.993l0.117,-0.007h6z" />
|
||||
</vector>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:pathData="M620,440q-25,0 -42.5,-17.5T560,380q0,-17 9.5,-34.5t20.5,-32q11,-14.5 20.5,-24l9.5,-9.5 9.5,9.5q9.5,9.5 20.5,24t20.5,32Q680,363 680,380q0,25 -17.5,42.5T620,440ZM780,320q-25,0 -42.5,-17.5T720,260q0,-17 9.5,-34.5t20.5,-32q11,-14.5 20.5,-24l9.5,-9.5 9.5,9.5q9.5,9.5 20.5,24t20.5,32Q840,243 840,260q0,25 -17.5,42.5T780,320ZM780,560q-25,0 -42.5,-17.5T720,500q0,-17 9.5,-34.5t20.5,-32q11,-14.5 20.5,-24l9.5,-9.5 9.5,9.5q9.5,9.5 20.5,24t20.5,32Q840,483 840,500q0,25 -17.5,42.5T780,560ZM360,840q-83,0 -141.5,-58.5T160,640q0,-48 21,-89.5t59,-70.5v-240q0,-50 35,-85t85,-35q50,0 85,35t35,85v240q38,29 59,70.5t21,89.5q0,83 -58.5,141.5T360,840ZM240,640h240q0,-29 -12.5,-54T432,544l-32,-24v-280q0,-17 -11.5,-28.5T360,200q-17,0 -28.5,11.5T320,240v280l-32,24q-23,17 -35.5,42T240,640Z"
|
||||
android:fillColor="#e8eaed"/>
|
||||
</vector>
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="200dp" android:viewportHeight="32" android:viewportWidth="32" android:width="200dp">
|
||||
|
||||
<path android:fillColor="#000000" android:pathData="M24.5,30a5.202,5.202 0,0 1,-4.626 -8.08L23.49,16.538a1.217,1.217 0,0 1,2.02 0L29.06,21.815A5.492,5.492 0,0 1,30 24.751,5.385 5.385,0 0,1 24.5,30ZM24.5,18.62 L21.564,22.987A3.208,3.208 0,0 0,24.5 28,3.385 3.385,0 0,0 28,24.751a3.435,3.435 0,0 0,-0.63 -1.867Z"/>
|
||||
|
||||
<path android:fillColor="#000000" android:pathData="M11,16V11h1a4.004,4.004 0,0 0,4 -4V4H13a3.978,3.978 0,0 0,-2.747 1.107A6.003,6.003 0,0 0,5 2H2V5a6.007,6.007 0,0 0,6 6H9v5H2v2H16V16ZM13,6h1V7a2.002,2.002 0,0 1,-2 2H11V8A2.002,2.002 0,0 1,13 6ZM8,9A4.004,4.004 0,0 1,4 5V4H5A4.004,4.004 0,0 1,9 8V9Z"/>
|
||||
|
||||
<path android:fillColor="#000000" android:pathData="M2,21h14v2h-14z"/>
|
||||
|
||||
<path android:fillColor="#000000" android:pathData="M2,26h14v2h-14z"/>
|
||||
|
||||
</vector>
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="200dp" android:viewportHeight="32" android:viewportWidth="32" android:width="200dp">
|
||||
|
||||
<path android:fillColor="#000000" android:pathData="M11,16V11h1a4.004,4.004 0,0 0,4 -4V4H13a3.978,3.978 0,0 0,-2.747 1.107A6.003,6.003 0,0 0,5 2H2V5a6.007,6.007 0,0 0,6 6H9v5H2v2H16V16ZM13,6h1V7a2.002,2.002 0,0 1,-2 2H11V8A2.002,2.002 0,0 1,13 6ZM8,9A4.004,4.004 0,0 1,4 5V4H5A4.004,4.004 0,0 1,9 8V9Z"/>
|
||||
|
||||
<path android:fillColor="#000000" android:pathData="M2,21h14v2h-14z"/>
|
||||
|
||||
<path android:fillColor="#000000" android:pathData="M2,26h14v2h-14z"/>
|
||||
|
||||
<path android:fillColor="#000000" android:pathData="M25,30a4.986,4.986 0,0 1,-3 -8.98L22,15a3,3 0,0 1,6 0v6.02A4.986,4.986 0,0 1,25 30ZM25,14a1.001,1.001 0,0 0,-1 1v7.13l-0.497,0.289A2.968,2.968 0,0 0,22 25a3,3 0,0 0,6 0,2.968 2.968,0 0,0 -1.503,-2.581L26,22.13L26,15A1.001,1.001 0,0 0,25 14Z"/>
|
||||
|
||||
</vector>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M23,11.99l-2.44,-2.79l0.34,-3.69l-3.61,-0.82L15.4,1.5L12,2.96L8.6,1.5L6.71,4.69L3.1,5.5L3.44,9.2L1,11.99l2.44,2.79l-0.34,3.7l3.61,0.82L8.6,22.5l3.4,-1.47l3.4,1.46l1.89,-3.19l3.61,-0.82l-0.34,-3.69L23,11.99zM19.05,13.47l-0.56,0.65l0.08,0.85l0.18,1.95l-1.9,0.43l-0.84,0.19l-0.44,0.74l-0.99,1.68l-1.78,-0.77L12,18.85l-0.79,0.34l-1.78,0.77l-0.99,-1.67l-0.44,-0.74l-0.84,-0.19l-1.9,-0.43l0.18,-1.96l0.08,-0.85l-0.56,-0.65l-1.29,-1.47l1.29,-1.48l0.56,-0.65L5.43,9.01L5.25,7.07l1.9,-0.43l0.84,-0.19l0.44,-0.74l0.99,-1.68l1.78,0.77L12,5.14l0.79,-0.34l1.78,-0.77l0.99,1.68l0.44,0.74l0.84,0.19l1.9,0.43l-0.18,1.95l-0.08,0.85l0.56,0.65l1.29,1.47L19.05,13.47z"
|
||||
android:fillColor="#e3e3e3"/>
|
||||
</vector>
|
||||
Loading…
Add table
Add a link
Reference in a new issue