mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-04 11:37:22 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75d162301b | ||
|
|
07c09ec3e9 | ||
|
|
513d50e56a | ||
|
|
366edd8bbb | ||
|
|
13df0dfcec |
No files matched your search
+35
-31
@@ -20,64 +20,68 @@ package com.rtbishop.look4sat.core.data.repository
|
|||||||
import com.rtbishop.look4sat.core.domain.repository.IWavelogRepository
|
import com.rtbishop.look4sat.core.domain.repository.IWavelogRepository
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the list of worked gridsquares from a self-hosted Wavelog instance.
|
* Fetches the list of worked gridsquares from a self-hosted Wavelog instance.
|
||||||
*
|
*
|
||||||
* Uses the Wavelog API v2 endpoint (Wavelog >= 3.1.0):
|
* Uses the Wavelog v1 API endpoint (verified against Wavelog 3.0.2):
|
||||||
* GET {base}/index.php/api/v2/lookup?grid=all
|
* POST {base}/index.php/api/logbook_get_worked_grids
|
||||||
* Authorization: Bearer wl2_...
|
* Body: {"key": "<api key>", "logbook_id": "<logbook id>"}
|
||||||
* Response: {"data": {"grids": ["JN47", "JO30"], "count": 2}, ...}
|
* Response 201: JSON array of 4-char gridsquares, e.g. ["JN47","JO30"]
|
||||||
|
* (see Wavelog Api.php logbook_get_worked_grids / Api_model::get_grids_worked_in_logbook)
|
||||||
*
|
*
|
||||||
* Uses a plain HttpURLConnection (bearer auth header) — IRemoteSource does not
|
* The token field carries "<api_key>:<logbook_id>" so one credential line
|
||||||
* support custom headers, and no feature module touches this class directly.
|
* configures both values (the dialog explains this to the user).
|
||||||
*/
|
*/
|
||||||
class WavelogRepository : IWavelogRepository {
|
class WavelogRepository : IWavelogRepository {
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch worked grids. Returns the grid set, or null on any failure
|
|
||||||
* (network error, non-200, malformed response, bad token).
|
|
||||||
*/
|
|
||||||
override suspend fun fetchWorkedGrids(url: String, token: String): Set<String>? = withContext(Dispatchers.IO) {
|
override suspend fun fetchWorkedGrids(url: String, token: String): Set<String>? = withContext(Dispatchers.IO) {
|
||||||
if (url.isBlank() || token.isBlank()) return@withContext null
|
val parts = token.trim().split(":")
|
||||||
|
val apiKey = parts.getOrNull(0)?.trim().orEmpty()
|
||||||
|
val logbookId = parts.getOrNull(1)?.trim().orEmpty()
|
||||||
|
if (url.isBlank() || apiKey.isBlank() || logbookId.isBlank()) return@withContext null
|
||||||
val base = url.trim().trimEnd('/')
|
val base = url.trim().trimEnd('/')
|
||||||
val requestUrl = "$base/index.php/api/v2/lookup?grid=all"
|
val requestUrl = "$base/index.php/api/logbook_get_worked_grids"
|
||||||
try {
|
try {
|
||||||
// IRemoteSource.getNetworkStream does not support custom headers, so
|
|
||||||
// run the bearer-token request directly here via java.net (simple GET,
|
|
||||||
// no OkHttp dependency added to this module's public API).
|
|
||||||
val connection = java.net.URL(requestUrl).openConnection() as java.net.HttpURLConnection
|
val connection = java.net.URL(requestUrl).openConnection() as java.net.HttpURLConnection
|
||||||
connectionTimeouts(connection)
|
connection.connectTimeout = 10_000
|
||||||
connection.setRequestProperty("Authorization", "Bearer ${token.trim()}")
|
connection.readTimeout = 15_000
|
||||||
connection.requestMethod = "GET"
|
connection.requestMethod = "POST"
|
||||||
|
connection.doOutput = true
|
||||||
|
connection.setRequestProperty("Content-Type", "application/json")
|
||||||
|
val body = JSONObject().apply {
|
||||||
|
put("key", apiKey)
|
||||||
|
put("logbook_id", logbookId)
|
||||||
|
}.toString().toByteArray()
|
||||||
|
connection.outputStream.use { it.write(body) }
|
||||||
val code = connection.responseCode
|
val code = connection.responseCode
|
||||||
if (code != 200) {
|
if (code !in 200..299) {
|
||||||
connection.disconnect()
|
connection.disconnect()
|
||||||
return@withContext null
|
return@withContext null
|
||||||
}
|
}
|
||||||
val body = connection.inputStream.bufferedReader().use { it.readText() }
|
val response = connection.inputStream.bufferedReader().use { it.readText() }
|
||||||
connection.disconnect()
|
connection.disconnect()
|
||||||
parseWorkedGrids(body)
|
parseWorkedGrids(response)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println("WavelogRepository fetch failure: $e")
|
println("WavelogRepository fetch failure: $e")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun connectionTimeouts(connection: java.net.HttpURLConnection) {
|
|
||||||
connection.connectTimeout = 10_000
|
|
||||||
connection.readTimeout = 15_000
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun parseWorkedGrids(body: String): Set<String>? {
|
internal fun parseWorkedGrids(body: String): Set<String>? {
|
||||||
return try {
|
return try {
|
||||||
val obj = JSONObject(body)
|
val array = when {
|
||||||
val data = obj.optJSONObject("data") ?: return null
|
body.trimStart().startsWith("[") -> JSONArray(body)
|
||||||
val grids = data.optJSONArray("grids") ?: return null
|
else -> {
|
||||||
|
// Tolerate {"grids": [...]} wrappers too
|
||||||
|
JSONObject(body).optJSONArray("grids") ?: return null
|
||||||
|
}
|
||||||
|
}
|
||||||
val result = mutableSetOf<String>()
|
val result = mutableSetOf<String>()
|
||||||
for (i in 0 until grids.length()) {
|
for (i in 0 until array.length()) {
|
||||||
val grid = grids.optString(i, "").trim().uppercase()
|
val grid = array.optString(i, "").trim().uppercase()
|
||||||
// Keep only well-formed 4-character Maidenhead squares (e.g. OL62)
|
// Keep only well-formed 4-character Maidenhead squares (e.g. OL62)
|
||||||
if (grid.length == 4 && grid[0] in 'A'..'R' && grid[1] in 'A'..'R'
|
if (grid.length == 4 && grid[0] in 'A'..'R' && grid[1] in 'A'..'R'
|
||||||
&& grid[2] in '0'..'9' && grid[3] in '0'..'9'
|
&& grid[2] in '0'..'9' && grid[3] in '0'..'9'
|
||||||
|
|||||||
+17
-13
@@ -2,11 +2,12 @@ package com.rtbishop.look4sat.core.domain.utility
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two version strings of the form "<major>.<minor>.<patch>[-<build>]" used by the
|
* Compares two version strings of the form "<major>.<minor>.<patch>[-<build>]" used by the
|
||||||
* BA7OPF fork releases (e.g. "4.4.6-ba7opf.6" or "v4.4.6-ba7opf.7").
|
* BA7OPF fork releases (e.g. "4.4.6-ba7opf.6" or "v4.4.6-ba7opf.9.1").
|
||||||
*
|
*
|
||||||
* The comparison is done on the numeric version segments first; when the base versions are
|
* The comparison is done on the numeric version segments first; when the base versions are
|
||||||
* equal, the trailing build number (if any) decides. This lets a "4.4.6-ba7opf.7" release be
|
* equal, the trailing build numbers of the suffix decide. All numeric segments of the suffix
|
||||||
* detected as newer than "4.4.6-ba7opf.6" while "4.5.0" trumps any "4.4.x" build.
|
* are compared as a sequence, so multi-level suffixes work correctly:
|
||||||
|
* "4.4.6-ba7opf.9.1" > "4.4.6-ba7opf.9" > "4.4.6-ba7opf.8".
|
||||||
*/
|
*/
|
||||||
object VersionComparator {
|
object VersionComparator {
|
||||||
|
|
||||||
@@ -20,25 +21,28 @@ object VersionComparator {
|
|||||||
val k = curr.first.getOrElse(i) { 0 }
|
val k = curr.first.getOrElse(i) { 0 }
|
||||||
if (c != k) return c > k
|
if (c != k) return c > k
|
||||||
}
|
}
|
||||||
return cand.second > curr.second
|
val maxBuild = maxOf(cand.second.size, curr.second.size)
|
||||||
|
for (i in 0 until maxBuild) {
|
||||||
|
val c = cand.second.getOrElse(i) { 0 }
|
||||||
|
val k = curr.second.getOrElse(i) { 0 }
|
||||||
|
if (c != k) return c > k
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a version string into (numeric base segments, trailing build number).
|
* Parses a version string into (numeric base segments, numeric suffix segments).
|
||||||
* "v4.4.6-ba7opf.7" -> ([4,4,6], 7); "4.4.6" -> ([4,4,6], 0); "4.4.6-ba7opf" -> ([4,4,6], 0).
|
* "v4.4.6-ba7opf.9.1" -> ([4,4,6], [9,1]); "4.4.6-ba7opf.8" -> ([4,4,6], [8]);
|
||||||
|
* "4.4.6" -> ([4,4,6], []); "4.4.6-ba7opf" -> ([4,4,6], []).
|
||||||
*/
|
*/
|
||||||
fun parse(version: String): Pair<List<Int>, Int> {
|
fun parse(version: String): Pair<List<Int>, List<Int>> {
|
||||||
val cleaned = version.trim().removePrefix("v")
|
val cleaned = version.trim().removePrefix("v")
|
||||||
val dashIndex = cleaned.indexOf('-')
|
val dashIndex = cleaned.indexOf('-')
|
||||||
val basePart = if (dashIndex >= 0) cleaned.substring(0, dashIndex) else cleaned
|
val basePart = if (dashIndex >= 0) cleaned.substring(0, dashIndex) else cleaned
|
||||||
val suffixPart = if (dashIndex >= 0) cleaned.substring(dashIndex + 1) else ""
|
val suffixPart = if (dashIndex >= 0) cleaned.substring(dashIndex + 1) else ""
|
||||||
val base = basePart.split('.').mapNotNull { it.toIntOrNull() }
|
val base = basePart.split('.').mapNotNull { it.toIntOrNull() }
|
||||||
// The build number is the trailing numeric segment of the suffix (e.g. "ba7opf.7" -> 7).
|
// All numeric segments of the suffix, in order (e.g. "ba7opf.9.1" -> [9, 1]).
|
||||||
val build = suffixPart
|
val build = suffixPart.split('.').mapNotNull { it.toIntOrNull() }
|
||||||
.split('.')
|
|
||||||
.lastOrNull()
|
|
||||||
?.toIntOrNull()
|
|
||||||
?: 0
|
|
||||||
return base to build
|
return base to build
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+14
-4
@@ -40,9 +40,19 @@ class VersionComparatorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `parse extracts base and build number`() {
|
fun `multi level suffixes compare correctly`() {
|
||||||
assertEquals(listOf(4, 4, 6) to 7, VersionComparator.parse("v4.4.6-ba7opf.7"))
|
// The .9.1 hotfix case that exposed the old single-build-number logic.
|
||||||
assertEquals(listOf(4, 4, 6) to 0, VersionComparator.parse("4.4.6"))
|
assertTrue(VersionComparator.isNewer("4.4.6-ba7opf.9.1", "4.4.6-ba7opf.9"))
|
||||||
assertEquals(listOf(4, 4, 6) to 0, VersionComparator.parse("4.4.6-ba7opf"))
|
assertTrue(VersionComparator.isNewer("v4.4.6-ba7opf.9.1", "4.4.6-ba7opf.8"))
|
||||||
|
assertFalse(VersionComparator.isNewer("4.4.6-ba7opf.9", "4.4.6-ba7opf.9.1"))
|
||||||
|
assertTrue(VersionComparator.isNewer("4.4.6-ba7opf.10", "4.4.6-ba7opf.9.1"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `parse extracts base and suffix segments`() {
|
||||||
|
assertEquals(listOf(4, 4, 6) to listOf(7), VersionComparator.parse("v4.4.6-ba7opf.7"))
|
||||||
|
assertEquals(listOf(4, 4, 6) to listOf(9, 1), VersionComparator.parse("v4.4.6-ba7opf.9.1"))
|
||||||
|
assertEquals(listOf(4, 4, 6) to emptyList<Int>(), VersionComparator.parse("4.4.6"))
|
||||||
|
assertEquals(listOf(4, 4, 6) to emptyList<Int>(), VersionComparator.parse("4.4.6-ba7opf"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,8 +144,8 @@
|
|||||||
<string name="prefs_wavelog_configured">已从 Wavelog 同步 — 通联 %1$d 个网格</string>
|
<string name="prefs_wavelog_configured">已从 Wavelog 同步 — 通联 %1$d 个网格</string>
|
||||||
<string name="prefs_wavelog_not_configured">未配置 — 连接你的 Wavelog 日志以在地图上高亮已通联网格</string>
|
<string name="prefs_wavelog_not_configured">未配置 — 连接你的 Wavelog 日志以在地图上高亮已通联网格</string>
|
||||||
<string name="prefs_wavelog_url">Wavelog 地址</string>
|
<string name="prefs_wavelog_url">Wavelog 地址</string>
|
||||||
<string name="prefs_wavelog_token">API 令牌</string>
|
<string name="prefs_wavelog_token">API key :日志 ID</string>
|
||||||
<string name="prefs_wavelog_hint">已同步网格数:%1$d。请先保存,再点同步。</string>
|
<string name="prefs_wavelog_hint">已同步网格数:%1$d。格式:API key:日志ID。请先保存,再点同步。</string>
|
||||||
<string name="prefs_wavelog_sync">立即同步</string>
|
<string name="prefs_wavelog_sync">立即同步</string>
|
||||||
<string name="prefs_wavelog_syncing">同步中…</string>
|
<string name="prefs_wavelog_syncing">同步中…</string>
|
||||||
<string name="prefs_locator_text">使用梅登黑德网格设置站点位置</string>
|
<string name="prefs_locator_text">使用梅登黑德网格设置站点位置</string>
|
||||||
|
|||||||
@@ -179,8 +179,8 @@
|
|||||||
<string name="prefs_wavelog_configured">Synced from Wavelog — %1$d grids worked</string>
|
<string name="prefs_wavelog_configured">Synced from Wavelog — %1$d grids worked</string>
|
||||||
<string name="prefs_wavelog_not_configured">Not configured — connect your Wavelog logbook to highlight worked grids on the map</string>
|
<string name="prefs_wavelog_not_configured">Not configured — connect your Wavelog logbook to highlight worked grids on the map</string>
|
||||||
<string name="prefs_wavelog_url">Wavelog URL</string>
|
<string name="prefs_wavelog_url">Wavelog URL</string>
|
||||||
<string name="prefs_wavelog_token">API token</string>
|
<string name="prefs_wavelog_token">API key : logbook ID</string>
|
||||||
<string name="prefs_wavelog_hint">Worked grids synced: %1$d. Save first, then tap Sync.</string>
|
<string name="prefs_wavelog_hint">Worked grids synced: %1$d. Format: API key, colon, logbook ID. Save first, then tap Sync.</string>
|
||||||
<string name="prefs_wavelog_sync">Sync now</string>
|
<string name="prefs_wavelog_sync">Sync now</string>
|
||||||
<string name="prefs_wavelog_syncing">Syncing…</string>
|
<string name="prefs_wavelog_syncing">Syncing…</string>
|
||||||
<string name="prefs_locator_text">Set station\'s position using locator</string>
|
<string name="prefs_locator_text">Set station\'s position using locator</string>
|
||||||
|
|||||||
+21
-10
@@ -25,6 +25,8 @@ import org.osmdroid.views.Projection
|
|||||||
import org.osmdroid.views.overlay.Overlay
|
import org.osmdroid.views.overlay.Overlay
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maidenhead locator grid overlay, drawn directly on the map canvas.
|
* Maidenhead locator grid overlay, drawn directly on the map canvas.
|
||||||
@@ -71,10 +73,10 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
// Visible bounding box in geographic coordinates
|
// Visible bounding box in geographic coordinates
|
||||||
val north = projection.fromPixels(0, 0)
|
val north = projection.fromPixels(0, 0)
|
||||||
val south = projection.fromPixels(canvas.width, canvas.height)
|
val south = projection.fromPixels(canvas.width, canvas.height)
|
||||||
val topLat = north.latitude.coerceIn(-90.0, 90.0)
|
val topLat = max(north.latitude, south.latitude).coerceIn(-90.0, 90.0)
|
||||||
val bottomLat = south.latitude.coerceIn(-90.0, 90.0)
|
val bottomLat = min(north.latitude, south.latitude).coerceIn(-90.0, 90.0)
|
||||||
val leftLon = south.longitude
|
val leftLon = min(north.longitude, south.longitude)
|
||||||
val rightLon = north.longitude
|
val rightLon = max(north.longitude, south.longitude)
|
||||||
|
|
||||||
val firstRow = floor(bottomLat / cellLat).toInt()
|
val firstRow = floor(bottomLat / cellLat).toInt()
|
||||||
val lastRow = ceil(topLat / cellLat).toInt()
|
val lastRow = ceil(topLat / cellLat).toInt()
|
||||||
@@ -82,6 +84,7 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
// crosses the antimeridian; index arithmetic still works (PMxx at 360° == same grid)
|
// crosses the antimeridian; index arithmetic still works (PMxx at 360° == same grid)
|
||||||
val firstCol = floor(leftLon / cellLon).toInt()
|
val firstCol = floor(leftLon / cellLon).toInt()
|
||||||
val lastCol = ceil(rightLon / cellLon).toInt()
|
val lastCol = ceil(rightLon / cellLon).toInt()
|
||||||
|
val centerLon = (leftLon + rightLon) / 2.0
|
||||||
|
|
||||||
// Worked-grid highlight fills (only meaningful at sub-square zoom)
|
// Worked-grid highlight fills (only meaningful at sub-square zoom)
|
||||||
if (workedGrids.isNotEmpty() && zoom >= GRID_ZOOM_SUB) {
|
if (workedGrids.isNotEmpty() && zoom >= GRID_ZOOM_SUB) {
|
||||||
@@ -95,8 +98,8 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
if (yTop == null || yBottom == null) continue
|
if (yTop == null || yBottom == null) continue
|
||||||
for (col in firstCol..lastCol) {
|
for (col in firstCol..lastCol) {
|
||||||
val lon = col * cellLon
|
val lon = col * cellLon
|
||||||
val xLeft = projectionToX(projection, lon) ?: continue
|
val xLeft = projectionToX(projection, lon, centerLon) ?: continue
|
||||||
val xRight = projectionToX(projection, lon + cellLon) ?: continue
|
val xRight = projectionToX(projection, lon + cellLon, centerLon) ?: continue
|
||||||
if (xRight < 0f || xLeft > canvas.width) continue
|
if (xRight < 0f || xLeft > canvas.width) continue
|
||||||
if (cellLabel(lat, lon, zoom) in workedGrids) {
|
if (cellLabel(lat, lon, zoom) in workedGrids) {
|
||||||
canvas.drawRect(xLeft, yTop, xRight, yBottom, workedPaint)
|
canvas.drawRect(xLeft, yTop, xRight, yBottom, workedPaint)
|
||||||
@@ -108,7 +111,7 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
// Vertical lines (meridians)
|
// Vertical lines (meridians)
|
||||||
for (col in firstCol..lastCol) {
|
for (col in firstCol..lastCol) {
|
||||||
val lon = col * cellLon
|
val lon = col * cellLon
|
||||||
val x = projectionToX(projection, lon)
|
val x = projectionToX(projection, lon, centerLon)
|
||||||
if (x == null) continue
|
if (x == null) continue
|
||||||
canvas.drawLine(x, 0f, x, canvas.height.toFloat(), linePaint)
|
canvas.drawLine(x, 0f, x, canvas.height.toFloat(), linePaint)
|
||||||
}
|
}
|
||||||
@@ -147,7 +150,7 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
if (y < -labelPaint.textSize || y > canvas.height) continue
|
if (y < -labelPaint.textSize || y > canvas.height) continue
|
||||||
for (col in firstCol..lastCol) {
|
for (col in firstCol..lastCol) {
|
||||||
val lon = col * cellLon
|
val lon = col * cellLon
|
||||||
val x = projectionToX(projection, lon) ?: continue
|
val x = projectionToX(projection, lon, centerLon) ?: continue
|
||||||
if (x < -200f || x > canvas.width) continue
|
if (x < -200f || x > canvas.width) continue
|
||||||
val label = cellLabel(lat, lon, zoom)
|
val label = cellLabel(lat, lon, zoom)
|
||||||
canvas.drawText(label, x + labelOffset, y + labelPaint.textSize + labelOffset, labelPaint)
|
canvas.drawText(label, x + labelOffset, y + labelPaint.textSize + labelOffset, labelPaint)
|
||||||
@@ -156,8 +159,16 @@ class MaidenheadGridOverlay : Overlay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** X pixel for a longitude (meridians are vertical in Web Mercator). */
|
/** X pixel for a longitude (meridians are vertical in Web Mercator). */
|
||||||
private fun projectionToX(projection: Projection, lon: Double): Float? {
|
private fun projectionToX(projection: Projection, lon: Double, centerLon: Double): Float? {
|
||||||
val geo = org.osmdroid.util.GeoPoint(0.0, lon)
|
// osmdroid clips Mercator X to [0, mapSize], which destroys the projection of
|
||||||
|
// longitudes outside the [0, 360) window of the current view (visible at low
|
||||||
|
// zoom where the whole world is narrower than the viewport). Normalizing the
|
||||||
|
// longitude to the equivalent value closest to the view center keeps every
|
||||||
|
// meridian's X near the center, safely inside the clip window.
|
||||||
|
var normalized = lon
|
||||||
|
while (normalized < centerLon - 180.0) normalized += 360.0
|
||||||
|
while (normalized > centerLon + 180.0) normalized -= 360.0
|
||||||
|
val geo = org.osmdroid.util.GeoPoint(0.0, normalized)
|
||||||
val p = projection.toPixels(geo, null)
|
val p = projection.toPixels(geo, null)
|
||||||
return if (p.x in -MAX_OVERSHOOT_PX..MAX_OVERSHOOT_PX + 4096) p.x.toFloat() else null
|
return if (p.x in -MAX_OVERSHOOT_PX..MAX_OVERSHOOT_PX + 4096) p.x.toFloat() else null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,7 +173,9 @@ private fun MapScreen(uiState: MapState, onAction: (MapAction) -> Unit, mapView:
|
|||||||
view.invalidate()
|
view.invalidate()
|
||||||
}
|
}
|
||||||
uiState.mapData?.let { mapData ->
|
uiState.mapData?.let { mapData ->
|
||||||
if (isVertical) MapDataCard(mapData) else MapDataCards(mapData)
|
if (!uiState.isGridMode) {
|
||||||
|
if (isVertical) MapDataCard(mapData) else MapDataCards(mapData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -188,7 +188,7 @@ fun WavelogDialog(
|
|||||||
value = token.value,
|
value = token.value,
|
||||||
onValueChange = { token.value = it },
|
onValueChange = { token.value = it },
|
||||||
label = { Text(text = stringResource(id = R.string.prefs_wavelog_token)) },
|
label = { Text(text = stringResource(id = R.string.prefs_wavelog_token)) },
|
||||||
placeholder = { Text(text = "wl2_...") },
|
placeholder = { Text(text = "abcdef123456:1") },
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
modifier = Modifier.fillMaxWidth().padding(horizontal = LocalSpacing.current.large),
|
modifier = Modifier.fillMaxWidth().padding(horizontal = LocalSpacing.current.large),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[versions]
|
[versions]
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
appVersionCode = "452"
|
appVersionCode = "454"
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
appVersionName = "4.4.6-ba7opf.9"
|
appVersionName = "4.4.6-ba7opf.9.2"
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
compileSdk = "37"
|
compileSdk = "37"
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
|
|||||||
Reference in new issue
Block a user