Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff408747cd | ||
|
|
6636dd8e98 | ||
|
|
15ab82f1c8 | ||
|
|
0ea9c0ede1 | ||
|
|
c496d90d5a | ||
|
|
c101870b71 | ||
|
|
9a4ceddcb0 | ||
|
|
50233cadc1 | ||
|
|
8d88100b15 | ||
|
|
59d6e2ae05 | ||
|
|
e2b5c6d673 | ||
|
|
da89d6426f |
No files matched your search
@@ -131,12 +131,17 @@ fun MainScreen(navigateToRadar: () -> Unit = {}) {
|
|||||||
val currentKey = backStack.lastOrNull()
|
val currentKey = backStack.lastOrNull()
|
||||||
val navigateBack: () -> Unit = { backStack.removeLastOrNull() }
|
val navigateBack: () -> Unit = { backStack.removeLastOrNull() }
|
||||||
val fadeTransition = fadeIn(animationSpec = tween(350)) togetherWith fadeOut(animationSpec = tween(350))
|
val fadeTransition = fadeIn(animationSpec = tween(350)) togetherWith fadeOut(animationSpec = tween(350))
|
||||||
val navItems = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Mutual, Screen.Roaming, Screen.Map, Screen.Settings)
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val container = (context.applicationContext as IContainerProvider).getMainContainer()
|
val container = (context.applicationContext as IContainerProvider).getMainContainer()
|
||||||
val trackingState by container.radioTrackingService.state.collectAsStateWithLifecycle()
|
val trackingState by container.radioTrackingService.state.collectAsStateWithLifecycle()
|
||||||
val otherSettings by container.settingsRepo.otherSettings.collectAsStateWithLifecycle()
|
val otherSettings by container.settingsRepo.otherSettings.collectAsStateWithLifecycle()
|
||||||
|
// UI 设置: 按 screenOrder 排序(空 = 默认顺序), 再按 hiddenScreens 过滤(设置页固定保留)
|
||||||
|
val navItems = listOf(Screen.Satellites, Screen.Passes, Screen.Radar, Screen.Mutual, Screen.Roaming, Screen.Map, Screen.Settings)
|
||||||
|
.sortedBy { screen ->
|
||||||
|
val idx = otherSettings.screenOrder.indexOf(screen.screenId)
|
||||||
|
if (idx == -1) Int.MAX_VALUE else idx
|
||||||
|
}
|
||||||
|
.filter { it.screenId !in otherSettings.hiddenScreens || it is Screen.Settings }
|
||||||
// Activity-scoped so the mutual query results survive navigation to Radar and back
|
// Activity-scoped so the mutual query results survive navigation to Radar and back
|
||||||
val mutualViewModel: MutualViewModel = viewModel(
|
val mutualViewModel: MutualViewModel = viewModel(
|
||||||
viewModelStoreOwner = context as ViewModelStoreOwner,
|
viewModelStoreOwner = context as ViewModelStoreOwner,
|
||||||
|
|||||||
@@ -69,11 +69,14 @@ class DatabaseRepo(
|
|||||||
val dataSourcesSettings = settingsRepo.dataSourcesSettings.value
|
val dataSourcesSettings = settingsRepo.dataSourcesSettings.value
|
||||||
val tleUrls = buildMap {
|
val tleUrls = buildMap {
|
||||||
putAll(Sources.satelliteDataUrls)
|
putAll(Sources.satelliteDataUrls)
|
||||||
if (dataSourcesSettings.useCustomTLE) put(customSourceType, dataSourcesSettings.tleUrl)
|
// 开关开且 URL 非空 -> All 用自定义 URL;否则用默认 URL(在线更新默认源)
|
||||||
|
put("All", if (dataSourcesSettings.useCustomTLE && dataSourcesSettings.tleUrl.isNotBlank())
|
||||||
|
dataSourcesSettings.tleUrl else Sources.defaultTleUrl)
|
||||||
}.filterValues { it.isNotBlank() }
|
}.filterValues { it.isNotBlank() }
|
||||||
val radioUrls = buildMap {
|
val radioUrls = buildMap {
|
||||||
putAll(Sources.transceiversDataUrls)
|
putAll(Sources.transceiversDataUrls)
|
||||||
if (dataSourcesSettings.useCustomTransceivers) put(customSourceType, dataSourcesSettings.transceiversUrl)
|
put("SatNOGS", if (dataSourcesSettings.useCustomTransceivers && dataSourcesSettings.transceiversUrl.isNotBlank())
|
||||||
|
dataSourcesSettings.transceiversUrl else Sources.defaultTransceiversUrl)
|
||||||
}.filterValues { it.isNotBlank() }
|
}.filterValues { it.isNotBlank() }
|
||||||
// launch all network requests concurrently
|
// launch all network requests concurrently
|
||||||
val tleJobs = tleUrls.values.map { url -> async { url to remoteSource.getNetworkStream(url) } }
|
val tleJobs = tleUrls.values.map { url -> async { url to remoteSource.getNetworkStream(url) } }
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import com.rtbishop.look4sat.core.domain.model.OtherSettings
|
|||||||
import com.rtbishop.look4sat.core.domain.model.PassesSettings
|
import com.rtbishop.look4sat.core.domain.model.PassesSettings
|
||||||
import com.rtbishop.look4sat.core.domain.model.RCSettings
|
import com.rtbishop.look4sat.core.domain.model.RCSettings
|
||||||
import com.rtbishop.look4sat.core.domain.model.RadioControlSettings
|
import com.rtbishop.look4sat.core.domain.model.RadioControlSettings
|
||||||
|
import com.rtbishop.look4sat.core.domain.source.Sources
|
||||||
import com.rtbishop.look4sat.core.domain.predict.GeoPos
|
import com.rtbishop.look4sat.core.domain.predict.GeoPos
|
||||||
import com.rtbishop.look4sat.core.domain.repository.ISettingsRepo
|
import com.rtbishop.look4sat.core.domain.repository.ISettingsRepo
|
||||||
import com.rtbishop.look4sat.core.domain.utility.positionToQth
|
import com.rtbishop.look4sat.core.domain.utility.positionToQth
|
||||||
@@ -76,6 +77,8 @@ class SettingsRepo(
|
|||||||
private val keyStateOfUtc = "stateOfUtc"
|
private val keyStateOfUtc = "stateOfUtc"
|
||||||
private val keyStateOfLightTheme = "stateOfLightTheme"
|
private val keyStateOfLightTheme = "stateOfLightTheme"
|
||||||
private val keyStateOfNightMode = "stateOfNightMode"
|
private val keyStateOfNightMode = "stateOfNightMode"
|
||||||
|
private val keyHiddenScreens = "hiddenScreens"
|
||||||
|
private val keyScreenOrder = "screenOrder"
|
||||||
private val keyStationAltitude = "stationAltitude"
|
private val keyStationAltitude = "stationAltitude"
|
||||||
private val keyStationLatitude = "stationLatitude"
|
private val keyStationLatitude = "stationLatitude"
|
||||||
private val keyStationLongitude = "stationLongitude"
|
private val keyStationLongitude = "stationLongitude"
|
||||||
@@ -356,6 +359,8 @@ class SettingsRepo(
|
|||||||
putString(keySstvMode, new.sstvMode)
|
putString(keySstvMode, new.sstvMode)
|
||||||
putLong(keyLowElevation, new.lowElevation.toRawBits())
|
putLong(keyLowElevation, new.lowElevation.toRawBits())
|
||||||
putLong(keyHighElevation, new.highElevation.toRawBits())
|
putLong(keyHighElevation, new.highElevation.toRawBits())
|
||||||
|
putStringSet(keyHiddenScreens, new.hiddenScreens.toSet())
|
||||||
|
putString(keyScreenOrder, new.screenOrder.joinToString(","))
|
||||||
}
|
}
|
||||||
new
|
new
|
||||||
}
|
}
|
||||||
@@ -371,6 +376,8 @@ class SettingsRepo(
|
|||||||
shouldSeeWarning = preferences.getBoolean(keyShouldSeeWarning, true),
|
shouldSeeWarning = preferences.getBoolean(keyShouldSeeWarning, true),
|
||||||
shouldSeeWhatsNew = preferences.getBoolean(keyShouldSeeWhatsNew, true),
|
shouldSeeWhatsNew = preferences.getBoolean(keyShouldSeeWhatsNew, true),
|
||||||
sstvMode = preferences.getString(keySstvMode, null) ?: "Auto",
|
sstvMode = preferences.getString(keySstvMode, null) ?: "Auto",
|
||||||
|
hiddenScreens = preferences.getStringSet(keyHiddenScreens, emptySet())?.toList() ?: emptyList(),
|
||||||
|
screenOrder = preferences.getString(keyScreenOrder, null)?.split(",")?.filter { it.isNotBlank() } ?: emptyList(),
|
||||||
lowElevation = Double.fromBits(preferences.getLong(keyLowElevation, 15.0.toRawBits())),
|
lowElevation = Double.fromBits(preferences.getLong(keyLowElevation, 15.0.toRawBits())),
|
||||||
highElevation = Double.fromBits(preferences.getLong(keyHighElevation, 45.0.toRawBits()))
|
highElevation = Double.fromBits(preferences.getLong(keyHighElevation, 45.0.toRawBits()))
|
||||||
)
|
)
|
||||||
@@ -390,12 +397,20 @@ class SettingsRepo(
|
|||||||
_dataSourcesSettings.value = settings
|
_dataSourcesSettings.value = settings
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDataSourcesSettings(): DataSourcesSettings = DataSourcesSettings(
|
private fun getDataSourcesSettings(): DataSourcesSettings {
|
||||||
useCustomTLE = preferences.getBoolean(keyUseCustomTle, false),
|
// 4.4.8 修复: 旧版 example.com 占位 URL 视为未配置 -> 替换为真实默认 URL 且开关强制关闭,
|
||||||
useCustomTransceivers = preferences.getBoolean(keyUseCustomTransceivers, false),
|
// 否则在线更新的 All/SatNOGS 源会指向错误地址导致更新失败
|
||||||
tleUrl = preferences.getString(keyTleUrl, "https://example.com/tle.txt") ?: "",
|
val storedTleUrl = preferences.getString(keyTleUrl, Sources.defaultTleUrl) ?: Sources.defaultTleUrl
|
||||||
transceiversUrl = preferences.getString(keyTransceiversUrl, "https://example.com/radio.json") ?: ""
|
val storedTxUrl = preferences.getString(keyTransceiversUrl, Sources.defaultTransceiversUrl) ?: Sources.defaultTransceiversUrl
|
||||||
)
|
val tleUrl = if (storedTleUrl == "https://example.com/tle.txt") Sources.defaultTleUrl else storedTleUrl
|
||||||
|
val txUrl = if (storedTxUrl == "https://example.com/radio.json") Sources.defaultTransceiversUrl else storedTxUrl
|
||||||
|
return DataSourcesSettings(
|
||||||
|
useCustomTLE = preferences.getBoolean(keyUseCustomTle, false) && tleUrl != Sources.defaultTleUrl,
|
||||||
|
useCustomTransceivers = preferences.getBoolean(keyUseCustomTransceivers, false) && txUrl != Sources.defaultTransceiversUrl,
|
||||||
|
tleUrl = tleUrl,
|
||||||
|
transceiversUrl = txUrl
|
||||||
|
)
|
||||||
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region # Radio control settings
|
//region # Radio control settings
|
||||||
|
|||||||
+2
-1
@@ -102,7 +102,8 @@ class DatabaseRepoTest {
|
|||||||
repository.updateFromRemote()
|
repository.updateFromRemote()
|
||||||
|
|
||||||
assertTrue(localSource.insertedEntries.any { it.catnum == 25544 })
|
assertTrue(localSource.insertedEntries.any { it.catnum == 25544 })
|
||||||
assertEquals(listOf(25544), settingsRepo.satelliteTypeIdsByType["Other"])
|
// 新语义: 开关开 + URL 非空 -> All 源用自定义 URL, 数据归入 All 类型
|
||||||
|
assertEquals(listOf(25544), settingsRepo.satelliteTypeIdsByType["All"])
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validCsvStream(): InputStream = """
|
private fun validCsvStream(): InputStream = """
|
||||||
|
|||||||
@@ -62,7 +62,11 @@ data class OtherSettings(
|
|||||||
val shouldSeeWhatsNew: Boolean,
|
val shouldSeeWhatsNew: Boolean,
|
||||||
val sstvMode: String = "Auto",
|
val sstvMode: String = "Auto",
|
||||||
val lowElevation: Double = 15.0,
|
val lowElevation: Double = 15.0,
|
||||||
val highElevation: Double = 45.0
|
val highElevation: Double = 45.0,
|
||||||
|
// UI 设置: 底部导航栏隐藏的页面(Screen simpleName 列表, 默认空 = 全部显示)
|
||||||
|
val hiddenScreens: List<String> = emptyList(),
|
||||||
|
// UI 设置: 页面顺序(空 = 默认顺序: 卫星/过境/雷达/匹配/漫游/地图/设置)
|
||||||
|
val screenOrder: List<String> = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DataSourcesSettings(
|
data class DataSourcesSettings(
|
||||||
|
|||||||
@@ -18,6 +18,10 @@
|
|||||||
package com.rtbishop.look4sat.core.domain.source
|
package com.rtbishop.look4sat.core.domain.source
|
||||||
|
|
||||||
object Sources {
|
object Sources {
|
||||||
|
// 在线更新的默认 URL(用户可通过"自定义URL"对话框修改/重置)
|
||||||
|
val defaultTleUrl = "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv"
|
||||||
|
val defaultTransceiversUrl = "https://db.satnogs.org/api/transmitters/?format=json&status=active"
|
||||||
|
|
||||||
val satelliteDataUrls = mapOf(
|
val satelliteDataUrls = mapOf(
|
||||||
"All" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
|
"All" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=csv",
|
||||||
"Amateur" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=csv",
|
"Amateur" to "https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=csv",
|
||||||
|
|||||||
+11
-8
@@ -21,30 +21,33 @@ import androidx.navigation3.runtime.NavKey
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
sealed class Screen(val iconResId: Int, val titleResId: Int) : NavKey {
|
sealed class Screen(val iconResId: Int, val titleResId: Int, val screenId: String) : NavKey {
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Satellites : Screen(R.drawable.ic_satellites, R.string.nav_sat)
|
data object Satellites : Screen(R.drawable.ic_satellites, R.string.nav_sat, "Satellites")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Passes : Screen(R.drawable.ic_passes, R.string.nav_pass)
|
data object Passes : Screen(R.drawable.ic_passes, R.string.nav_pass, "Passes")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Radar : Screen(R.drawable.ic_radar, R.string.nav_radar)
|
data object Radar : Screen(R.drawable.ic_radar, R.string.nav_radar, "Radar")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Map : Screen(R.drawable.ic_map, R.string.nav_map)
|
data object Map : Screen(R.drawable.ic_map, R.string.nav_map, "Map")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Mutual : Screen(R.drawable.ic_match, R.string.nav_mutual)
|
data object Mutual : Screen(R.drawable.ic_match, R.string.nav_mutual, "Mutual")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Roaming : Screen(R.drawable.ic_roaming, R.string.nav_roaming)
|
data object Roaming : Screen(R.drawable.ic_roaming, R.string.nav_roaming, "Roaming")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Settings : Screen(R.drawable.ic_settings, R.string.nav_prefs)
|
data object Settings : Screen(R.drawable.ic_settings, R.string.nav_prefs, "Settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UI 设置: 默认页面顺序(与 MainScreen navItems 一致)
|
||||||
|
val defaultScreenOrder = listOf("Satellites", "Passes", "Radar", "Mutual", "Roaming", "Map", "Settings")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data object RadarDestination : NavKey
|
data object RadarDestination : NavKey
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zM18.76,7.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z" />
|
||||||
|
</vector>
|
||||||
@@ -51,12 +51,8 @@
|
|||||||
\n\nDoğru tahminler alabilmek için veritabanını en az haftada bir güncelleyin.</string>
|
\n\nDoğru tahminler alabilmek için veritabanını en az haftada bir güncelleyin.</string>
|
||||||
<string name="pass_whatsnew_title" translatable="false">What\'s new in Look4Sat</string>
|
<string name="pass_whatsnew_title" translatable="false">What\'s new in Look4Sat</string>
|
||||||
<string name="pass_whatsnew_message" translatable="false">
|
<string name="pass_whatsnew_message" translatable="false">
|
||||||
* Roaming sayfası QTH定位器 2.0'dan birebir taşındı: 8 haneli konum, 3x3 ızgara, kırmızı işaret, canlı GPS
|
* Ayarlar sayfasında kaydırırken oluşan çökme düzeltildi
|
||||||
\n\n* İçeriğin sistem durum/gezinme çubuklarıyla çakışması düzeltildi; ızgara sütunları referans düzenle eşleşiyor
|
* Sayfa sırası: tek hareketle birden fazla konum atlanabilir; tutamaç tek nokta oldu</string>
|
||||||
\n\n* Roaming sayfası renkleri uygulama temasını takip eder - kırmızı gece modunda tamamen görünür
|
|
||||||
\n\n* Ayarlar: sıkışan anahtar satırları ve teşekkür aralığı düzeltildi
|
|
||||||
\n\n* Uygulama adı artık Look4Sat Pro
|
|
||||||
</string>
|
|
||||||
|
|
||||||
<!-- Radar screen -->
|
<!-- Radar screen -->
|
||||||
<string name="radar_back">Geri</string>
|
<string name="radar_back">Geri</string>
|
||||||
@@ -137,7 +133,7 @@
|
|||||||
<string name="prefs_data_entries">Uydular: %s</string>
|
<string name="prefs_data_entries">Uydular: %s</string>
|
||||||
<string name="prefs_data_radios">Transceiver\'lar: %s</string>
|
<string name="prefs_data_radios">Transceiver\'lar: %s</string>
|
||||||
<string name="prefs_data_update">Güncelle</string>
|
<string name="prefs_data_update">Güncelle</string>
|
||||||
<string name="prefs_data_import">İçe aktar</string>
|
<string name="prefs_data_import">Özel URL</string>
|
||||||
<string name="prefs_data_clear">Temizle</string>
|
<string name="prefs_data_clear">Temizle</string>
|
||||||
<string name="prefs_data_clear_success">Veriler başarıyla temizlendi</string>
|
<string name="prefs_data_clear_success">Veriler başarıyla temizlendi</string>
|
||||||
<string name="prefs_data_update_success">Güncelleme başarıyla tamamlandı</string>
|
<string name="prefs_data_update_success">Güncelleme başarıyla tamamlandı</string>
|
||||||
@@ -146,6 +142,7 @@
|
|||||||
<string name="prefs_data_sources_tle_switch">Özel TLE URL</string>
|
<string name="prefs_data_sources_tle_switch">Özel TLE URL</string>
|
||||||
<string name="prefs_data_sources_transceivers_switch">Özel transceiver URL</string>
|
<string name="prefs_data_sources_transceivers_switch">Özel transceiver URL</string>
|
||||||
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
|
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
|
||||||
|
<string name="prefs_data_reset_url">Varsayılan URL\'ye sıfırla</string>
|
||||||
|
|
||||||
<string name="prefs_data_output_title">Veri aktarımı</string>
|
<string name="prefs_data_output_title">Veri aktarımı</string>
|
||||||
<string name="prefs_net_output">Ağ</string>
|
<string name="prefs_net_output">Ağ</string>
|
||||||
@@ -181,6 +178,9 @@
|
|||||||
<string name="prefs_net_perm_error">Ağ izninizi kontrol edin</string>
|
<string name="prefs_net_perm_error">Ağ izninizi kontrol edin</string>
|
||||||
|
|
||||||
<string name="prefs_other_title">Diğer ayarlar</string>
|
<string name="prefs_other_title">Diğer ayarlar</string>
|
||||||
|
<string name="prefs_ui_title">UI Ayarları</string>
|
||||||
|
<string name="prefs_ui_order_title">Sayfa sırası</string>
|
||||||
|
<string name="prefs_ui_order_reset">Sıralamayı sıfırla</string>
|
||||||
<string name="prefs_other_switch_utc">Geçiş saatini UTC olarak göster</string>
|
<string name="prefs_other_switch_utc">Geçiş saatini UTC olarak göster</string>
|
||||||
<string name="prefs_other_switch_update">Otomatik veri güncellemeyi etkinleştir</string>
|
<string name="prefs_other_switch_update">Otomatik veri güncellemeyi etkinleştir</string>
|
||||||
<string name="prefs_other_switch_sweep">Radar taramasını etkinleştir</string>
|
<string name="prefs_other_switch_sweep">Radar taramasını etkinleştir</string>
|
||||||
|
|||||||
@@ -40,13 +40,9 @@
|
|||||||
<string name="pass_welcome_title">欢迎来到 Look4Sat\!</string>
|
<string name="pass_welcome_title">欢迎来到 Look4Sat\!</string>
|
||||||
<string name="pass_welcome_message">请务必在设置中通过GPS、经纬度或QTH定位您的位置\n建议至少每周更新一次数据库,以确保预测结果的准确性</string>
|
<string name="pass_welcome_message">请务必在设置中通过GPS、经纬度或QTH定位您的位置\n建议至少每周更新一次数据库,以确保预测结果的准确性</string>
|
||||||
<string name="pass_whatsnew_title">Look4Sat Pro 更新内容</string>
|
<string name="pass_whatsnew_title">Look4Sat Pro 更新内容</string>
|
||||||
<string name="pass_whatsnew_message">
|
<string name="pass_whatsnew_message" translatable="false">
|
||||||
* 漫游页完整移植 QTH定位器 2.0:8 位网格、3x3 九宫格、红点查表定位、实时 GPS
|
* 修复设置页滑动时闪退的问题
|
||||||
\n\n* 修复内容被系统状态栏/导航栏遮挡;九宫格列宽与原版一致
|
* 页面顺序:可一次拖动越过多个位置;拖拽手柄改为小圆点</string>
|
||||||
\n\n* 漫游页配色跟随应用主题,红色夜间模式下清晰可见
|
|
||||||
\n\n* 设置:修复开关行挤压与感谢列表间距
|
|
||||||
\n\n* 应用名现为 Look4Sat Pro
|
|
||||||
</string>
|
|
||||||
|
|
||||||
<!-- Radar screen -->
|
<!-- Radar screen -->
|
||||||
<string name="radar_back">后退</string>
|
<string name="radar_back">后退</string>
|
||||||
@@ -119,7 +115,7 @@
|
|||||||
<string name="prefs_data_entries">卫星:%s</string>
|
<string name="prefs_data_entries">卫星:%s</string>
|
||||||
<string name="prefs_data_radios">收发器:%s</string>
|
<string name="prefs_data_radios">收发器:%s</string>
|
||||||
<string name="prefs_data_update">在线更新</string>
|
<string name="prefs_data_update">在线更新</string>
|
||||||
<string name="prefs_data_import">导入文件</string>
|
<string name="prefs_data_import">自定义URL</string>
|
||||||
<string name="prefs_data_clear">清空</string>
|
<string name="prefs_data_clear">清空</string>
|
||||||
<string name="prefs_data_clear_success">数据清空成功</string>
|
<string name="prefs_data_clear_success">数据清空成功</string>
|
||||||
<string name="prefs_data_update_success">更新成功</string>
|
<string name="prefs_data_update_success">更新成功</string>
|
||||||
@@ -165,6 +161,9 @@
|
|||||||
<string name="rc_enable_switch">启用 CAT 控制</string>
|
<string name="rc_enable_switch">启用 CAT 控制</string>
|
||||||
|
|
||||||
<string name="prefs_other_title">其他设置</string>
|
<string name="prefs_other_title">其他设置</string>
|
||||||
|
<string name="prefs_ui_title">UI设置</string>
|
||||||
|
<string name="prefs_ui_order_title">页面顺序</string>
|
||||||
|
<string name="prefs_ui_order_reset">重置顺序</string>
|
||||||
<string name="prefs_other_switch_utc">以 UTC 时间显示</string>
|
<string name="prefs_other_switch_utc">以 UTC 时间显示</string>
|
||||||
<string name="prefs_other_switch_update">启用卫星数据自动更新</string>
|
<string name="prefs_other_switch_update">启用卫星数据自动更新</string>
|
||||||
<string name="prefs_other_switch_sweep">启用雷达扫描动画</string>
|
<string name="prefs_other_switch_sweep">启用雷达扫描动画</string>
|
||||||
|
|||||||
@@ -53,12 +53,8 @@
|
|||||||
\n\nPlease update the database at least weekly to get accurate predictions.</string>
|
\n\nPlease update the database at least weekly to get accurate predictions.</string>
|
||||||
<string name="pass_whatsnew_title" translatable="false">What\'s new in Look4Sat</string>
|
<string name="pass_whatsnew_title" translatable="false">What\'s new in Look4Sat</string>
|
||||||
<string name="pass_whatsnew_message" translatable="false">
|
<string name="pass_whatsnew_message" translatable="false">
|
||||||
* Roaming page ported verbatim from QTH定位器 2.0: 8-char locator, 3x3 grid, red marker lookup, live GPS
|
* Fixed a crash when scrolling the Settings page
|
||||||
\n\n* Fixed content overlapping the system status/navigation bars; grid columns now match the reference layout
|
* Page order: drag a page past multiple positions in one gesture; grip is now a single dot</string>
|
||||||
\n\n* Roaming page colors follow the app theme - fully visible in red night mode
|
|
||||||
\n\n* Settings: fixed squeezed toggle rows and credits spacing
|
|
||||||
\n\n* App name is now Look4Sat Pro
|
|
||||||
</string>
|
|
||||||
|
|
||||||
<!-- Radar screen -->
|
<!-- Radar screen -->
|
||||||
<string name="radar_back">Back</string>
|
<string name="radar_back">Back</string>
|
||||||
@@ -149,7 +145,7 @@
|
|||||||
<string name="prefs_data_entries">Satellites: %s</string>
|
<string name="prefs_data_entries">Satellites: %s</string>
|
||||||
<string name="prefs_data_radios">Transceivers: %s</string>
|
<string name="prefs_data_radios">Transceivers: %s</string>
|
||||||
<string name="prefs_data_update">Update</string>
|
<string name="prefs_data_update">Update</string>
|
||||||
<string name="prefs_data_import">Import</string>
|
<string name="prefs_data_import">Custom URL</string>
|
||||||
<string name="prefs_data_clear">Clear</string>
|
<string name="prefs_data_clear">Clear</string>
|
||||||
<string name="prefs_data_clear_success">Data was cleared successfully</string>
|
<string name="prefs_data_clear_success">Data was cleared successfully</string>
|
||||||
<string name="prefs_data_update_success">Update completed successfully</string>
|
<string name="prefs_data_update_success">Update completed successfully</string>
|
||||||
@@ -160,6 +156,7 @@
|
|||||||
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
|
<string name="prefs_data_sources_tle_switch">Custom TLE URL</string>
|
||||||
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
|
<string name="prefs_data_sources_transceivers_switch">Custom transceivers URL</string>
|
||||||
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
|
<string name="prefs_data_sources_url_title" translatable="false">URL (HTTPS)</string>
|
||||||
|
<string name="prefs_data_reset_url">Reset to default URL</string>
|
||||||
|
|
||||||
<string name="prefs_data_output_title">Data output</string>
|
<string name="prefs_data_output_title">Data output</string>
|
||||||
<string name="prefs_net_output">Network</string>
|
<string name="prefs_net_output">Network</string>
|
||||||
@@ -195,6 +192,9 @@
|
|||||||
<string name="prefs_net_perm_error">Check your network permission</string>
|
<string name="prefs_net_perm_error">Check your network permission</string>
|
||||||
|
|
||||||
<string name="prefs_other_title">Other settings</string>
|
<string name="prefs_other_title">Other settings</string>
|
||||||
|
<string name="prefs_ui_title">UI Settings</string>
|
||||||
|
<string name="prefs_ui_order_title">Page order</string>
|
||||||
|
<string name="prefs_ui_order_reset">Reset order</string>
|
||||||
<string name="prefs_other_switch_utc">Show pass time in UTC</string>
|
<string name="prefs_other_switch_utc">Show pass time in UTC</string>
|
||||||
<string name="prefs_other_switch_update">Enable automatic data update</string>
|
<string name="prefs_other_switch_update">Enable automatic data update</string>
|
||||||
<string name="prefs_other_switch_sweep">Enable radar sweep animation</string>
|
<string name="prefs_other_switch_sweep">Enable radar sweep animation</string>
|
||||||
|
|||||||
+26
@@ -30,6 +30,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -41,11 +43,13 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.rtbishop.look4sat.core.domain.model.RCSettings
|
import com.rtbishop.look4sat.core.domain.model.RCSettings
|
||||||
|
import com.rtbishop.look4sat.core.domain.source.Sources
|
||||||
import com.rtbishop.look4sat.core.domain.model.RadioControlSettings
|
import com.rtbishop.look4sat.core.domain.model.RadioControlSettings
|
||||||
import com.rtbishop.look4sat.core.presentation.CardButton
|
import com.rtbishop.look4sat.core.presentation.CardButton
|
||||||
import com.rtbishop.look4sat.core.presentation.LocalSpacing
|
import com.rtbishop.look4sat.core.presentation.LocalSpacing
|
||||||
@@ -203,6 +207,17 @@ fun DataSourcesDialog(
|
|||||||
singleLine = true,
|
singleLine = true,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = isEnabledCustomTle.value,
|
enabled = isEnabledCustomTle.value,
|
||||||
|
trailingIcon = {
|
||||||
|
IconButton(
|
||||||
|
onClick = { urlTle.value = Sources.defaultTleUrl },
|
||||||
|
enabled = isEnabledCustomTle.value
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_reset),
|
||||||
|
contentDescription = stringResource(id = R.string.prefs_data_reset_url)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(6.dp))
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
Row(
|
Row(
|
||||||
@@ -232,6 +247,17 @@ fun DataSourcesDialog(
|
|||||||
singleLine = true,
|
singleLine = true,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = isEnabledCustomTransceivers.value,
|
enabled = isEnabledCustomTransceivers.value,
|
||||||
|
trailingIcon = {
|
||||||
|
IconButton(
|
||||||
|
onClick = { urlTransceivers.value = Sources.defaultTransceiversUrl },
|
||||||
|
enabled = isEnabledCustomTransceivers.value
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_reset),
|
||||||
|
contentDescription = stringResource(id = R.string.prefs_data_reset_url)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
}
|
}
|
||||||
|
|||||||
+171
-1
@@ -22,16 +22,26 @@ import android.os.Build
|
|||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.gestures.detectDragGestures
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
@@ -41,15 +51,28 @@ import androidx.compose.material3.LinearProgressIndicator
|
|||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import kotlin.math.roundToInt
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.zIndex
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -300,6 +323,15 @@ private fun SettingsScreen(uiState: SettingsState, onAction: (SettingsAction) ->
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
item { OtherCard(uiState.otherSettings, onAction) }
|
item { OtherCard(uiState.otherSettings, onAction) }
|
||||||
|
item {
|
||||||
|
UiSettingsCard(
|
||||||
|
hiddenScreens = uiState.otherSettings.hiddenScreens,
|
||||||
|
screenOrder = uiState.otherSettings.screenOrder,
|
||||||
|
onToggle = { name -> onAction(SettingsAction.ToggleScreen(name)) },
|
||||||
|
onReorder = { order -> onAction(SettingsAction.ReorderScreens(order)) },
|
||||||
|
onResetOrder = { onAction(SettingsAction.ResetScreenOrder) }
|
||||||
|
)
|
||||||
|
}
|
||||||
item { CardCredits() }
|
item { CardCredits() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -516,8 +548,146 @@ private fun OtherCard(settings: OtherSettings, onAction: (SettingsAction) -> Uni
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI 设置卡片: 控制底部菜单栏各页面显示/隐藏。
|
||||||
|
* 顺序固定(卫星/过境/雷达/匹配/漫游/地图/设置), 关闭后其余自动靠拢;
|
||||||
|
* "设置"页固定显示(防止失去设置入口)。
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
private fun SwitchRow(labelResId: Int, checked: Boolean, onCheckedChange: (Boolean) -> Unit) {
|
private fun UiSettingsCard(
|
||||||
|
hiddenScreens: List<String>,
|
||||||
|
screenOrder: List<String>,
|
||||||
|
onToggle: (String) -> Unit,
|
||||||
|
onReorder: (List<String>) -> Unit,
|
||||||
|
onResetOrder: () -> Unit
|
||||||
|
) {
|
||||||
|
val screens = listOf(
|
||||||
|
R.string.nav_sat to "Satellites",
|
||||||
|
R.string.nav_pass to "Passes",
|
||||||
|
R.string.nav_radar to "Radar",
|
||||||
|
R.string.nav_mutual to "Mutual",
|
||||||
|
R.string.nav_roaming to "Roaming",
|
||||||
|
R.string.nav_map to "Map",
|
||||||
|
R.string.nav_prefs to "Settings"
|
||||||
|
) // name 必须与 Screen.screenId 一致 (R8 安全)
|
||||||
|
ElevatedCard(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.prefs_ui_title),
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
screens.forEach { (labelRes, name) ->
|
||||||
|
if (name == "Settings") {
|
||||||
|
// 设置页固定显示, 开关禁用
|
||||||
|
SwitchRow(labelRes, true, null)
|
||||||
|
} else {
|
||||||
|
SwitchRow(labelRes, name !in hiddenScreens) {
|
||||||
|
onToggle(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.prefs_ui_order_title),
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
DragOrderList(screens = screens, screenOrder = screenOrder, onReorder = onReorder)
|
||||||
|
TextButton(onClick = onResetOrder) {
|
||||||
|
Text(text = stringResource(id = R.string.prefs_ui_order_reset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 页面顺序拖拽列表: 每行右侧抓手, 按住上下拖动实时换位, 松手持久化 */
|
||||||
|
@Composable
|
||||||
|
private fun DragOrderList(
|
||||||
|
screens: List<Pair<Int, String>>,
|
||||||
|
screenOrder: List<String>,
|
||||||
|
onReorder: (List<String>) -> Unit
|
||||||
|
) {
|
||||||
|
val itemHeight = 48.dp
|
||||||
|
val itemHeightPx = with(LocalDensity.current) { itemHeight.toPx() }
|
||||||
|
// 按 screenOrder 排序(空 = 默认顺序); remember 以 screens 参数为 key, 持久化后重组
|
||||||
|
val ordered = remember(screens, screenOrder) {
|
||||||
|
screens.sortedBy { (_, name) ->
|
||||||
|
screenOrder.indexOf(name).let { if (it == -1) Int.MAX_VALUE else it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
val items = remember(ordered) { mutableStateListOf<Pair<Int, String>>().apply { addAll(ordered) } }
|
||||||
|
var draggingIndex by remember { mutableIntStateOf(-1) }
|
||||||
|
var dragOffset by remember { mutableStateOf(0f) }
|
||||||
|
|
||||||
|
// 固定高度: 嵌套在 LazyVerticalGrid 内必须给有界高度, 否则无限高度约束崩溃
|
||||||
|
LazyColumn(
|
||||||
|
state = listState,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(itemHeight * items.size)
|
||||||
|
) {
|
||||||
|
itemsIndexed(items, key = { _, s -> s.second }) { index, screen ->
|
||||||
|
val isDragging = draggingIndex == index
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(itemHeight)
|
||||||
|
.zIndex(if (isDragging) 1f else 0f)
|
||||||
|
.graphicsLayer { translationY = if (isDragging) dragOffset else 0f }
|
||||||
|
.animateItem()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = screen.first),
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.padding(20.dp)
|
||||||
|
.pointerInput(screen.second) {
|
||||||
|
detectDragGestures(
|
||||||
|
onDragStart = { draggingIndex = items.indexOf(screen) },
|
||||||
|
onDrag = { change, dragAmount ->
|
||||||
|
change.consume()
|
||||||
|
dragOffset += dragAmount.y
|
||||||
|
val currentIndex = draggingIndex
|
||||||
|
val target = (currentIndex + (dragOffset / itemHeightPx).roundToInt())
|
||||||
|
.coerceIn(0, items.size - 1)
|
||||||
|
if (target != currentIndex) {
|
||||||
|
items.add(target, items.removeAt(currentIndex))
|
||||||
|
dragOffset += (currentIndex - target) * itemHeightPx
|
||||||
|
draggingIndex = target
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDragEnd = {
|
||||||
|
onReorder(items.map { it.second })
|
||||||
|
draggingIndex = -1
|
||||||
|
dragOffset = 0f
|
||||||
|
},
|
||||||
|
onDragCancel = {
|
||||||
|
draggingIndex = -1
|
||||||
|
dragOffset = 0f
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
// 小圆点手柄(触控区 padding 20dp 居中 -> 8dp 圆点)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(MaterialTheme.colorScheme.onSurfaceVariant, CircleShape)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SwitchRow(labelResId: Int, checked: Boolean, onCheckedChange: ((Boolean) -> Unit)?) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ sealed interface SettingsAction {
|
|||||||
data class ToggleSensor(val value: Boolean) : SettingsAction
|
data class ToggleSensor(val value: Boolean) : SettingsAction
|
||||||
data class ToggleLightTheme(val value: Boolean) : SettingsAction
|
data class ToggleLightTheme(val value: Boolean) : SettingsAction
|
||||||
data class ToggleNightMode(val value: Boolean) : SettingsAction
|
data class ToggleNightMode(val value: Boolean) : SettingsAction
|
||||||
|
// UI 设置: 切换底部导航栏页面的隐藏状态(Screen simpleName)
|
||||||
|
data class ToggleScreen(val screenName: String) : SettingsAction
|
||||||
|
// UI 设置: 更新页面顺序
|
||||||
|
data class ReorderScreens(val order: List<String>) : SettingsAction
|
||||||
|
// UI 设置: 重置为默认顺序
|
||||||
|
data object ResetScreenOrder : SettingsAction
|
||||||
|
|
||||||
// Remote control
|
// Remote control
|
||||||
data class UpdateRC(val settings: RCSettings) : SettingsAction
|
data class UpdateRC(val settings: RCSettings) : SettingsAction
|
||||||
|
|||||||
+7
@@ -121,6 +121,13 @@ class SettingsViewModel(
|
|||||||
is SettingsAction.ToggleSensor -> settingsRepo.updateOtherSettings { it.copy(stateOfSensors = action.value) }
|
is SettingsAction.ToggleSensor -> settingsRepo.updateOtherSettings { it.copy(stateOfSensors = action.value) }
|
||||||
is SettingsAction.ToggleLightTheme -> settingsRepo.updateOtherSettings { it.copy(stateOfLightTheme = action.value) }
|
is SettingsAction.ToggleLightTheme -> settingsRepo.updateOtherSettings { it.copy(stateOfLightTheme = action.value) }
|
||||||
is SettingsAction.ToggleNightMode -> settingsRepo.updateOtherSettings { it.copy(stateOfNightMode = action.value) }
|
is SettingsAction.ToggleNightMode -> settingsRepo.updateOtherSettings { it.copy(stateOfNightMode = action.value) }
|
||||||
|
is SettingsAction.ToggleScreen -> settingsRepo.updateOtherSettings { current ->
|
||||||
|
val hidden = current.hiddenScreens.toMutableList()
|
||||||
|
if (action.screenName in hidden) hidden.remove(action.screenName) else hidden.add(action.screenName)
|
||||||
|
current.copy(hiddenScreens = hidden)
|
||||||
|
}
|
||||||
|
is SettingsAction.ReorderScreens -> settingsRepo.updateOtherSettings { it.copy(screenOrder = action.order) }
|
||||||
|
SettingsAction.ResetScreenOrder -> settingsRepo.updateOtherSettings { it.copy(screenOrder = emptyList()) }
|
||||||
// Remote control & data sources
|
// Remote control & data sources
|
||||||
is SettingsAction.UpdateRC -> settingsRepo.updateRCSettings(action.settings)
|
is SettingsAction.UpdateRC -> settingsRepo.updateRCSettings(action.settings)
|
||||||
is SettingsAction.UpdateRadioControl -> settingsRepo.updateRadioControlSettings(action.settings)
|
is SettingsAction.UpdateRadioControl -> settingsRepo.updateRadioControlSettings(action.settings)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[versions]
|
[versions]
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
appVersionCode = "447"
|
appVersionCode = "449"
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
appVersionName = "4.4.7"
|
appVersionName = "4.4.9"
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
compileSdk = "37"
|
compileSdk = "37"
|
||||||
#noinspection UnusedVersionCatalogEntry
|
#noinspection UnusedVersionCatalogEntry
|
||||||
|
|||||||
Reference in new issue
Block a user