fix: optimize the logic for saving scroll position
This commit is contained in:
@@ -12,8 +12,8 @@ android {
|
|||||||
applicationId = "org.noxylva.lbjconsole"
|
applicationId = "org.noxylva.lbjconsole"
|
||||||
minSdk = 29
|
minSdk = 29
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 7
|
versionCode = 8
|
||||||
versionName = "0.0.7"
|
versionName = "0.0.8"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.osmdroid.config.Configuration
|
import org.osmdroid.config.Configuration
|
||||||
import org.noxylva.lbjconsole.model.TrainRecord
|
import org.noxylva.lbjconsole.model.TrainRecord
|
||||||
@@ -418,9 +419,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
deviceName = settingsDeviceName,
|
deviceName = settingsDeviceName,
|
||||||
onDeviceNameChange = { newName -> settingsDeviceName = newName },
|
onDeviceNameChange = { newName -> settingsDeviceName = newName },
|
||||||
onApplySettings = {
|
onApplySettings = {
|
||||||
saveSettings()
|
if (targetDeviceName != settingsDeviceName) {
|
||||||
targetDeviceName = settingsDeviceName
|
targetDeviceName = settingsDeviceName
|
||||||
Log.d(TAG, "Applied settings deviceName=${settingsDeviceName}")
|
Log.d(TAG, "Applied settings deviceName=${settingsDeviceName}")
|
||||||
|
saveSettings()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
appVersion = getAppVersion(),
|
appVersion = getAppVersion(),
|
||||||
locoInfoUtil = locoInfoUtil,
|
locoInfoUtil = locoInfoUtil,
|
||||||
@@ -795,28 +798,30 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
|
|
||||||
private fun saveSettings() {
|
private fun saveSettings() {
|
||||||
val editor = settingsPrefs.edit()
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
.putString("device_name", settingsDeviceName)
|
val editor = settingsPrefs.edit()
|
||||||
.putInt("current_tab", currentTab)
|
.putString("device_name", settingsDeviceName)
|
||||||
.putBoolean("history_edit_mode", historyEditMode)
|
.putInt("current_tab", currentTab)
|
||||||
.putString("history_selected_records", historySelectedRecords.joinToString(","))
|
.putBoolean("history_edit_mode", historyEditMode)
|
||||||
.putString("history_expanded_states", historyExpandedStates.map { "${it.key}:${it.value}" }.joinToString(";"))
|
.putString("history_selected_records", historySelectedRecords.joinToString(","))
|
||||||
.putInt("history_scroll_position", historyScrollPosition)
|
.putString("history_expanded_states", historyExpandedStates.map { "${it.key}:${it.value}" }.joinToString(";"))
|
||||||
.putInt("history_scroll_offset", historyScrollOffset)
|
.putInt("history_scroll_position", historyScrollPosition)
|
||||||
.putInt("settings_scroll_position", settingsScrollPosition)
|
.putInt("history_scroll_offset", historyScrollOffset)
|
||||||
.putFloat("map_zoom_level", mapZoomLevel.toFloat())
|
.putInt("settings_scroll_position", settingsScrollPosition)
|
||||||
.putBoolean("map_railway_visible", mapRailwayLayerVisible)
|
.putFloat("map_zoom_level", mapZoomLevel.toFloat())
|
||||||
.putString("specified_device_address", specifiedDeviceAddress)
|
.putBoolean("map_railway_visible", mapRailwayLayerVisible)
|
||||||
.putString("search_order_list", searchOrderList.joinToString(","))
|
.putString("specified_device_address", specifiedDeviceAddress)
|
||||||
.putBoolean("auto_connect_enabled", autoConnectEnabled)
|
.putString("search_order_list", searchOrderList.joinToString(","))
|
||||||
|
.putBoolean("auto_connect_enabled", autoConnectEnabled)
|
||||||
|
|
||||||
mapCenterPosition?.let { (lat, lon) ->
|
mapCenterPosition?.let { (lat, lon) ->
|
||||||
editor.putFloat("map_center_lat", lat.toFloat())
|
editor.putFloat("map_center_lat", lat.toFloat())
|
||||||
editor.putFloat("map_center_lon", lon.toFloat())
|
editor.putFloat("map_center_lon", lon.toFloat())
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.apply()
|
||||||
|
Log.d(TAG, "Saved settings deviceName=${settingsDeviceName} tab=${currentTab} mapCenter=${mapCenterPosition} zoom=${mapZoomLevel}")
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.apply()
|
|
||||||
Log.d(TAG, "Saved settings deviceName=${settingsDeviceName} tab=${currentTab} mapCenter=${mapCenterPosition} zoom=${mapZoomLevel}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ data class MergeSettings(
|
|||||||
)
|
)
|
||||||
|
|
||||||
enum class GroupBy(val displayName: String) {
|
enum class GroupBy(val displayName: String) {
|
||||||
TRAIN_AND_LOCO("车次号+机车号"),
|
TRAIN_ONLY("车次号"),
|
||||||
TRAIN_ONLY("仅车次号"),
|
LOCO_ONLY("机车号"),
|
||||||
LOCO_ONLY("仅机车号")
|
TRAIN_OR_LOCO("车次号或机车号"),
|
||||||
|
TRAIN_AND_LOCO("车次号与机车号")
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class TimeWindow(val displayName: String, val seconds: Long?) {
|
enum class TimeWindow(val displayName: String, val seconds: Long?) {
|
||||||
@@ -23,14 +24,6 @@ enum class TimeWindow(val displayName: String, val seconds: Long?) {
|
|||||||
|
|
||||||
fun generateGroupKey(record: TrainRecord, groupBy: GroupBy): String? {
|
fun generateGroupKey(record: TrainRecord, groupBy: GroupBy): String? {
|
||||||
return when (groupBy) {
|
return when (groupBy) {
|
||||||
GroupBy.TRAIN_AND_LOCO -> {
|
|
||||||
val train = record.train.trim()
|
|
||||||
val loco = record.loco.trim()
|
|
||||||
if (train.isNotEmpty() && train != "<NUL>" &&
|
|
||||||
loco.isNotEmpty() && loco != "<NUL>") {
|
|
||||||
"${train}_${loco}"
|
|
||||||
} else null
|
|
||||||
}
|
|
||||||
GroupBy.TRAIN_ONLY -> {
|
GroupBy.TRAIN_ONLY -> {
|
||||||
val train = record.train.trim()
|
val train = record.train.trim()
|
||||||
if (train.isNotEmpty() && train != "<NUL>") train else null
|
if (train.isNotEmpty() && train != "<NUL>") train else null
|
||||||
@@ -39,5 +32,22 @@ fun generateGroupKey(record: TrainRecord, groupBy: GroupBy): String? {
|
|||||||
val loco = record.loco.trim()
|
val loco = record.loco.trim()
|
||||||
if (loco.isNotEmpty() && loco != "<NUL>") loco else null
|
if (loco.isNotEmpty() && loco != "<NUL>") loco else null
|
||||||
}
|
}
|
||||||
|
GroupBy.TRAIN_OR_LOCO -> {
|
||||||
|
val train = record.train.trim()
|
||||||
|
val loco = record.loco.trim()
|
||||||
|
when {
|
||||||
|
train.isNotEmpty() && train != "<NUL>" -> train
|
||||||
|
loco.isNotEmpty() && loco != "<NUL>" -> loco
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GroupBy.TRAIN_AND_LOCO -> {
|
||||||
|
val train = record.train.trim()
|
||||||
|
val loco = record.loco.trim()
|
||||||
|
if (train.isNotEmpty() && train != "<NUL>" &&
|
||||||
|
loco.isNotEmpty() && loco != "<NUL>") {
|
||||||
|
"${train}_${loco}"
|
||||||
|
} else null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,26 +234,79 @@ class TrainRecordManager(private val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun processRecordsForMerging(records: List<TrainRecord>, settings: MergeSettings): List<MergedTrainRecord> {
|
private fun processRecordsForMerging(records: List<TrainRecord>, settings: MergeSettings): List<MergedTrainRecord> {
|
||||||
val groupedRecords = mutableMapOf<String, MutableList<TrainRecord>>()
|
|
||||||
val currentTime = Date()
|
val currentTime = Date()
|
||||||
|
val validRecords = records.filter { record ->
|
||||||
|
settings.timeWindow.seconds?.let { windowSeconds ->
|
||||||
|
(currentTime.time - record.timestamp.time) / 1000 <= windowSeconds
|
||||||
|
} ?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (settings.groupBy) {
|
||||||
|
GroupBy.TRAIN_OR_LOCO -> processTrainOrLocoMerging(validRecords)
|
||||||
|
else -> {
|
||||||
|
val groupedRecords = mutableMapOf<String, MutableList<TrainRecord>>()
|
||||||
|
validRecords.forEach { record ->
|
||||||
|
val groupKey = generateGroupKey(record, settings.groupBy)
|
||||||
|
if (groupKey != null) {
|
||||||
|
groupedRecords.getOrPut(groupKey) { mutableListOf() }.add(record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
groupedRecords.mapNotNull { (groupKey, groupRecords) ->
|
||||||
|
if (groupRecords.size >= 2) {
|
||||||
|
val sortedRecords = groupRecords.sortedBy { it.timestamp }
|
||||||
|
val latestRecord = sortedRecords.maxByOrNull { it.timestamp }!!
|
||||||
|
MergedTrainRecord(
|
||||||
|
groupKey = groupKey,
|
||||||
|
records = sortedRecords,
|
||||||
|
latestRecord = latestRecord
|
||||||
|
)
|
||||||
|
} else null
|
||||||
|
}.sortedByDescending { it.latestRecord.timestamp }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processTrainOrLocoMerging(records: List<TrainRecord>): List<MergedTrainRecord> {
|
||||||
|
val groups = mutableListOf<MutableList<TrainRecord>>()
|
||||||
|
|
||||||
records.forEach { record ->
|
records.forEach { record ->
|
||||||
val groupKey = generateGroupKey(record, settings.groupBy)
|
val train = record.train.trim()
|
||||||
if (groupKey != null) {
|
val loco = record.loco.trim()
|
||||||
val withinTimeWindow = settings.timeWindow.seconds?.let { windowSeconds ->
|
|
||||||
(currentTime.time - record.timestamp.time) / 1000 <= windowSeconds
|
|
||||||
} ?: true
|
|
||||||
|
|
||||||
if (withinTimeWindow) {
|
if ((train.isEmpty() || train == "<NUL>") && (loco.isEmpty() || loco == "<NUL>")) {
|
||||||
groupedRecords.getOrPut(groupKey) { mutableListOf() }.add(record)
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
|
var foundGroup: MutableList<TrainRecord>? = null
|
||||||
|
|
||||||
|
for (group in groups) {
|
||||||
|
val shouldMerge = group.any { existingRecord ->
|
||||||
|
val existingTrain = existingRecord.train.trim()
|
||||||
|
val existingLoco = existingRecord.loco.trim()
|
||||||
|
|
||||||
|
(train.isNotEmpty() && train != "<NUL>" && train == existingTrain) ||
|
||||||
|
(loco.isNotEmpty() && loco != "<NUL>" && loco == existingLoco)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldMerge) {
|
||||||
|
foundGroup = group
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundGroup != null) {
|
||||||
|
foundGroup.add(record)
|
||||||
|
} else {
|
||||||
|
groups.add(mutableListOf(record))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupedRecords.mapNotNull { (groupKey, groupRecords) ->
|
return groups.mapNotNull { groupRecords ->
|
||||||
if (groupRecords.size >= 2) {
|
if (groupRecords.size >= 2) {
|
||||||
val sortedRecords = groupRecords.sortedBy { it.timestamp }
|
val sortedRecords = groupRecords.sortedBy { it.timestamp }
|
||||||
val latestRecord = sortedRecords.maxByOrNull { it.timestamp }!!
|
val latestRecord = sortedRecords.maxByOrNull { it.timestamp }!!
|
||||||
|
val groupKey = "${latestRecord.train}_OR_${latestRecord.loco}"
|
||||||
MergedTrainRecord(
|
MergedTrainRecord(
|
||||||
groupKey = groupKey,
|
groupKey = groupKey,
|
||||||
records = sortedRecords,
|
records = sortedRecords,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.noxylva.lbjconsole.ui.screens
|
package org.noxylva.lbjconsole.ui.screens
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
@@ -862,6 +863,7 @@ fun HistoryScreen(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
val refreshKey = latestRecord?.timestamp?.time ?: 0
|
val refreshKey = latestRecord?.timestamp?.time ?: 0
|
||||||
|
var wasAtTopBeforeUpdate by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
var isInEditMode by remember(editMode) { mutableStateOf(editMode) }
|
var isInEditMode by remember(editMode) { mutableStateOf(editMode) }
|
||||||
val selectedRecordsList = remember(selectedRecords) {
|
val selectedRecordsList = remember(selectedRecords) {
|
||||||
@@ -942,6 +944,22 @@ fun HistoryScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(listState.firstVisibleItemIndex, listState.firstVisibleItemScrollOffset) {
|
||||||
|
if (!isInEditMode && filteredRecords.isNotEmpty()) {
|
||||||
|
wasAtTopBeforeUpdate = listState.firstVisibleItemIndex == 0 && listState.firstVisibleItemScrollOffset <= 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(refreshKey) {
|
||||||
|
if (refreshKey > 0 && !isInEditMode && filteredRecords.isNotEmpty() && wasAtTopBeforeUpdate) {
|
||||||
|
try {
|
||||||
|
listState.animateScrollToItem(0, 0)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
listState.scrollToItem(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import androidx.compose.ui.platform.LocalUriHandler
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import org.noxylva.lbjconsole.model.MergeSettings
|
import org.noxylva.lbjconsole.model.MergeSettings
|
||||||
import org.noxylva.lbjconsole.model.GroupBy
|
import org.noxylva.lbjconsole.model.GroupBy
|
||||||
import org.noxylva.lbjconsole.model.TimeWindow
|
import org.noxylva.lbjconsole.model.TimeWindow
|
||||||
@@ -46,17 +47,16 @@ fun SettingsScreen(
|
|||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
LaunchedEffect(scrollPosition) {
|
LaunchedEffect(scrollPosition) {
|
||||||
scrollState.scrollTo(scrollPosition)
|
if (scrollState.value != scrollPosition) {
|
||||||
|
scrollState.scrollTo(scrollPosition)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(scrollState.value) {
|
LaunchedEffect(scrollState.value) {
|
||||||
|
delay(50)
|
||||||
onScrollPositionChange(scrollState.value)
|
onScrollPositionChange(scrollState.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(deviceName) {
|
|
||||||
onApplySettings()
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -198,12 +198,13 @@ fun SettingsScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var backgroundServiceEnabled by remember {
|
val notificationService = remember(context) { NotificationService(context) }
|
||||||
|
|
||||||
|
var backgroundServiceEnabled by remember(context) {
|
||||||
mutableStateOf(SettingsActivity.isBackgroundServiceEnabled(context))
|
mutableStateOf(SettingsActivity.isBackgroundServiceEnabled(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
val notificationService = remember { NotificationService(context) }
|
var notificationEnabled by remember(context, notificationService) {
|
||||||
var notificationEnabled by remember {
|
|
||||||
mutableStateOf(notificationService.isNotificationEnabled())
|
mutableStateOf(notificationService.isNotificationEnabled())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user