mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Merge branch 'main' into release/2.7.0
This commit is contained in:
commit
f287e35a69
59 changed files with 727 additions and 584 deletions
180
.github/renovate.json
vendored
180
.github/renovate.json
vendored
|
|
@ -17,7 +17,10 @@
|
|||
},
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": ["minor", "patch"],
|
||||
"matchUpdateTypes": [
|
||||
"minor",
|
||||
"patch"
|
||||
],
|
||||
"matchCurrentVersion": "!/^0/",
|
||||
"automerge": true
|
||||
},
|
||||
|
|
@ -37,153 +40,194 @@
|
|||
},
|
||||
{
|
||||
"description": "Group all AndroidX dependencies (excluding more specific AndroidX groups)",
|
||||
"matchPackagePatterns": ["^androidx\\."],
|
||||
"excludePackagePatterns": [
|
||||
"^androidx\\.room",
|
||||
"^androidx\\.lifecycle",
|
||||
"^androidx\\.navigation",
|
||||
"^androidx\\.datastore",
|
||||
"^androidx\\.compose\\.material3\\.adaptive",
|
||||
"^androidx\\.compose\\.material3:material3-adaptive-navigation-suite$",
|
||||
"^androidx\\.test\\.espresso",
|
||||
"^androidx\\.test\\.ext",
|
||||
"^androidx\\.compose\\.ui:ui-test-junit4$",
|
||||
"^androidx\\.hilt"
|
||||
],
|
||||
"groupName": "AndroidX (General)",
|
||||
"groupSlug": "androidx-general"
|
||||
"groupSlug": "androidx-general",
|
||||
"matchPackageNames": [
|
||||
"/^androidx\\./",
|
||||
"!/^androidx\\.room/",
|
||||
"!/^androidx\\.lifecycle/",
|
||||
"!/^androidx\\.navigation/",
|
||||
"!/^androidx\\.datastore/",
|
||||
"!/^androidx\\.compose\\.material3\\.adaptive/",
|
||||
"!/^androidx\\.compose\\.material3:material3-adaptive-navigation-suite$/",
|
||||
"!/^androidx\\.test\\.espresso/",
|
||||
"!/^androidx\\.test\\.ext/",
|
||||
"!/^androidx\\.compose\\.ui:ui-test-junit4$/",
|
||||
"!/^androidx\\.hilt/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Kotlin standard library, coroutines, and serialization",
|
||||
"matchPackagePatterns": ["^org\\.jetbrains\\.kotlin", "^org\\.jetbrains\\.kotlinx"],
|
||||
"groupName": "Kotlin Ecosystem",
|
||||
"groupSlug": "kotlin"
|
||||
"groupSlug": "kotlin",
|
||||
"matchPackageNames": [
|
||||
"/^org\\.jetbrains\\.kotlin/",
|
||||
"/^org\\.jetbrains\\.kotlinx/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Dagger and Hilt dependencies",
|
||||
"matchPackagePatterns": ["^com\\.google\\.dagger", "^androidx\\.hilt"],
|
||||
"groupName": "Dagger & Hilt",
|
||||
"groupSlug": "hilt"
|
||||
"groupSlug": "hilt",
|
||||
"matchPackageNames": [
|
||||
"/^com\\.google\\.dagger/",
|
||||
"/^androidx\\.hilt/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Accompanist libraries",
|
||||
"matchPackagePatterns": ["^com\\.google\\.accompanist"],
|
||||
"groupName": "Accompanist",
|
||||
"groupSlug": "accompanist"
|
||||
"groupSlug": "accompanist",
|
||||
"matchPackageNames": [
|
||||
"/^com\\.google\\.accompanist/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group JVM testing libraries (JUnit, Mockito, Robolectric)",
|
||||
"matchPackagePatterns": [
|
||||
"^junit:junit$",
|
||||
"^org\\.mockito:",
|
||||
"^org\\.robolectric:robolectric$"
|
||||
],
|
||||
"groupName": "JVM Testing Libraries",
|
||||
"groupSlug": "jvm-testing"
|
||||
"groupSlug": "jvm-testing",
|
||||
"matchPackageNames": [
|
||||
"/^junit:junit$/",
|
||||
"/^org\\.mockito:/",
|
||||
"/^org\\.robolectric:robolectric$/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group AndroidX Testing libraries",
|
||||
"matchPackagePatterns": [
|
||||
"^androidx\\.test\\.espresso",
|
||||
"^androidx\\.test\\.ext",
|
||||
"^androidx\\.compose\\.ui:ui-test-junit4$"
|
||||
],
|
||||
"groupName": "AndroidX Testing",
|
||||
"groupSlug": "androidx-testing"
|
||||
"groupSlug": "androidx-testing",
|
||||
"matchPackageNames": [
|
||||
"/^androidx\\.test\\.espresso/",
|
||||
"/^androidx\\.test\\.ext/",
|
||||
"/^androidx\\.compose\\.ui:ui-test-junit4$/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Square networking libraries (OkHttp, Retrofit)",
|
||||
"matchPackagePatterns": ["^com\\.squareup\\.okhttp3", "^com\\.squareup\\.retrofit2"],
|
||||
"groupName": "Square Networking",
|
||||
"groupSlug": "square-network"
|
||||
"groupSlug": "square-network",
|
||||
"matchPackageNames": [
|
||||
"/^com\\.squareup\\.okhttp3/",
|
||||
"/^com\\.squareup\\.retrofit2/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Coil image loading library",
|
||||
"matchPackagePatterns": ["^io\\.coil-kt\\.coil3"],
|
||||
"groupName": "Coil",
|
||||
"groupSlug": "coil"
|
||||
"groupSlug": "coil",
|
||||
"matchPackageNames": [
|
||||
"/^io\\.coil-kt\\.coil3/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group ZXing barcode scanning libraries",
|
||||
"matchPackagePatterns": ["^com\\.journeyapps:zxing-android-embedded", "^com\\.google\\.zxing:core"],
|
||||
"groupName": "ZXing",
|
||||
"groupSlug": "zxing"
|
||||
"groupSlug": "zxing",
|
||||
"matchPackageNames": [
|
||||
"/^com\\.journeyapps:zxing-android-embedded/",
|
||||
"/^com\\.google\\.zxing:core/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Eclipse Paho MQTT client libraries",
|
||||
"matchPackagePatterns": ["^org\\.eclipse\\.paho"],
|
||||
"groupName": "MQTT Paho Client",
|
||||
"groupSlug": "mqtt-paho"
|
||||
"groupSlug": "mqtt-paho",
|
||||
"matchPackageNames": [
|
||||
"/^org\\.eclipse\\.paho/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Mike Penz Markdown renderer libraries",
|
||||
"matchPackagePatterns": ["^com\\.mikepenz"],
|
||||
"groupName": "Markdown Renderer (Mike Penz)",
|
||||
"groupSlug": "markdown-renderer-mikepenz"
|
||||
"groupSlug": "markdown-renderer-mikepenz",
|
||||
"matchPackageNames": [
|
||||
"/^com\\.mikepenz/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Firebase libraries",
|
||||
"matchPackagePatterns": ["^com\\.google\\.firebase"],
|
||||
"groupName": "Firebase",
|
||||
"groupSlug": "firebase"
|
||||
"groupSlug": "firebase",
|
||||
"matchPackageNames": [
|
||||
"/^com\\.google\\.firebase/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Datadog libraries",
|
||||
"matchPackagePatterns": ["^com\\.datadoghq"],
|
||||
"groupName": "Datadog",
|
||||
"groupSlug": "datadog"
|
||||
"groupSlug": "datadog",
|
||||
"matchPackageNames": [
|
||||
"/^com\\.datadoghq/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group OpenStreetMap (OSM) libraries",
|
||||
"matchPackagePatterns": ["^org\\.osmdroid", "^com\\.github\\.MKergall\\.osmbonuspack", "^mil\\.nga"],
|
||||
"groupName": "OSM Libraries",
|
||||
"groupSlug": "osm-libraries"
|
||||
"groupSlug": "osm-libraries",
|
||||
"matchPackageNames": [
|
||||
"/^org\\.osmdroid/",
|
||||
"/^com\\.github\\.MKergall\\.osmbonuspack/",
|
||||
"/^mil\\.nga/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Google Maps Compose libraries",
|
||||
"matchPackagePatterns": ["^com\\.google\\.android\\.gms:play-services-location", "^com\\.google\\.maps\\.android"],
|
||||
"groupName": "Google Maps Compose",
|
||||
"groupSlug": "google-maps-compose"
|
||||
"groupSlug": "google-maps-compose",
|
||||
"matchPackageNames": [
|
||||
"/^com\\.google\\.android\\.gms:play-services-location/",
|
||||
"/^com\\.google\\.maps\\.android/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group Google Protobuf runtime libraries",
|
||||
"matchPackagePatterns": ["^com\\.google\\.protobuf"],
|
||||
"excludePackageNames": ["https://github.com/meshtastic/protobufs.git"],
|
||||
"groupName": "Protobuf Runtime",
|
||||
"groupSlug": "protobuf-runtime"
|
||||
"groupSlug": "protobuf-runtime",
|
||||
"matchPackageNames": [
|
||||
"/^com\\.google\\.protobuf/",
|
||||
"!https://github.com/meshtastic/protobufs.git"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group AndroidX Room libraries",
|
||||
"matchPackagePatterns": ["^androidx\\.room"],
|
||||
"groupName": "AndroidX Room",
|
||||
"groupSlug": "androidx-room"
|
||||
"groupSlug": "androidx-room",
|
||||
"matchPackageNames": [
|
||||
"/^androidx\\.room/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group AndroidX Lifecycle libraries",
|
||||
"matchPackagePatterns": ["^androidx\\.lifecycle"],
|
||||
"groupName": "AndroidX Lifecycle",
|
||||
"groupSlug": "androidx-lifecycle"
|
||||
"groupSlug": "androidx-lifecycle",
|
||||
"matchPackageNames": [
|
||||
"/^androidx\\.lifecycle/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group AndroidX Navigation libraries",
|
||||
"matchPackagePatterns": ["^androidx\\.navigation"],
|
||||
"groupName": "AndroidX Navigation",
|
||||
"groupSlug": "androidx-navigation"
|
||||
"groupSlug": "androidx-navigation",
|
||||
"matchPackageNames": [
|
||||
"/^androidx\\.navigation/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group AndroidX DataStore libraries",
|
||||
"matchPackagePatterns": ["^androidx\\.datastore"],
|
||||
"groupName": "AndroidX DataStore",
|
||||
"groupSlug": "androidx-datastore"
|
||||
"groupSlug": "androidx-datastore",
|
||||
"matchPackageNames": [
|
||||
"/^androidx\\.datastore/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Group AndroidX Adaptive UI libraries",
|
||||
"matchPackagePatterns": [
|
||||
"^androidx\\.compose\\.material3\\.adaptive",
|
||||
"^androidx\\.compose\\.material3:material3-adaptive-navigation-suite$"
|
||||
],
|
||||
"groupName": "AndroidX Adaptive UI",
|
||||
"groupSlug": "androidx-adaptive-ui"
|
||||
"groupSlug": "androidx-adaptive-ui",
|
||||
"matchPackageNames": [
|
||||
"/^androidx\\.compose\\.material3\\.adaptive/",
|
||||
"/^androidx\\.compose\\.material3:material3-adaptive-navigation-suite$/"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
.github/workflows/pr_enforce_labels.yml
vendored
2
.github/workflows/pr_enforce_labels.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check for PR labels
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const labels = context.payload.pull_request.labels.map(label => label.name);
|
||||
|
|
|
|||
2
.github/workflows/pull-request-target.yml
vendored
2
.github/workflows/pull-request-target.yml
vendored
|
|
@ -12,4 +12,4 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: label-the-PR
|
||||
uses: actions/labeler@v5
|
||||
uses: actions/labeler@v6
|
||||
|
|
@ -114,24 +114,25 @@ android {
|
|||
)
|
||||
ndk { abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64") }
|
||||
}
|
||||
flavorDimensions.add("default")
|
||||
flavorDimensions += "default"
|
||||
productFlavors {
|
||||
// Read versionCode from defaultConfig after it's been potentially set by ENV or fallback
|
||||
val resolvedVersionCode = defaultConfig.versionCode
|
||||
val resolvedVersionName = defaultConfig.versionName
|
||||
|
||||
create("fdroid") {
|
||||
dimension = "default"
|
||||
dependenciesInfo { includeInApk = false }
|
||||
versionName = "$resolvedVersionName ($resolvedVersionCode) fdroid"
|
||||
}
|
||||
create("google") {
|
||||
dimension = "default"
|
||||
isDefault = true
|
||||
// Enable Firebase Crashlytics for Google Play builds
|
||||
apply(plugin = libs.plugins.google.services.get().pluginId)
|
||||
apply(plugin = libs.plugins.firebase.crashlytics.get().pluginId)
|
||||
versionName = "$resolvedVersionName ($resolvedVersionCode) google"
|
||||
}
|
||||
create("fdroid") {
|
||||
dimension = "default"
|
||||
dependenciesInfo { includeInApk = false }
|
||||
versionName = "$resolvedVersionName ($resolvedVersionCode) fdroid"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ import com.datadog.android.sessionreplay.SessionReplay
|
|||
import com.datadog.android.sessionreplay.SessionReplayConfiguration
|
||||
import com.datadog.android.sessionreplay.compose.ComposeExtensionSupport
|
||||
import com.datadog.android.timber.DatadogTree
|
||||
import com.datadog.android.trace.AndroidTracer
|
||||
import com.datadog.android.trace.Trace
|
||||
import com.datadog.android.trace.TraceConfiguration
|
||||
import com.datadog.android.trace.opentelemetry.DatadogOpenTelemetry
|
||||
import com.geeksville.mesh.BuildConfig
|
||||
import com.geeksville.mesh.analytics.AnalyticsProvider
|
||||
import com.geeksville.mesh.analytics.FirebaseAnalytics
|
||||
|
|
@ -59,7 +59,7 @@ import com.google.firebase.crashlytics.crashlytics
|
|||
import com.google.firebase.crashlytics.setCustomKeys
|
||||
import com.google.firebase.initialize
|
||||
import com.suddenh4x.ratingdialog.AppRating
|
||||
import io.opentracing.util.GlobalTracer
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry
|
||||
import timber.log.Timber
|
||||
|
||||
abstract class GeeksvilleApplication :
|
||||
|
|
@ -218,8 +218,7 @@ abstract class GeeksvilleApplication :
|
|||
val traceConfig = TraceConfiguration.Builder().build()
|
||||
Trace.enable(traceConfig)
|
||||
|
||||
val tracer = AndroidTracer.Builder().build()
|
||||
GlobalTracer.registerIfAbsent(tracer)
|
||||
GlobalOpenTelemetry.set(DatadogOpenTelemetry(BuildConfig.APPLICATION_ID))
|
||||
|
||||
val sessionReplayConfig =
|
||||
SessionReplayConfiguration.Builder(sampleRate = 20.0f)
|
||||
|
|
|
|||
|
|
@ -27,29 +27,23 @@ import android.net.Uri
|
|||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.compose.animation.core.animate
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.TripOrigin
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FloatingToolbarDefaults
|
||||
import androidx.compose.material3.FloatingToolbarExitDirection.Companion.End
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberFloatingToolbarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -62,7 +56,6 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -123,7 +116,7 @@ import com.google.maps.android.compose.Polyline
|
|||
import com.google.maps.android.compose.TileOverlay
|
||||
import com.google.maps.android.compose.rememberCameraPositionState
|
||||
import com.google.maps.android.compose.rememberUpdatedMarkerState
|
||||
import com.google.maps.android.compose.widgets.DisappearingScaleBar
|
||||
import com.google.maps.android.compose.widgets.ScaleBar
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
|
@ -214,7 +207,6 @@ fun MapView(
|
|||
val mapFilterState by mapViewModel.mapFilterStateFlow.collectAsStateWithLifecycle()
|
||||
val ourNodeInfo by uiViewModel.ourNodeInfo.collectAsStateWithLifecycle()
|
||||
var editingWaypoint by remember { mutableStateOf<Waypoint?>(null) }
|
||||
val savedCameraPosition by mapViewModel.cameraPosition.collectAsStateWithLifecycle()
|
||||
|
||||
val selectedGoogleMapType by mapViewModel.selectedGoogleMapType.collectAsStateWithLifecycle()
|
||||
val currentCustomTileProviderUrl by mapViewModel.selectedCustomTileProviderUrl.collectAsStateWithLifecycle()
|
||||
|
|
@ -222,13 +214,7 @@ fun MapView(
|
|||
var mapTypeMenuExpanded by remember { mutableStateOf(false) }
|
||||
var showCustomTileManagerSheet by remember { mutableStateOf(false) }
|
||||
|
||||
val defaultLatLng = LatLng(0.0, 0.0)
|
||||
val cameraPositionState = rememberCameraPositionState {
|
||||
position =
|
||||
savedCameraPosition?.let {
|
||||
CameraPosition(LatLng(it.targetLat, it.targetLng), it.zoom, it.tilt, it.bearing)
|
||||
} ?: CameraPosition.fromLatLngZoom(defaultLatLng, 7f)
|
||||
}
|
||||
val cameraPositionState = rememberCameraPositionState {}
|
||||
|
||||
// Location tracking functionality
|
||||
val fusedLocationClient = remember { LocationServices.getFusedLocationProviderClient(context) }
|
||||
|
|
@ -277,29 +263,6 @@ fun MapView(
|
|||
// Clean up location tracking on disposal
|
||||
DisposableEffect(Unit) { onDispose { fusedLocationClient.removeLocationUpdates(locationCallback) } }
|
||||
|
||||
val floatingToolbarState = rememberFloatingToolbarState()
|
||||
val exitAlwaysScrollBehavior =
|
||||
FloatingToolbarDefaults.exitAlwaysScrollBehavior(exitDirection = End, state = floatingToolbarState)
|
||||
|
||||
LaunchedEffect(cameraPositionState.isMoving, floatingToolbarState.offsetLimit) {
|
||||
val targetOffset =
|
||||
if (cameraPositionState.isMoving) {
|
||||
floatingToolbarState.offsetLimit
|
||||
} else {
|
||||
mapViewModel.onCameraPositionChanged(cameraPositionState.position)
|
||||
0f
|
||||
}
|
||||
if (floatingToolbarState.offset != targetOffset) {
|
||||
if (targetOffset == 0f || floatingToolbarState.offsetLimit != 0f) {
|
||||
launch {
|
||||
animate(initialValue = floatingToolbarState.offset, targetValue = targetOffset) { value, _ ->
|
||||
floatingToolbarState.offset = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val allNodes by
|
||||
mapViewModel.nodes
|
||||
.map { nodes -> nodes.filter { node -> node.validPosition != null } }
|
||||
|
|
@ -370,7 +333,7 @@ fun MapView(
|
|||
|
||||
var showClusterItemsDialog by remember { mutableStateOf<List<NodeClusterItem>?>(null) }
|
||||
|
||||
Scaffold(modifier = Modifier.nestedScroll(exitAlwaysScrollBehavior)) { paddingValues ->
|
||||
Scaffold { paddingValues ->
|
||||
Box(modifier = Modifier.fillMaxSize().padding(paddingValues)) {
|
||||
GoogleMap(
|
||||
mapColorScheme = mapColorScheme,
|
||||
|
|
@ -380,7 +343,7 @@ fun MapView(
|
|||
MapUiSettings(
|
||||
zoomControlsEnabled = true,
|
||||
mapToolbarEnabled = true,
|
||||
compassEnabled = true,
|
||||
compassEnabled = false,
|
||||
myLocationButtonEnabled = false, // Disabled - we use custom location button
|
||||
rotationGesturesEnabled = true,
|
||||
scrollGesturesEnabled = true,
|
||||
|
|
@ -399,32 +362,27 @@ fun MapView(
|
|||
}
|
||||
},
|
||||
onMapLoaded = {
|
||||
if (
|
||||
savedCameraPosition?.targetLat == defaultLatLng.latitude &&
|
||||
savedCameraPosition?.targetLng == defaultLatLng.longitude
|
||||
) {
|
||||
val pointsToBound: List<LatLng> =
|
||||
when {
|
||||
!nodeTrack.isNullOrEmpty() -> nodeTrack.map { it.toLatLng() }
|
||||
val pointsToBound: List<LatLng> =
|
||||
when {
|
||||
!nodeTrack.isNullOrEmpty() -> nodeTrack.map { it.toLatLng() }
|
||||
|
||||
allNodes.isNotEmpty() || displayableWaypoints.isNotEmpty() ->
|
||||
allNodes.mapNotNull { it.toLatLng() } + displayableWaypoints.map { it.toLatLng() }
|
||||
allNodes.isNotEmpty() || displayableWaypoints.isNotEmpty() ->
|
||||
allNodes.mapNotNull { it.toLatLng() } + displayableWaypoints.map { it.toLatLng() }
|
||||
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
if (pointsToBound.isNotEmpty()) {
|
||||
val bounds = LatLngBounds.builder().apply { pointsToBound.forEach(::include) }.build()
|
||||
|
||||
val padding = if (!pointsToBound.isEmpty()) 100 else 48
|
||||
|
||||
try {
|
||||
coroutineScope.launch {
|
||||
cameraPositionState.animate(CameraUpdateFactory.newLatLngBounds(bounds, padding))
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
warn("MapView Could not animate to bounds: ${e.message}")
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
if (pointsToBound.isNotEmpty()) {
|
||||
val bounds = LatLngBounds.builder().apply { pointsToBound.forEach(::include) }.build()
|
||||
|
||||
val padding = if (!pointsToBound.isEmpty()) 100 else 48
|
||||
|
||||
try {
|
||||
coroutineScope.launch {
|
||||
cameraPositionState.animate(CameraUpdateFactory.newLatLngBounds(bounds, padding))
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
warn("MapView Could not animate to bounds: ${e.message}")
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -550,6 +508,7 @@ fun MapView(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
LayerType.GEOJSON -> {
|
||||
layerItem.geoJsonLayerData?.let { geoJsonLayer ->
|
||||
if (layerItem.isVisible && !geoJsonLayer.isLayerOnMap) {
|
||||
|
|
@ -565,12 +524,10 @@ fun MapView(
|
|||
}
|
||||
}
|
||||
|
||||
val currentCameraPosition = cameraPositionState.position
|
||||
var displayedZoom by remember { mutableStateOf(currentCameraPosition.zoom) }
|
||||
|
||||
if (displayedZoom != 0f) {
|
||||
DisappearingScaleBar(cameraPositionState = cameraPositionState)
|
||||
}
|
||||
ScaleBar(
|
||||
cameraPositionState = cameraPositionState,
|
||||
modifier = Modifier.align(Alignment.BottomStart).padding(bottom = 48.dp),
|
||||
)
|
||||
editingWaypoint?.let { waypointToEdit ->
|
||||
EditWaypointDialog(
|
||||
waypoint = waypointToEdit,
|
||||
|
|
@ -599,7 +556,7 @@ fun MapView(
|
|||
}
|
||||
|
||||
MapControlsOverlay(
|
||||
modifier = Modifier.align(Alignment.TopEnd).padding(top = 50.dp),
|
||||
modifier = Modifier.align(Alignment.TopCenter).padding(top = 8.dp),
|
||||
mapFilterMenuExpanded = mapFilterMenuExpanded,
|
||||
onMapFilterMenuDismissRequest = { mapFilterMenuExpanded = false },
|
||||
onToggleMapFilterMenu = { mapFilterMenuExpanded = true },
|
||||
|
|
@ -613,7 +570,6 @@ fun MapView(
|
|||
showCustomTileManagerSheet = true
|
||||
},
|
||||
showFilterButton = focusedNodeNum == null,
|
||||
scrollBehavior = exitAlwaysScrollBehavior,
|
||||
hasLocationPermission = hasLocationPermission,
|
||||
isLocationTrackingEnabled = isLocationTrackingEnabled,
|
||||
onToggleLocationTracking = {
|
||||
|
|
@ -642,6 +598,7 @@ fun MapView(
|
|||
isLocationTrackingEnabled = !isLocationTrackingEnabled
|
||||
}
|
||||
},
|
||||
bearing = cameraPositionState.position.bearing,
|
||||
onOrientNorth = {
|
||||
coroutineScope.launch {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import com.geeksville.mesh.database.PacketRepository
|
|||
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
|
||||
import com.geeksville.mesh.repository.map.CustomTileProviderRepository
|
||||
import com.google.android.gms.maps.GoogleMap
|
||||
import com.google.android.gms.maps.model.CameraPosition
|
||||
import com.google.android.gms.maps.model.TileProvider
|
||||
import com.google.android.gms.maps.model.UrlTileProvider
|
||||
import com.google.maps.android.compose.MapType
|
||||
|
|
@ -101,10 +100,6 @@ constructor(
|
|||
private val _selectedGoogleMapType = MutableStateFlow(MapType.NORMAL)
|
||||
val selectedGoogleMapType: StateFlow<MapType> = _selectedGoogleMapType.asStateFlow()
|
||||
|
||||
private val _cameraPosition = MutableStateFlow<MapCameraPosition?>(null)
|
||||
|
||||
val cameraPosition: StateFlow<MapCameraPosition?> = _cameraPosition.asStateFlow()
|
||||
|
||||
val displayUnits =
|
||||
radioConfigRepository.deviceProfileFlow
|
||||
.mapNotNull { it.config.display.units }
|
||||
|
|
@ -114,17 +109,6 @@ constructor(
|
|||
initialValue = ConfigProtos.Config.DisplayConfig.DisplayUnits.METRIC,
|
||||
)
|
||||
|
||||
fun onCameraPositionChanged(cameraPosition: CameraPosition) {
|
||||
_cameraPosition.value =
|
||||
MapCameraPosition(
|
||||
targetLat = cameraPosition.target.latitude,
|
||||
targetLng = cameraPosition.target.longitude,
|
||||
zoom = cameraPosition.zoom,
|
||||
tilt = cameraPosition.tilt,
|
||||
bearing = cameraPosition.bearing,
|
||||
)
|
||||
}
|
||||
|
||||
fun addCustomTileProvider(name: String, urlTemplate: String) {
|
||||
viewModelScope.launch {
|
||||
if (name.isBlank() || urlTemplate.isBlank() || !isValidTileUrlTemplate(urlTemplate)) {
|
||||
|
|
|
|||
|
|
@ -19,13 +19,25 @@ package com.geeksville.mesh.ui.map.components
|
|||
|
||||
import androidx.compose.material3.FilledIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
@Composable
|
||||
fun MapButton(icon: ImageVector, contentDescription: String, modifier: Modifier = Modifier, onClick: () -> Unit) {
|
||||
fun MapButton(
|
||||
modifier: Modifier = Modifier,
|
||||
icon: ImageVector,
|
||||
iconTint: Color? = null,
|
||||
contentDescription: String,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
FilledIconButton(onClick = onClick, modifier = modifier) {
|
||||
Icon(imageVector = icon, contentDescription = contentDescription)
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = iconTint ?: IconButtonDefaults.filledIconButtonColors().contentColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,18 +20,20 @@ package com.geeksville.mesh.ui.map.components
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.LocationDisabled
|
||||
import androidx.compose.material.icons.outlined.Explore
|
||||
import androidx.compose.material.icons.outlined.Layers
|
||||
import androidx.compose.material.icons.outlined.Map
|
||||
import androidx.compose.material.icons.outlined.MyLocation
|
||||
import androidx.compose.material.icons.outlined.Navigation
|
||||
import androidx.compose.material.icons.outlined.Tune
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.FloatingToolbarScrollBehavior
|
||||
import androidx.compose.material3.VerticalFloatingToolbar
|
||||
import androidx.compose.material3.HorizontalFloatingToolbar
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
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
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
|
|
@ -48,20 +50,20 @@ fun MapControlsOverlay(
|
|||
onManageLayersClicked: () -> Unit,
|
||||
onManageCustomTileProvidersClicked: () -> Unit, // New parameter
|
||||
showFilterButton: Boolean,
|
||||
scrollBehavior: FloatingToolbarScrollBehavior,
|
||||
// Location tracking parameters
|
||||
hasLocationPermission: Boolean = false,
|
||||
isLocationTrackingEnabled: Boolean = false,
|
||||
onToggleLocationTracking: () -> Unit = {},
|
||||
bearing: Float = 0f,
|
||||
onOrientNorth: () -> Unit = {},
|
||||
) {
|
||||
VerticalFloatingToolbar(
|
||||
HorizontalFloatingToolbar(
|
||||
modifier = modifier,
|
||||
expanded = true,
|
||||
leadingContent = {},
|
||||
trailingContent = {},
|
||||
scrollBehavior = scrollBehavior,
|
||||
content = {
|
||||
CompassButton(onOrientNorth = onOrientNorth, bearing = bearing)
|
||||
if (showFilterButton) {
|
||||
Box {
|
||||
MapButton(
|
||||
|
|
@ -97,8 +99,6 @@ fun MapControlsOverlay(
|
|||
onClick = onManageLayersClicked,
|
||||
)
|
||||
|
||||
CompassButton(onOrientNorth = onOrientNorth)
|
||||
|
||||
// Location tracking button
|
||||
if (hasLocationPermission) {
|
||||
MapButton(
|
||||
|
|
@ -117,9 +117,13 @@ fun MapControlsOverlay(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun CompassButton(onOrientNorth: () -> Unit) {
|
||||
private fun CompassButton(onOrientNorth: () -> Unit, bearing: Float) {
|
||||
val icon = Icons.Outlined.Navigation
|
||||
|
||||
MapButton(
|
||||
icon = Icons.Outlined.Explore,
|
||||
modifier = Modifier.rotate(-bearing),
|
||||
icon = icon,
|
||||
iconTint = MaterialTheme.colorScheme.StatusRed.takeIf { bearing == 0f },
|
||||
contentDescription = stringResource(id = R.string.orient_north),
|
||||
onClick = onOrientNorth,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,39 +18,39 @@
|
|||
package com.geeksville.mesh.navigation
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.PowerSettingsNew
|
||||
import androidx.compose.material.icons.rounded.RestartAlt
|
||||
import androidx.compose.material.icons.rounded.Restore
|
||||
import androidx.compose.material.icons.rounded.Storage
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.NavDestination.Companion.hasRoute
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navDeepLink
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.BluetoothViewModel
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.TopLevelDestination.Companion.isTopLevel
|
||||
import com.geeksville.mesh.ui.debug.DebugScreen
|
||||
import com.geeksville.mesh.ui.map.MapViewModel
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
enum class AdminRoute(@StringRes val title: Int) {
|
||||
REBOOT(R.string.reboot),
|
||||
SHUTDOWN(R.string.shutdown),
|
||||
FACTORY_RESET(R.string.factory_reset),
|
||||
NODEDB_RESET(R.string.nodedb_reset),
|
||||
enum class AdminRoute(val icon: ImageVector, @StringRes val title: Int) {
|
||||
REBOOT(Icons.Rounded.RestartAlt, R.string.reboot),
|
||||
SHUTDOWN(Icons.Rounded.PowerSettingsNew, R.string.shutdown),
|
||||
FACTORY_RESET(Icons.Rounded.Restore, R.string.factory_reset),
|
||||
NODEDB_RESET(Icons.Rounded.Storage, R.string.nodedb_reset),
|
||||
}
|
||||
|
||||
const val DEEP_LINK_BASE_URI = "meshtastic://meshtastic"
|
||||
|
||||
@Serializable sealed interface Graph : Route
|
||||
|
||||
@Serializable
|
||||
sealed interface Route {
|
||||
@Serializable data object DebugPanel : Route
|
||||
}
|
||||
@Serializable sealed interface Route
|
||||
|
||||
fun NavDestination.isConfigRoute(): Boolean =
|
||||
ConfigRoute.entries.any { hasRoute(it.route::class) } || ModuleRoute.entries.any { hasRoute(it.route::class) }
|
||||
|
|
@ -80,11 +80,6 @@ fun NavGraph(
|
|||
mapGraph(navController, uIViewModel, mapViewModel)
|
||||
channelsGraph(navController, uIViewModel)
|
||||
connectionsGraph(navController, uIViewModel, bluetoothViewModel)
|
||||
composable<Route.DebugPanel>(
|
||||
deepLinks = listOf(navDeepLink<Route.DebugPanel>(basePath = "$DEEP_LINK_BASE_URI/debug_panel")),
|
||||
) {
|
||||
DebugScreen()
|
||||
}
|
||||
settingsGraph(navController, uIViewModel)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import com.geeksville.mesh.AdminProtos
|
|||
import com.geeksville.mesh.MeshProtos.DeviceMetadata
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.ui.debug.DebugScreen
|
||||
import com.geeksville.mesh.ui.settings.SettingsScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.CleanNodeDatabaseScreen
|
||||
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
|
||||
|
|
@ -147,6 +148,8 @@ sealed class SettingsRoutes {
|
|||
|
||||
@Serializable data object CleanNodeDb : Route
|
||||
|
||||
@Serializable data object DebugPanel : Route
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +180,12 @@ fun NavGraphBuilder.settingsGraph(navController: NavHostController, uiViewModel:
|
|||
}
|
||||
configRoutesScreens(navController)
|
||||
moduleRoutesScreens(navController)
|
||||
composable<SettingsRoutes.DebugPanel>(
|
||||
deepLinks =
|
||||
listOf(navDeepLink<SettingsRoutes.DebugPanel>(basePath = "$DEEP_LINK_BASE_URI/settings/debug_panel")),
|
||||
) {
|
||||
DebugScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ class MeshService :
|
|||
|
||||
private const val CONFIG_ONLY_NONCE = 69420
|
||||
private const val NODE_INFO_ONLY_NONCE = 69421
|
||||
private const val CONFIG_WAIT_MS = 250L
|
||||
private const val CONFIG_WAIT_MS = 50L
|
||||
}
|
||||
|
||||
private var previousSummary: String? = null
|
||||
|
|
@ -1750,9 +1750,6 @@ class MeshService :
|
|||
processQueuedPackets() // send any packets that were queued up
|
||||
startMqttClientProxy()
|
||||
serviceBroadcasts.broadcastConnection()
|
||||
packetHandler.sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket { setTimeOnly = currentSecond() }) {
|
||||
connectionState
|
||||
}
|
||||
sendAnalytics()
|
||||
reportConnection()
|
||||
}
|
||||
|
|
@ -1825,6 +1822,9 @@ class MeshService :
|
|||
sendAnalytics()
|
||||
onNodeDBChanged()
|
||||
}
|
||||
packetHandler.sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket { setTimeOnly = currentSecond() }) {
|
||||
connectionState
|
||||
}
|
||||
}
|
||||
|
||||
/** Start the modern (REV2) API configuration flow */
|
||||
|
|
|
|||
|
|
@ -351,7 +351,6 @@ fun MainScreen(
|
|||
onAction = { action ->
|
||||
if (action is MainMenuAction) {
|
||||
when (action) {
|
||||
MainMenuAction.DEBUG -> navController.navigate(Route.DebugPanel)
|
||||
MainMenuAction.QUICK_CHAT -> navController.navigate(ContactsRoutes.QuickChat)
|
||||
MainMenuAction.SHOW_INTRO -> uIViewModel.onMainMenuAction(action)
|
||||
else -> onAction(action)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ import com.geeksville.mesh.model.Node
|
|||
import com.geeksville.mesh.model.UIViewModel
|
||||
import com.geeksville.mesh.navigation.ContactsRoutes
|
||||
import com.geeksville.mesh.navigation.NodesRoutes
|
||||
import com.geeksville.mesh.navigation.Route
|
||||
import com.geeksville.mesh.navigation.SettingsRoutes
|
||||
import com.geeksville.mesh.navigation.showLongNameTitle
|
||||
import com.geeksville.mesh.ui.TopLevelDestination.Companion.isTopLevel
|
||||
|
|
@ -88,7 +87,7 @@ fun MainAppBar(
|
|||
when {
|
||||
currentDestination == null || currentDestination.isTopLevel() -> stringResource(id = R.string.app_name)
|
||||
|
||||
currentDestination.hasRoute<Route.DebugPanel>() -> stringResource(id = R.string.debug_panel)
|
||||
currentDestination.hasRoute<SettingsRoutes.DebugPanel>() -> stringResource(id = R.string.debug_panel)
|
||||
|
||||
currentDestination.hasRoute<ContactsRoutes.QuickChat>() -> stringResource(id = R.string.quick_chat)
|
||||
|
||||
|
|
@ -120,7 +119,7 @@ fun MainAppBar(
|
|||
when {
|
||||
it.isTopLevel() -> MainMenuActions(onAction)
|
||||
|
||||
currentDestination.hasRoute<Route.DebugPanel>() -> DebugMenuActions()
|
||||
currentDestination.hasRoute<SettingsRoutes.DebugPanel>() -> DebugMenuActions()
|
||||
|
||||
currentDestination.hasRoute<SettingsRoutes.Settings>() ->
|
||||
RadioConfigMenuActions(viewModel = viewModel)
|
||||
|
|
@ -206,7 +205,6 @@ private fun TopBarActions(
|
|||
}
|
||||
|
||||
enum class MainMenuAction(@StringRes val stringRes: Int) {
|
||||
DEBUG(R.string.debug_panel),
|
||||
EXPORT_RANGETEST(R.string.save_rangetest),
|
||||
SHOW_INTRO(R.string.intro_show),
|
||||
QUICK_CHAT(R.string.quick_chat),
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import android.os.Build
|
|||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.MaterialExpressiveTheme
|
||||
import androidx.compose.material3.MotionScheme.Companion.expressive
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
|
|
@ -32,267 +33,270 @@ import androidx.compose.runtime.Immutable
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val lightScheme = lightColorScheme(
|
||||
primary = primaryLight,
|
||||
onPrimary = onPrimaryLight,
|
||||
primaryContainer = primaryContainerLight,
|
||||
onPrimaryContainer = onPrimaryContainerLight,
|
||||
secondary = secondaryLight,
|
||||
onSecondary = onSecondaryLight,
|
||||
secondaryContainer = secondaryContainerLight,
|
||||
onSecondaryContainer = onSecondaryContainerLight,
|
||||
tertiary = tertiaryLight,
|
||||
onTertiary = onTertiaryLight,
|
||||
tertiaryContainer = tertiaryContainerLight,
|
||||
onTertiaryContainer = onTertiaryContainerLight,
|
||||
error = errorLight,
|
||||
onError = onErrorLight,
|
||||
errorContainer = errorContainerLight,
|
||||
onErrorContainer = onErrorContainerLight,
|
||||
background = backgroundLight,
|
||||
onBackground = onBackgroundLight,
|
||||
surface = surfaceLight,
|
||||
onSurface = onSurfaceLight,
|
||||
surfaceVariant = surfaceVariantLight,
|
||||
onSurfaceVariant = onSurfaceVariantLight,
|
||||
outline = outlineLight,
|
||||
outlineVariant = outlineVariantLight,
|
||||
scrim = scrimLight,
|
||||
inverseSurface = inverseSurfaceLight,
|
||||
inverseOnSurface = inverseOnSurfaceLight,
|
||||
inversePrimary = inversePrimaryLight,
|
||||
surfaceDim = surfaceDimLight,
|
||||
surfaceBright = surfaceBrightLight,
|
||||
surfaceContainerLowest = surfaceContainerLowestLight,
|
||||
surfaceContainerLow = surfaceContainerLowLight,
|
||||
surfaceContainer = surfaceContainerLight,
|
||||
surfaceContainerHigh = surfaceContainerHighLight,
|
||||
surfaceContainerHighest = surfaceContainerHighestLight,
|
||||
)
|
||||
private val lightScheme =
|
||||
lightColorScheme(
|
||||
primary = primaryLight,
|
||||
onPrimary = onPrimaryLight,
|
||||
primaryContainer = primaryContainerLight,
|
||||
onPrimaryContainer = onPrimaryContainerLight,
|
||||
secondary = secondaryLight,
|
||||
onSecondary = onSecondaryLight,
|
||||
secondaryContainer = secondaryContainerLight,
|
||||
onSecondaryContainer = onSecondaryContainerLight,
|
||||
tertiary = tertiaryLight,
|
||||
onTertiary = onTertiaryLight,
|
||||
tertiaryContainer = tertiaryContainerLight,
|
||||
onTertiaryContainer = onTertiaryContainerLight,
|
||||
error = errorLight,
|
||||
onError = onErrorLight,
|
||||
errorContainer = errorContainerLight,
|
||||
onErrorContainer = onErrorContainerLight,
|
||||
background = backgroundLight,
|
||||
onBackground = onBackgroundLight,
|
||||
surface = surfaceLight,
|
||||
onSurface = onSurfaceLight,
|
||||
surfaceVariant = surfaceVariantLight,
|
||||
onSurfaceVariant = onSurfaceVariantLight,
|
||||
outline = outlineLight,
|
||||
outlineVariant = outlineVariantLight,
|
||||
scrim = scrimLight,
|
||||
inverseSurface = inverseSurfaceLight,
|
||||
inverseOnSurface = inverseOnSurfaceLight,
|
||||
inversePrimary = inversePrimaryLight,
|
||||
surfaceDim = surfaceDimLight,
|
||||
surfaceBright = surfaceBrightLight,
|
||||
surfaceContainerLowest = surfaceContainerLowestLight,
|
||||
surfaceContainerLow = surfaceContainerLowLight,
|
||||
surfaceContainer = surfaceContainerLight,
|
||||
surfaceContainerHigh = surfaceContainerHighLight,
|
||||
surfaceContainerHighest = surfaceContainerHighestLight,
|
||||
)
|
||||
|
||||
private val darkScheme = darkColorScheme(
|
||||
primary = primaryDark,
|
||||
onPrimary = onPrimaryDark,
|
||||
primaryContainer = primaryContainerDark,
|
||||
onPrimaryContainer = onPrimaryContainerDark,
|
||||
secondary = secondaryDark,
|
||||
onSecondary = onSecondaryDark,
|
||||
secondaryContainer = secondaryContainerDark,
|
||||
onSecondaryContainer = onSecondaryContainerDark,
|
||||
tertiary = tertiaryDark,
|
||||
onTertiary = onTertiaryDark,
|
||||
tertiaryContainer = tertiaryContainerDark,
|
||||
onTertiaryContainer = onTertiaryContainerDark,
|
||||
error = errorDark,
|
||||
onError = onErrorDark,
|
||||
errorContainer = errorContainerDark,
|
||||
onErrorContainer = onErrorContainerDark,
|
||||
background = backgroundDark,
|
||||
onBackground = onBackgroundDark,
|
||||
surface = surfaceDark,
|
||||
onSurface = onSurfaceDark,
|
||||
surfaceVariant = surfaceVariantDark,
|
||||
onSurfaceVariant = onSurfaceVariantDark,
|
||||
outline = outlineDark,
|
||||
outlineVariant = outlineVariantDark,
|
||||
scrim = scrimDark,
|
||||
inverseSurface = inverseSurfaceDark,
|
||||
inverseOnSurface = inverseOnSurfaceDark,
|
||||
inversePrimary = inversePrimaryDark,
|
||||
surfaceDim = surfaceDimDark,
|
||||
surfaceBright = surfaceBrightDark,
|
||||
surfaceContainerLowest = surfaceContainerLowestDark,
|
||||
surfaceContainerLow = surfaceContainerLowDark,
|
||||
surfaceContainer = surfaceContainerDark,
|
||||
surfaceContainerHigh = surfaceContainerHighDark,
|
||||
surfaceContainerHighest = surfaceContainerHighestDark,
|
||||
)
|
||||
private val darkScheme =
|
||||
darkColorScheme(
|
||||
primary = primaryDark,
|
||||
onPrimary = onPrimaryDark,
|
||||
primaryContainer = primaryContainerDark,
|
||||
onPrimaryContainer = onPrimaryContainerDark,
|
||||
secondary = secondaryDark,
|
||||
onSecondary = onSecondaryDark,
|
||||
secondaryContainer = secondaryContainerDark,
|
||||
onSecondaryContainer = onSecondaryContainerDark,
|
||||
tertiary = tertiaryDark,
|
||||
onTertiary = onTertiaryDark,
|
||||
tertiaryContainer = tertiaryContainerDark,
|
||||
onTertiaryContainer = onTertiaryContainerDark,
|
||||
error = errorDark,
|
||||
onError = onErrorDark,
|
||||
errorContainer = errorContainerDark,
|
||||
onErrorContainer = onErrorContainerDark,
|
||||
background = backgroundDark,
|
||||
onBackground = onBackgroundDark,
|
||||
surface = surfaceDark,
|
||||
onSurface = onSurfaceDark,
|
||||
surfaceVariant = surfaceVariantDark,
|
||||
onSurfaceVariant = onSurfaceVariantDark,
|
||||
outline = outlineDark,
|
||||
outlineVariant = outlineVariantDark,
|
||||
scrim = scrimDark,
|
||||
inverseSurface = inverseSurfaceDark,
|
||||
inverseOnSurface = inverseOnSurfaceDark,
|
||||
inversePrimary = inversePrimaryDark,
|
||||
surfaceDim = surfaceDimDark,
|
||||
surfaceBright = surfaceBrightDark,
|
||||
surfaceContainerLowest = surfaceContainerLowestDark,
|
||||
surfaceContainerLow = surfaceContainerLowDark,
|
||||
surfaceContainer = surfaceContainerDark,
|
||||
surfaceContainerHigh = surfaceContainerHighDark,
|
||||
surfaceContainerHighest = surfaceContainerHighestDark,
|
||||
)
|
||||
|
||||
private val mediumContrastLightColorScheme = lightColorScheme(
|
||||
primary = primaryLightMediumContrast,
|
||||
onPrimary = onPrimaryLightMediumContrast,
|
||||
primaryContainer = primaryContainerLightMediumContrast,
|
||||
onPrimaryContainer = onPrimaryContainerLightMediumContrast,
|
||||
secondary = secondaryLightMediumContrast,
|
||||
onSecondary = onSecondaryLightMediumContrast,
|
||||
secondaryContainer = secondaryContainerLightMediumContrast,
|
||||
onSecondaryContainer = onSecondaryContainerLightMediumContrast,
|
||||
tertiary = tertiaryLightMediumContrast,
|
||||
onTertiary = onTertiaryLightMediumContrast,
|
||||
tertiaryContainer = tertiaryContainerLightMediumContrast,
|
||||
onTertiaryContainer = onTertiaryContainerLightMediumContrast,
|
||||
error = errorLightMediumContrast,
|
||||
onError = onErrorLightMediumContrast,
|
||||
errorContainer = errorContainerLightMediumContrast,
|
||||
onErrorContainer = onErrorContainerLightMediumContrast,
|
||||
background = backgroundLightMediumContrast,
|
||||
onBackground = onBackgroundLightMediumContrast,
|
||||
surface = surfaceLightMediumContrast,
|
||||
onSurface = onSurfaceLightMediumContrast,
|
||||
surfaceVariant = surfaceVariantLightMediumContrast,
|
||||
onSurfaceVariant = onSurfaceVariantLightMediumContrast,
|
||||
outline = outlineLightMediumContrast,
|
||||
outlineVariant = outlineVariantLightMediumContrast,
|
||||
scrim = scrimLightMediumContrast,
|
||||
inverseSurface = inverseSurfaceLightMediumContrast,
|
||||
inverseOnSurface = inverseOnSurfaceLightMediumContrast,
|
||||
inversePrimary = inversePrimaryLightMediumContrast,
|
||||
surfaceDim = surfaceDimLightMediumContrast,
|
||||
surfaceBright = surfaceBrightLightMediumContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestLightMediumContrast,
|
||||
surfaceContainerLow = surfaceContainerLowLightMediumContrast,
|
||||
surfaceContainer = surfaceContainerLightMediumContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighLightMediumContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestLightMediumContrast,
|
||||
)
|
||||
private val mediumContrastLightColorScheme =
|
||||
lightColorScheme(
|
||||
primary = primaryLightMediumContrast,
|
||||
onPrimary = onPrimaryLightMediumContrast,
|
||||
primaryContainer = primaryContainerLightMediumContrast,
|
||||
onPrimaryContainer = onPrimaryContainerLightMediumContrast,
|
||||
secondary = secondaryLightMediumContrast,
|
||||
onSecondary = onSecondaryLightMediumContrast,
|
||||
secondaryContainer = secondaryContainerLightMediumContrast,
|
||||
onSecondaryContainer = onSecondaryContainerLightMediumContrast,
|
||||
tertiary = tertiaryLightMediumContrast,
|
||||
onTertiary = onTertiaryLightMediumContrast,
|
||||
tertiaryContainer = tertiaryContainerLightMediumContrast,
|
||||
onTertiaryContainer = onTertiaryContainerLightMediumContrast,
|
||||
error = errorLightMediumContrast,
|
||||
onError = onErrorLightMediumContrast,
|
||||
errorContainer = errorContainerLightMediumContrast,
|
||||
onErrorContainer = onErrorContainerLightMediumContrast,
|
||||
background = backgroundLightMediumContrast,
|
||||
onBackground = onBackgroundLightMediumContrast,
|
||||
surface = surfaceLightMediumContrast,
|
||||
onSurface = onSurfaceLightMediumContrast,
|
||||
surfaceVariant = surfaceVariantLightMediumContrast,
|
||||
onSurfaceVariant = onSurfaceVariantLightMediumContrast,
|
||||
outline = outlineLightMediumContrast,
|
||||
outlineVariant = outlineVariantLightMediumContrast,
|
||||
scrim = scrimLightMediumContrast,
|
||||
inverseSurface = inverseSurfaceLightMediumContrast,
|
||||
inverseOnSurface = inverseOnSurfaceLightMediumContrast,
|
||||
inversePrimary = inversePrimaryLightMediumContrast,
|
||||
surfaceDim = surfaceDimLightMediumContrast,
|
||||
surfaceBright = surfaceBrightLightMediumContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestLightMediumContrast,
|
||||
surfaceContainerLow = surfaceContainerLowLightMediumContrast,
|
||||
surfaceContainer = surfaceContainerLightMediumContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighLightMediumContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestLightMediumContrast,
|
||||
)
|
||||
|
||||
private val highContrastLightColorScheme = lightColorScheme(
|
||||
primary = primaryLightHighContrast,
|
||||
onPrimary = onPrimaryLightHighContrast,
|
||||
primaryContainer = primaryContainerLightHighContrast,
|
||||
onPrimaryContainer = onPrimaryContainerLightHighContrast,
|
||||
secondary = secondaryLightHighContrast,
|
||||
onSecondary = onSecondaryLightHighContrast,
|
||||
secondaryContainer = secondaryContainerLightHighContrast,
|
||||
onSecondaryContainer = onSecondaryContainerLightHighContrast,
|
||||
tertiary = tertiaryLightHighContrast,
|
||||
onTertiary = onTertiaryLightHighContrast,
|
||||
tertiaryContainer = tertiaryContainerLightHighContrast,
|
||||
onTertiaryContainer = onTertiaryContainerLightHighContrast,
|
||||
error = errorLightHighContrast,
|
||||
onError = onErrorLightHighContrast,
|
||||
errorContainer = errorContainerLightHighContrast,
|
||||
onErrorContainer = onErrorContainerLightHighContrast,
|
||||
background = backgroundLightHighContrast,
|
||||
onBackground = onBackgroundLightHighContrast,
|
||||
surface = surfaceLightHighContrast,
|
||||
onSurface = onSurfaceLightHighContrast,
|
||||
surfaceVariant = surfaceVariantLightHighContrast,
|
||||
onSurfaceVariant = onSurfaceVariantLightHighContrast,
|
||||
outline = outlineLightHighContrast,
|
||||
outlineVariant = outlineVariantLightHighContrast,
|
||||
scrim = scrimLightHighContrast,
|
||||
inverseSurface = inverseSurfaceLightHighContrast,
|
||||
inverseOnSurface = inverseOnSurfaceLightHighContrast,
|
||||
inversePrimary = inversePrimaryLightHighContrast,
|
||||
surfaceDim = surfaceDimLightHighContrast,
|
||||
surfaceBright = surfaceBrightLightHighContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestLightHighContrast,
|
||||
surfaceContainerLow = surfaceContainerLowLightHighContrast,
|
||||
surfaceContainer = surfaceContainerLightHighContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighLightHighContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestLightHighContrast,
|
||||
)
|
||||
private val highContrastLightColorScheme =
|
||||
lightColorScheme(
|
||||
primary = primaryLightHighContrast,
|
||||
onPrimary = onPrimaryLightHighContrast,
|
||||
primaryContainer = primaryContainerLightHighContrast,
|
||||
onPrimaryContainer = onPrimaryContainerLightHighContrast,
|
||||
secondary = secondaryLightHighContrast,
|
||||
onSecondary = onSecondaryLightHighContrast,
|
||||
secondaryContainer = secondaryContainerLightHighContrast,
|
||||
onSecondaryContainer = onSecondaryContainerLightHighContrast,
|
||||
tertiary = tertiaryLightHighContrast,
|
||||
onTertiary = onTertiaryLightHighContrast,
|
||||
tertiaryContainer = tertiaryContainerLightHighContrast,
|
||||
onTertiaryContainer = onTertiaryContainerLightHighContrast,
|
||||
error = errorLightHighContrast,
|
||||
onError = onErrorLightHighContrast,
|
||||
errorContainer = errorContainerLightHighContrast,
|
||||
onErrorContainer = onErrorContainerLightHighContrast,
|
||||
background = backgroundLightHighContrast,
|
||||
onBackground = onBackgroundLightHighContrast,
|
||||
surface = surfaceLightHighContrast,
|
||||
onSurface = onSurfaceLightHighContrast,
|
||||
surfaceVariant = surfaceVariantLightHighContrast,
|
||||
onSurfaceVariant = onSurfaceVariantLightHighContrast,
|
||||
outline = outlineLightHighContrast,
|
||||
outlineVariant = outlineVariantLightHighContrast,
|
||||
scrim = scrimLightHighContrast,
|
||||
inverseSurface = inverseSurfaceLightHighContrast,
|
||||
inverseOnSurface = inverseOnSurfaceLightHighContrast,
|
||||
inversePrimary = inversePrimaryLightHighContrast,
|
||||
surfaceDim = surfaceDimLightHighContrast,
|
||||
surfaceBright = surfaceBrightLightHighContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestLightHighContrast,
|
||||
surfaceContainerLow = surfaceContainerLowLightHighContrast,
|
||||
surfaceContainer = surfaceContainerLightHighContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighLightHighContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestLightHighContrast,
|
||||
)
|
||||
|
||||
private val mediumContrastDarkColorScheme = darkColorScheme(
|
||||
primary = primaryDarkMediumContrast,
|
||||
onPrimary = onPrimaryDarkMediumContrast,
|
||||
primaryContainer = primaryContainerDarkMediumContrast,
|
||||
onPrimaryContainer = onPrimaryContainerDarkMediumContrast,
|
||||
secondary = secondaryDarkMediumContrast,
|
||||
onSecondary = onSecondaryDarkMediumContrast,
|
||||
secondaryContainer = secondaryContainerDarkMediumContrast,
|
||||
onSecondaryContainer = onSecondaryContainerDarkMediumContrast,
|
||||
tertiary = tertiaryDarkMediumContrast,
|
||||
onTertiary = onTertiaryDarkMediumContrast,
|
||||
tertiaryContainer = tertiaryContainerDarkMediumContrast,
|
||||
onTertiaryContainer = onTertiaryContainerDarkMediumContrast,
|
||||
error = errorDarkMediumContrast,
|
||||
onError = onErrorDarkMediumContrast,
|
||||
errorContainer = errorContainerDarkMediumContrast,
|
||||
onErrorContainer = onErrorContainerDarkMediumContrast,
|
||||
background = backgroundDarkMediumContrast,
|
||||
onBackground = onBackgroundDarkMediumContrast,
|
||||
surface = surfaceDarkMediumContrast,
|
||||
onSurface = onSurfaceDarkMediumContrast,
|
||||
surfaceVariant = surfaceVariantDarkMediumContrast,
|
||||
onSurfaceVariant = onSurfaceVariantDarkMediumContrast,
|
||||
outline = outlineDarkMediumContrast,
|
||||
outlineVariant = outlineVariantDarkMediumContrast,
|
||||
scrim = scrimDarkMediumContrast,
|
||||
inverseSurface = inverseSurfaceDarkMediumContrast,
|
||||
inverseOnSurface = inverseOnSurfaceDarkMediumContrast,
|
||||
inversePrimary = inversePrimaryDarkMediumContrast,
|
||||
surfaceDim = surfaceDimDarkMediumContrast,
|
||||
surfaceBright = surfaceBrightDarkMediumContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestDarkMediumContrast,
|
||||
surfaceContainerLow = surfaceContainerLowDarkMediumContrast,
|
||||
surfaceContainer = surfaceContainerDarkMediumContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighDarkMediumContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestDarkMediumContrast,
|
||||
)
|
||||
private val mediumContrastDarkColorScheme =
|
||||
darkColorScheme(
|
||||
primary = primaryDarkMediumContrast,
|
||||
onPrimary = onPrimaryDarkMediumContrast,
|
||||
primaryContainer = primaryContainerDarkMediumContrast,
|
||||
onPrimaryContainer = onPrimaryContainerDarkMediumContrast,
|
||||
secondary = secondaryDarkMediumContrast,
|
||||
onSecondary = onSecondaryDarkMediumContrast,
|
||||
secondaryContainer = secondaryContainerDarkMediumContrast,
|
||||
onSecondaryContainer = onSecondaryContainerDarkMediumContrast,
|
||||
tertiary = tertiaryDarkMediumContrast,
|
||||
onTertiary = onTertiaryDarkMediumContrast,
|
||||
tertiaryContainer = tertiaryContainerDarkMediumContrast,
|
||||
onTertiaryContainer = onTertiaryContainerDarkMediumContrast,
|
||||
error = errorDarkMediumContrast,
|
||||
onError = onErrorDarkMediumContrast,
|
||||
errorContainer = errorContainerDarkMediumContrast,
|
||||
onErrorContainer = onErrorContainerDarkMediumContrast,
|
||||
background = backgroundDarkMediumContrast,
|
||||
onBackground = onBackgroundDarkMediumContrast,
|
||||
surface = surfaceDarkMediumContrast,
|
||||
onSurface = onSurfaceDarkMediumContrast,
|
||||
surfaceVariant = surfaceVariantDarkMediumContrast,
|
||||
onSurfaceVariant = onSurfaceVariantDarkMediumContrast,
|
||||
outline = outlineDarkMediumContrast,
|
||||
outlineVariant = outlineVariantDarkMediumContrast,
|
||||
scrim = scrimDarkMediumContrast,
|
||||
inverseSurface = inverseSurfaceDarkMediumContrast,
|
||||
inverseOnSurface = inverseOnSurfaceDarkMediumContrast,
|
||||
inversePrimary = inversePrimaryDarkMediumContrast,
|
||||
surfaceDim = surfaceDimDarkMediumContrast,
|
||||
surfaceBright = surfaceBrightDarkMediumContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestDarkMediumContrast,
|
||||
surfaceContainerLow = surfaceContainerLowDarkMediumContrast,
|
||||
surfaceContainer = surfaceContainerDarkMediumContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighDarkMediumContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestDarkMediumContrast,
|
||||
)
|
||||
|
||||
private val highContrastDarkColorScheme = darkColorScheme(
|
||||
primary = primaryDarkHighContrast,
|
||||
onPrimary = onPrimaryDarkHighContrast,
|
||||
primaryContainer = primaryContainerDarkHighContrast,
|
||||
onPrimaryContainer = onPrimaryContainerDarkHighContrast,
|
||||
secondary = secondaryDarkHighContrast,
|
||||
onSecondary = onSecondaryDarkHighContrast,
|
||||
secondaryContainer = secondaryContainerDarkHighContrast,
|
||||
onSecondaryContainer = onSecondaryContainerDarkHighContrast,
|
||||
tertiary = tertiaryDarkHighContrast,
|
||||
onTertiary = onTertiaryDarkHighContrast,
|
||||
tertiaryContainer = tertiaryContainerDarkHighContrast,
|
||||
onTertiaryContainer = onTertiaryContainerDarkHighContrast,
|
||||
error = errorDarkHighContrast,
|
||||
onError = onErrorDarkHighContrast,
|
||||
errorContainer = errorContainerDarkHighContrast,
|
||||
onErrorContainer = onErrorContainerDarkHighContrast,
|
||||
background = backgroundDarkHighContrast,
|
||||
onBackground = onBackgroundDarkHighContrast,
|
||||
surface = surfaceDarkHighContrast,
|
||||
onSurface = onSurfaceDarkHighContrast,
|
||||
surfaceVariant = surfaceVariantDarkHighContrast,
|
||||
onSurfaceVariant = onSurfaceVariantDarkHighContrast,
|
||||
outline = outlineDarkHighContrast,
|
||||
outlineVariant = outlineVariantDarkHighContrast,
|
||||
scrim = scrimDarkHighContrast,
|
||||
inverseSurface = inverseSurfaceDarkHighContrast,
|
||||
inverseOnSurface = inverseOnSurfaceDarkHighContrast,
|
||||
inversePrimary = inversePrimaryDarkHighContrast,
|
||||
surfaceDim = surfaceDimDarkHighContrast,
|
||||
surfaceBright = surfaceBrightDarkHighContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestDarkHighContrast,
|
||||
surfaceContainerLow = surfaceContainerLowDarkHighContrast,
|
||||
surfaceContainer = surfaceContainerDarkHighContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighDarkHighContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestDarkHighContrast,
|
||||
)
|
||||
private val highContrastDarkColorScheme =
|
||||
darkColorScheme(
|
||||
primary = primaryDarkHighContrast,
|
||||
onPrimary = onPrimaryDarkHighContrast,
|
||||
primaryContainer = primaryContainerDarkHighContrast,
|
||||
onPrimaryContainer = onPrimaryContainerDarkHighContrast,
|
||||
secondary = secondaryDarkHighContrast,
|
||||
onSecondary = onSecondaryDarkHighContrast,
|
||||
secondaryContainer = secondaryContainerDarkHighContrast,
|
||||
onSecondaryContainer = onSecondaryContainerDarkHighContrast,
|
||||
tertiary = tertiaryDarkHighContrast,
|
||||
onTertiary = onTertiaryDarkHighContrast,
|
||||
tertiaryContainer = tertiaryContainerDarkHighContrast,
|
||||
onTertiaryContainer = onTertiaryContainerDarkHighContrast,
|
||||
error = errorDarkHighContrast,
|
||||
onError = onErrorDarkHighContrast,
|
||||
errorContainer = errorContainerDarkHighContrast,
|
||||
onErrorContainer = onErrorContainerDarkHighContrast,
|
||||
background = backgroundDarkHighContrast,
|
||||
onBackground = onBackgroundDarkHighContrast,
|
||||
surface = surfaceDarkHighContrast,
|
||||
onSurface = onSurfaceDarkHighContrast,
|
||||
surfaceVariant = surfaceVariantDarkHighContrast,
|
||||
onSurfaceVariant = onSurfaceVariantDarkHighContrast,
|
||||
outline = outlineDarkHighContrast,
|
||||
outlineVariant = outlineVariantDarkHighContrast,
|
||||
scrim = scrimDarkHighContrast,
|
||||
inverseSurface = inverseSurfaceDarkHighContrast,
|
||||
inverseOnSurface = inverseOnSurfaceDarkHighContrast,
|
||||
inversePrimary = inversePrimaryDarkHighContrast,
|
||||
surfaceDim = surfaceDimDarkHighContrast,
|
||||
surfaceBright = surfaceBrightDarkHighContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestDarkHighContrast,
|
||||
surfaceContainerLow = surfaceContainerLowDarkHighContrast,
|
||||
surfaceContainer = surfaceContainerDarkHighContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighDarkHighContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestDarkHighContrast,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
data class ColorFamily(
|
||||
val color: Color,
|
||||
val onColor: Color,
|
||||
val colorContainer: Color,
|
||||
val onColorContainer: Color
|
||||
)
|
||||
data class ColorFamily(val color: Color, val onColor: Color, val colorContainer: Color, val onColorContainer: Color)
|
||||
|
||||
val unspecified_scheme = ColorFamily(
|
||||
Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified
|
||||
)
|
||||
val unspecified_scheme = ColorFamily(Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified)
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun AppTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable() () -> Unit
|
||||
content:
|
||||
@Composable()
|
||||
() -> Unit,
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
val colorScheme =
|
||||
when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
darkTheme -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
|
||||
MaterialExpressiveTheme(
|
||||
colorScheme = colorScheme,
|
||||
motionScheme = expressive(),
|
||||
typography = AppTypography,
|
||||
content = content
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ package com.geeksville.mesh.ui.node
|
|||
import android.content.Intent
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -93,11 +91,9 @@ import androidx.compose.material3.LocalContentColor
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -150,6 +146,7 @@ import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
|||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusYellow
|
||||
import com.geeksville.mesh.ui.node.components.NodeActionDialogs
|
||||
import com.geeksville.mesh.ui.node.components.NodeMenuAction
|
||||
import com.geeksville.mesh.ui.node.components.TracerouteButton
|
||||
import com.geeksville.mesh.ui.settings.components.SettingsItem
|
||||
import com.geeksville.mesh.ui.settings.components.SettingsItemDetail
|
||||
import com.geeksville.mesh.ui.settings.components.SettingsItemSwitch
|
||||
|
|
@ -631,8 +628,7 @@ private fun RemoteDeviceActions(node: Node, lastTracerouteTime: Long?, onAction:
|
|||
trailingIcon = null,
|
||||
onClick = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.RequestUserInfo(node))) },
|
||||
)
|
||||
TracerouteActionButton(
|
||||
title = stringResource(id = R.string.traceroute),
|
||||
TracerouteButton(
|
||||
lastTracerouteTime = lastTracerouteTime,
|
||||
onClick = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.TraceRoute(node))) },
|
||||
)
|
||||
|
|
@ -1035,59 +1031,6 @@ private fun PowerMetrics(node: Node) {
|
|||
}
|
||||
}
|
||||
|
||||
private const val COOL_DOWN_TIME_MS = 30000L
|
||||
|
||||
@Composable
|
||||
fun TracerouteActionButton(title: String, lastTracerouteTime: Long?, onClick: () -> Unit) {
|
||||
val progress = remember { Animatable(0f) }
|
||||
var isCoolingDown by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(lastTracerouteTime) {
|
||||
val timeSinceLast = System.currentTimeMillis() - (lastTracerouteTime ?: 0)
|
||||
isCoolingDown = timeSinceLast < COOL_DOWN_TIME_MS
|
||||
|
||||
if (isCoolingDown) {
|
||||
val remainingTime = COOL_DOWN_TIME_MS - timeSinceLast
|
||||
progress.snapTo(remainingTime / COOL_DOWN_TIME_MS.toFloat())
|
||||
progress.animateTo(
|
||||
targetValue = 0f,
|
||||
animationSpec = tween(durationMillis = remainingTime.toInt(), easing = { it }),
|
||||
)
|
||||
isCoolingDown = false
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
if (!isCoolingDown) {
|
||||
onClick()
|
||||
}
|
||||
},
|
||||
enabled = !isCoolingDown,
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp).height(48.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (isCoolingDown) {
|
||||
CircularProgressIndicator(
|
||||
progress = { progress.value },
|
||||
modifier = Modifier.size(24.dp),
|
||||
strokeWidth = 2.dp,
|
||||
trackColor = ProgressIndicatorDefaults.circularDeterminateTrackColor,
|
||||
strokeCap = ProgressIndicatorDefaults.CircularDeterminateStrokeCap,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Route,
|
||||
contentDescription = stringResource(R.string.traceroute),
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = title, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NodeActionButton(
|
||||
modifier: Modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp).height(48.dp),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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.components
|
||||
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Route
|
||||
import androidx.compose.material3.CircularWavyProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
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
|
||||
|
||||
private const val COOL_DOWN_TIME_MS = 30000L
|
||||
|
||||
@Composable
|
||||
fun TracerouteButton(
|
||||
text: String = stringResource(id = R.string.traceroute),
|
||||
lastTracerouteTime: Long?,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val progress = remember { Animatable(0f) }
|
||||
|
||||
LaunchedEffect(lastTracerouteTime) {
|
||||
val timeSinceLast = System.currentTimeMillis() - (lastTracerouteTime ?: 0)
|
||||
if (timeSinceLast < COOL_DOWN_TIME_MS) {
|
||||
val remainingTime = COOL_DOWN_TIME_MS - timeSinceLast
|
||||
progress.snapTo(remainingTime / COOL_DOWN_TIME_MS.toFloat())
|
||||
progress.animateTo(
|
||||
targetValue = 0f,
|
||||
animationSpec = tween(durationMillis = remainingTime.toInt(), easing = { it }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
TracerouteButton(text = text, progress = progress.value, onClick = onClick)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
private fun TracerouteButton(text: String, progress: Float, onClick: () -> Unit) {
|
||||
val isCoolingDown = progress > 0f
|
||||
|
||||
val stroke = Stroke(width = with(LocalDensity.current) { 2.dp.toPx() }, cap = StrokeCap.Round)
|
||||
|
||||
SettingsItem(
|
||||
text = text,
|
||||
enabled = !isCoolingDown,
|
||||
leadingIcon = Icons.Default.Route,
|
||||
trailingContent = {
|
||||
if (isCoolingDown) {
|
||||
CircularWavyProgressIndicator(
|
||||
progress = { progress },
|
||||
modifier = Modifier.size(24.dp),
|
||||
stroke = stroke,
|
||||
trackStroke = stroke,
|
||||
wavelength = 8.dp,
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
if (!isCoolingDown) {
|
||||
onClick()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun TracerouteButtonPreview() {
|
||||
AppTheme { TracerouteButton(text = "Traceroute", progress = .6f, onClick = {}) }
|
||||
}
|
||||
|
|
@ -26,7 +26,6 @@ import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight
|
|||
import androidx.compose.material.icons.rounded.Android
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
|
|
@ -45,28 +44,52 @@ import com.geeksville.mesh.ui.common.theme.AppTheme
|
|||
@Composable
|
||||
fun SettingsItem(
|
||||
text: String,
|
||||
textColor: Color = LocalContentColor.current,
|
||||
enabled: Boolean = true,
|
||||
leadingIcon: ImageVector? = null,
|
||||
leadingIconTint: Color = LocalContentColor.current,
|
||||
trailingIcon: ImageVector? = Icons.AutoMirrored.Rounded.KeyboardArrowRight,
|
||||
trailingIconTint: Color = LocalContentColor.current,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
SettingsItem(
|
||||
text = text,
|
||||
textColor = textColor,
|
||||
enabled = enabled,
|
||||
leadingIcon = leadingIcon,
|
||||
leadingIconTint = leadingIconTint,
|
||||
trailingContent = { trailingIcon.Icon(trailingIconTint) },
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
/** A clickable settings button item. */
|
||||
@Composable
|
||||
fun SettingsItem(
|
||||
text: String,
|
||||
textColor: Color = LocalContentColor.current,
|
||||
enabled: Boolean = true,
|
||||
leadingIcon: ImageVector? = null,
|
||||
leadingIconTint: Color = LocalContentColor.current,
|
||||
trailingContent: @Composable (() -> Unit),
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
ClickableWrapper(enabled = enabled, onClick = onClick) {
|
||||
Content(
|
||||
leading = { leadingIcon.Icon(leadingIconTint) },
|
||||
text = text,
|
||||
trailing = { trailingIcon.Icon(trailingIconTint) },
|
||||
textColor = textColor,
|
||||
trailing = trailingContent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A toggleable settings switch item. */
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun SettingsItemSwitch(
|
||||
checked: Boolean,
|
||||
text: String,
|
||||
textColor: Color = LocalContentColor.current,
|
||||
enabled: Boolean = true,
|
||||
leadingIcon: ImageVector? = null,
|
||||
leadingIconTint: Color = LocalContentColor.current,
|
||||
|
|
@ -76,6 +99,7 @@ fun SettingsItemSwitch(
|
|||
Content(
|
||||
leading = { leadingIcon.Icon(leadingIconTint) },
|
||||
text = text,
|
||||
textColor = textColor,
|
||||
trailing = { Switch(checked = checked, enabled = enabled, onCheckedChange = null) },
|
||||
)
|
||||
}
|
||||
|
|
@ -85,6 +109,7 @@ fun SettingsItemSwitch(
|
|||
@Composable
|
||||
fun SettingsItemDetail(
|
||||
text: String,
|
||||
textColor: Color = LocalContentColor.current,
|
||||
icon: ImageVector? = null,
|
||||
iconTint: Color = LocalContentColor.current,
|
||||
trailingText: String? = null,
|
||||
|
|
@ -92,7 +117,12 @@ fun SettingsItemDetail(
|
|||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
val content: @Composable ColumnScope.() -> Unit = {
|
||||
Content(leading = { icon.Icon(iconTint) }, text = text, trailing = { trailingText?.let { Text(text = it) } })
|
||||
Content(
|
||||
leading = { icon.Icon(iconTint) },
|
||||
text = text,
|
||||
textColor = textColor,
|
||||
trailing = { trailingText?.let { Text(text = it) } },
|
||||
)
|
||||
}
|
||||
|
||||
if (onClick != null) {
|
||||
|
|
@ -116,11 +146,11 @@ private fun ClickableWrapper(enabled: Boolean, onClick: () -> Unit, content: @Co
|
|||
|
||||
/** The row content to display for a settings item. */
|
||||
@Composable
|
||||
private fun Content(leading: @Composable () -> Unit, text: String, trailing: @Composable () -> Unit) {
|
||||
private fun Content(leading: @Composable () -> Unit, text: String, textColor: Color, trailing: @Composable () -> Unit) {
|
||||
ListItem(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
||||
headlineContent = { Text(text) },
|
||||
headlineContent = { Text(text = text, color = textColor) },
|
||||
leadingContent = { leading() },
|
||||
trailingContent = { trailing() },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,26 +18,16 @@
|
|||
package com.geeksville.mesh.ui.settings.radio
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.Upload
|
||||
import androidx.compose.material.icons.twotone.Warning
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material.icons.rounded.BugReport
|
||||
import androidx.compose.material.icons.rounded.CleaningServices
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -45,7 +35,6 @@ import androidx.compose.runtime.mutableIntStateOf
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -63,64 +52,11 @@ import com.geeksville.mesh.ui.common.components.TitledCard
|
|||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
import com.geeksville.mesh.ui.common.theme.StatusColors.StatusRed
|
||||
import com.geeksville.mesh.ui.settings.components.SettingsItem
|
||||
import com.geeksville.mesh.ui.settings.radio.components.WarningDialog
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun NavButton(@StringRes title: Int, enabled: Boolean, onClick: () -> Unit) {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
if (showDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {},
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
title = {
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.Warning,
|
||||
contentDescription = stringResource(id = R.string.warning),
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
)
|
||||
Text(text = "${stringResource(title)}?\n")
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.Warning,
|
||||
contentDescription = stringResource(id = R.string.warning),
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TextButton(modifier = Modifier.weight(1f), onClick = { showDialog = false }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
Button(
|
||||
modifier = Modifier.weight(1f),
|
||||
onClick = {
|
||||
showDialog = false
|
||||
onClick()
|
||||
},
|
||||
) {
|
||||
Text(stringResource(R.string.send))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Button(modifier = Modifier.fillMaxWidth().height(48.dp), enabled = enabled, onClick = { showDialog = true }) {
|
||||
Text(text = stringResource(title))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
fun RadioConfigItemList(
|
||||
state: RadioConfigState,
|
||||
|
|
@ -189,8 +125,26 @@ fun RadioConfigItemList(
|
|||
}
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(top = 16.dp)) {
|
||||
AdminRoute.entries.forEach { NavButton(it.title, enabled) { onRouteClick(it) } }
|
||||
TitledCard(title = stringResource(R.string.administration), modifier = Modifier.padding(top = 16.dp)) {
|
||||
AdminRoute.entries.forEach { route ->
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
if (showDialog) {
|
||||
WarningDialog(
|
||||
title = "${stringResource(route.title)}?",
|
||||
onDismiss = { showDialog = false },
|
||||
onConfirm = { onRouteClick(route) },
|
||||
)
|
||||
}
|
||||
|
||||
SettingsItem(
|
||||
enabled = enabled,
|
||||
text = stringResource(route.title),
|
||||
leadingIcon = route.icon,
|
||||
trailingIcon = null,
|
||||
) {
|
||||
showDialog = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TitledCard(title = stringResource(R.string.advanced_title), modifier = Modifier.padding(top = 16.dp)) {
|
||||
|
|
@ -200,9 +154,17 @@ fun RadioConfigItemList(
|
|||
|
||||
SettingsItem(
|
||||
text = stringResource(R.string.clean_node_database_title),
|
||||
leadingIcon = Icons.Rounded.CleaningServices,
|
||||
enabled = enabled,
|
||||
onClick = { onNavigate(SettingsRoutes.CleanNodeDb) },
|
||||
)
|
||||
|
||||
SettingsItem(
|
||||
text = stringResource(R.string.debug_panel),
|
||||
leadingIcon = Icons.Rounded.BugReport,
|
||||
enabled = enabled,
|
||||
onClick = { onNavigate(SettingsRoutes.DebugPanel) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.settings.radio.components
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Warning
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.ui.common.theme.AppTheme
|
||||
|
||||
@Composable
|
||||
fun WarningDialog(
|
||||
icon: ImageVector? = Icons.Rounded.Warning,
|
||||
title: String,
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {},
|
||||
icon = { icon?.let { Icon(imageVector = it, contentDescription = null) } },
|
||||
title = { Text(text = title) },
|
||||
dismissButton = { TextButton(onClick = { onDismiss() }) { Text(stringResource(R.string.cancel)) } },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
onConfirm()
|
||||
},
|
||||
) {
|
||||
Text(stringResource(R.string.send))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun WarningDialogPreview() {
|
||||
AppTheme { WarningDialog(title = "Factory Reset?", onDismiss = {}, onConfirm = {}) }
|
||||
}
|
||||
|
|
@ -322,6 +322,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -316,6 +316,7 @@
|
|||
<string name="udp_config">UDP конфигурација</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Корисник</string>
|
||||
<string name="channels">Канали</string>
|
||||
<string name="device">Уређај</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">Конфигуриране на UDP</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Последно чут: %2$s<br>Последна позиция: %3$s<br>Батерия: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Потребител</string>
|
||||
<string name="channels">Канали</string>
|
||||
<string name="device">Устройство</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@
|
|||
<string name="udp_config">UDP Konfigurace</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Naposledy slyšen: %2$s<br>Poslední pozice: %3$s<br>Baterie: %4$s]]></string>
|
||||
<string name="toggle_my_position">Zapnout/vypnout pozici</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Uživatel</string>
|
||||
<string name="channels">Kanály</string>
|
||||
<string name="device">Zařízení</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Konfiguration</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Zuletzt gehört:%2$s<br>Letzte Position:%3$s<br>Akku:%4$s]]></string>
|
||||
<string name="toggle_my_position">Position einschalten</string>
|
||||
<string name="orient_north">Ausrichtung Nord</string>
|
||||
<string name="user">Benutzer</string>
|
||||
<string name="channels">Kanäle</string>
|
||||
<string name="device">Gerät</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Χρήστης</string>
|
||||
<string name="channels">Κανάλια</string>
|
||||
<string name="device">Συσκευή</string>
|
||||
|
|
|
|||
|
|
@ -315,6 +315,7 @@ Rango de Valores 0 - 500.</string>
|
|||
<string name="udp_config">Configuración UDP</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Última escucha: %2$s<br>Última posición: %3$s<br>Batería: %4$s]]></string>
|
||||
<string name="toggle_my_position">Cambiar mi posición</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Usuario</string>
|
||||
<string name="channels">Canales</string>
|
||||
<string name="device">Dispositivo</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP sätted</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Viimati kuuldud: %2$s<br>viimane asukoht: %3$s<br>Akupinge: %4$s]]></string>
|
||||
<string name="toggle_my_position">Lülita asukoht sisse</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Kasutaja</string>
|
||||
<string name="channels">Kanal</string>
|
||||
<string name="device">Seade</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP asetukset</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Viimeksi kuultu: %2$s<br>Viimeisin sijainti: %3$s<br>Akku: %4$s]]></string>
|
||||
<string name="toggle_my_position">Kytke sijainti päälle</string>
|
||||
<string name="orient_north">Aseta kompassi pohjoiseen</string>
|
||||
<string name="user">Käyttäjä</string>
|
||||
<string name="channels">Kanavat</string>
|
||||
<string name="device">Laite</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">Configuration UDP</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Dernière écoute : %2$s<br>Dernière position : %3$s<br>Batterie : %4$s]]></string>
|
||||
<string name="toggle_my_position">Basculer ma position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Utilisateur</string>
|
||||
<string name="channels">Canaux</string>
|
||||
<string name="device">Appareil</string>
|
||||
|
|
|
|||
|
|
@ -320,6 +320,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -316,6 +316,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">Configurazione UDP</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Ricevuto l\'ultima volta: %2$s<br>Posizione più recente: %3$s<br>Batteria: %4$s]]></string>
|
||||
<string name="toggle_my_position">Attiva/disattiva posizione</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Utente</string>
|
||||
<string name="channels">Canali</string>
|
||||
<string name="device">Dispositivo</string>
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">自分の位置を切り替え</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">ユーザー</string>
|
||||
<string name="channels">チャンネル</string>
|
||||
<string name="device">接続するデバイスを選択</string>
|
||||
|
|
|
|||
|
|
@ -312,6 +312,7 @@
|
|||
<string name="udp_config">UDP 설정</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>최근 수신: %2$s<br>최근 위치: %3$s<br>배터리: %4$s]]></string>
|
||||
<string name="toggle_my_position">내 위치 토글</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">사용자</string>
|
||||
<string name="channels">채널</string>
|
||||
<string name="device">장치</string>
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Configuratie</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Wissel mijn positie</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Gebruiker</string>
|
||||
<string name="channels">Kanalen</string>
|
||||
<string name="device">Apparaat</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@
|
|||
<string name="udp_config">Ustawienia UDP</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Pokaż moją pozycję</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Użytkownik</string>
|
||||
<string name="channels">Kanały</string>
|
||||
<string name="device">Urządzenie</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">Configuração UDP</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Última vez: %2$s<br>Última posição: %3$s<br>Bateria: %4$s]]></string>
|
||||
<string name="toggle_my_position">Ativar minha posição</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Usuário</string>
|
||||
<string name="channels">Canais</string>
|
||||
<string name="device">Dispositivo</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">Configuração UDP</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Utilizador</string>
|
||||
<string name="channels">Canal</string>
|
||||
<string name="device">Dispositivo</string>
|
||||
|
|
|
|||
|
|
@ -316,6 +316,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Последний приём: %2$s<br>Последнее местоположение: %3$s<br>Батарея: %4$s]]></string>
|
||||
<string name="toggle_my_position">Переключить мою позицию</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Пользователь</string>
|
||||
<string name="channels">Каналы</string>
|
||||
<string name="device">Устройство</string>
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@
|
|||
<string name="udp_config">Konfigurácia UDP</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Zapnúť lokalizáciu</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Užívateľ</string>
|
||||
<string name="channels">Kanále</string>
|
||||
<string name="device">Zariadenie</string>
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Config</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Channels</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -316,6 +316,7 @@
|
|||
<string name="udp_config">UDP конфигурација</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Корисник</string>
|
||||
<string name="channels">Канали</string>
|
||||
<string name="device">Уређај</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP-konfiguration</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">User</string>
|
||||
<string name="channels">Kanaler</string>
|
||||
<string name="device">Device</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP Ayarları</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Konumunumu aç/kapa</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Kullanıcı</string>
|
||||
<string name="channels">Kanallar</string>
|
||||
<string name="device">Cihaz</string>
|
||||
|
|
|
|||
|
|
@ -318,6 +318,7 @@
|
|||
<string name="udp_config">Налаштування UDP</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>Last heard: %2$s<br>Last position: %3$s<br>Battery: %4$s]]></string>
|
||||
<string name="toggle_my_position">Toggle my position</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">Користувач</string>
|
||||
<string name="channels">Канали</string>
|
||||
<string name="device">Пристрій</string>
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@
|
|||
<string name="udp_config">UDP 设置</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>最后听到: %2$s<br>最后位置: %3$s<br>电量: %4$s]]></string>
|
||||
<string name="toggle_my_position">切换我的位置</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">用户</string>
|
||||
<string name="channels">频道</string>
|
||||
<string name="device">设备</string>
|
||||
|
|
|
|||
|
|
@ -312,6 +312,7 @@
|
|||
<string name="udp_config">UDP設置</string>
|
||||
<string name="map_node_popup_details"><![CDATA[%1$s<br>最後接收: %2$s<br>最後位置: %3$s<br>電量: %4$s]]></string>
|
||||
<string name="toggle_my_position">切換我的位置</string>
|
||||
<string name="orient_north">Orient north</string>
|
||||
<string name="user">用戶</string>
|
||||
<string name="channels">頻道</string>
|
||||
<string name="device">裝置</string>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ core-location-altitude = "1.0.0-alpha03"
|
|||
core-splashscreen = "1.0.1"
|
||||
crashlytics = "3.0.6"
|
||||
datastore = "1.1.7"
|
||||
dd-sdk-android = "2.26.0"
|
||||
dd-sdk-android = "3.0.0"
|
||||
dd-sdk-android-gradle-plugin = "1.19.0"
|
||||
detekt = "1.23.8"
|
||||
devtools-ksp = "2.2.10-2.0.2"
|
||||
|
|
@ -94,6 +94,7 @@ dd-sdk-android-session-replay = { group = "com.datadoghq", name = "dd-sdk-androi
|
|||
dd-sdk-android-session-replay-compose = { group = "com.datadoghq", name = "dd-sdk-android-session-replay-compose", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-timber = { group = "com.datadoghq", name = "dd-sdk-android-timber", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-trace = { group = "com.datadoghq", name = "dd-sdk-android-trace", version.ref = "dd-sdk-android" }
|
||||
dd-sdk-android-trace-otel = { group = "com.datadoghq", name = "dd-sdk-android-trace-otel", version.ref = "dd-sdk-android" }
|
||||
detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" }
|
||||
emoji2-emojipicker = { group = "androidx.emoji2", name = "emoji2-emojipicker", version.ref = "emoji2" }
|
||||
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
|
||||
|
|
@ -193,7 +194,7 @@ maps-compose = ["location-services", "maps-compose", "maps-compose-utils", "maps
|
|||
firebase = ["firebase-analytics", "firebase-crashlytics"]
|
||||
|
||||
# Datadog
|
||||
datadog = ["dd-sdk-android-compose", "dd-sdk-android-logs", "dd-sdk-android-okhttp", "dd-sdk-android-rum", "dd-sdk-android-session-replay", "dd-sdk-android-session-replay-compose", "dd-sdk-android-timber", "dd-sdk-android-trace"]
|
||||
datadog = ["dd-sdk-android-compose", "dd-sdk-android-logs", "dd-sdk-android-okhttp", "dd-sdk-android-rum", "dd-sdk-android-session-replay", "dd-sdk-android-session-replay-compose", "dd-sdk-android-timber", "dd-sdk-android-trace", "dd-sdk-android-trace-otel"]
|
||||
|
||||
# Protobuf
|
||||
protobuf = ["protobuf-kotlin"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue