fix: 次駅表示をDirection非依存に修正(JS/Kotlin両方)

- JS側: currentPosition[0]ではなくstopStationIDList上のmax(idx0,idx1)で進行方向の駅を判定
- Kotlin側: pollRunnable復活、allStationsのダイヤ順でmaxOf(idx0,idx1)で向かう駅を判定
- Kotlin at-station: 停車中は現在駅を表示(JS側と統一)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
harukin-expo-dev-env
2026-03-24 01:39:31 +00:00
parent 3f6b3cfcfb
commit dc3d250466
2 changed files with 80 additions and 34 deletions

View File

@@ -217,12 +217,17 @@ export const FixedTrain: FC<props> = ({ trainID }) => {
const delayTime = train?.delay == "入線" ? 0 : train?.delay;
let additionalSkipCount = 0;
// 2駅間走行中の場合: currentPosition[0]が向かう駅IDなので
// trainDataWidhThroughでその駅以降の最初の停車駅を探す
// 2駅間走行中の場合: ダイヤ順で後ろのインデックスが進行方向の駅
// Direction に関係なく、travel-order で大きい方が「向かう駅」
let searchStart: number;
if (currentPosition.length === 2) {
const toIdx = stopStationIDList.findIndex(d => d.includes(currentPosition[0]));
searchStart = toIdx >= 0 ? toIdx : searchCountLast;
const idx0 = stopStationIDList.findIndex(d => d.includes(currentPosition[0]));
const idx1 = stopStationIDList.findIndex(d => d.includes(currentPosition[1]));
const aheadIdx = Math.max(
idx0 >= 0 ? idx0 : -1,
idx1 >= 0 ? idx1 : -1
);
searchStart = aheadIdx >= 0 ? aheadIdx : searchCountLast;
} else {
searchStart = searchCountFirst;
}

View File

@@ -62,9 +62,12 @@ class LiveActivityForegroundService : Service() {
private val handler = Handler(Looper.getMainLooper())
private val pollRunnable = object : Runnable {
override fun run() {
// ポーリングは無効化。JS側が10秒ごとに更新を送信する。
// ネイティブ側のポーリングはDirectionを考慮できず
// JS側の更新と競合して不正な表示になるため。
if (!isRunning) return
when (currentMode) {
"train" -> executor.execute { pollTrainPosition() }
"station" -> executor.execute { pollStationTrains() }
}
handler.postDelayed(this, POLL_INTERVAL_MS)
}
}
@@ -233,37 +236,75 @@ class LiveActivityForegroundService : Service() {
val delay = parseDelay(train)
val stationStops = parseLinesJson(stationStopsJson)
val posStations = pos.split("")
val currentStation = posStations.firstOrNull()
?.replace("(下り)", "")?.replace("(上り)", "")
?.replace("(下り)", "")?.replace("(上り)", "")
?.trim() ?: ""
val nextStation = if (posStations.size > 1) {
posStations[1]
.replace("(下り)", "").replace("(上り)", "")
.replace("(下り)", "").replace("(上り)", "")
.trim()
} else ""
// allStationsから現在地インデックスを計算
val allStations = parseAllStationsJson(allStationsJson)
val currentIndex = if (allStations.isNotEmpty()) {
allStations.indexOfFirst { it.first == currentStation }.let { if (it < 0) 0 else it }
} else {
if (stationStops.isNotEmpty() && currentStation.isNotEmpty()) {
stationStops.indexOf(currentStation).let { if (it < 0) 0 else it }
} else 0
}
val progressTotal = if (allStations.isNotEmpty()) allStations.size else stationStops.size
val delayText = if (delay > 0) "${delay}分遅れ" else "定刻"
// Posの方向表記を除去
val cleanedPos = pos
.replace("(下り)", "").replace("(上り)", "")
.replace("(下り)", "").replace("(上り)", "")
.replace("(徳島線)", "").replace("(高徳線)", "")
.replace("(坂出方)", "").replace("(児島方)", "")
.trim()
val posStations = cleanedPos.split("").map { it.trim() }
val isAtStation = posStations.size <= 1
// allStationsのダイヤ順で現在地と次の停車駅を決定
var nextStopName = ""
var currentIndex = 0
if (allStations.isNotEmpty()) {
if (isAtStation) {
// 駅に停車中: 現在駅を表示JS側と同じ "ただいま [駅名]"
val stationName = posStations.firstOrNull() ?: ""
val idx = allStations.indexOfFirst { it.first == stationName }
currentIndex = if (idx >= 0) idx else 0
nextStopName = stationName
} else {
// 2駅間走行中: 両方の駅をallStationsで見つけ、ダイヤ上で後ろにある方が向かう駅
val idx0 = allStations.indexOfFirst { it.first == posStations[0] }
val idx1 = allStations.indexOfFirst { it.first == posStations.getOrElse(1) { "" } }
val towardIdx: Int
if (idx0 >= 0 && idx1 >= 0) {
// ダイヤ順で後ろの方が「向かっている駅」
towardIdx = maxOf(idx0, idx1)
currentIndex = minOf(idx0, idx1)
} else if (idx0 >= 0) {
towardIdx = idx0
currentIndex = idx0
} else if (idx1 >= 0) {
towardIdx = idx1
currentIndex = idx1
} else {
towardIdx = 0
currentIndex = 0
}
// 向かう駅以降で最初の停車駅を探す
for (i in towardIdx until allStations.size) {
if (allStations[i].second) { // isStop == true
nextStopName = allStations[i].first
break
}
}
if (nextStopName.isEmpty() && posStations.size > 1) {
nextStopName = posStations[1]
}
}
} else {
// allStationsが空の場合のフォールバック
val stationName = posStations.firstOrNull() ?: ""
currentIndex = if (stationStops.isNotEmpty()) {
stationStops.indexOf(stationName).let { if (it < 0) 0 else it }
} else 0
nextStopName = if (posStations.size > 1) posStations[1] else stationName
}
val progressTotal = if (allStations.isNotEmpty()) allStations.size else stationStops.size
val delayText = if (delay > 0) "${delay}分遅れ" else "定刻"
val positionLabel = if (isAtStation) "ただいま" else "次は"
val displayStation = if (isAtStation) currentStation else nextStation
val bodyLines = mutableListOf<String>()
bodyLines.add("$positionLabel $displayStation")
if (!isAtStation && currentStation.isNotEmpty() && nextStation.isNotEmpty()) {
bodyLines.add("${currentStation}${nextStation}間走行中")
bodyLines.add("$positionLabel $nextStopName")
if (!isAtStation) {
bodyLines.add("${cleanedPos}間走行中")
}
bodyLines.add(delayText)
val body = bodyLines.joinToString("\n")
@@ -279,7 +320,7 @@ class LiveActivityForegroundService : Service() {
val nm = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nm.notify(NOTIFICATION_ID, notification)
}
Log.d(TAG, "Poll updated: pos=$pos delay=$delay")
Log.d(TAG, "Poll updated: pos=$pos nextStop=$nextStopName delay=$delay")
} catch (e: Exception) {
Log.e(TAG, "Poll error", e)
}