From d4842d4b292fb7932e9da51c661b614141f847b3 Mon Sep 17 00:00:00 2001 From: grigo Date: Fri, 19 Jun 2026 12:50:42 +0300 Subject: [PATCH] offline track --- .../loratester/CommandPoller.java | 9 +++ .../loratester/track/TrackRecorder.java | 74 +++++++++++++++---- .../loratester/ui/MapFragment.java | 1 + 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/grigowashere/loratester/CommandPoller.java b/app/src/main/java/com/grigowashere/loratester/CommandPoller.java index 15a174a..b458510 100644 --- a/app/src/main/java/com/grigowashere/loratester/CommandPoller.java +++ b/app/src/main/java/com/grigowashere/loratester/CommandPoller.java @@ -67,6 +67,15 @@ public class CommandPoller { } } + @Override + public void onSyncComplete(long trackId, int pointCount) { + if (pendingAckSessionId > 0 && trackId > 0) { + long sid = pendingAckSessionId; + pendingAckSessionId = -1; + executor.execute(() -> ackSession(sid, trackId)); + } + } + @Override public void onError(String message) { Log.w(TAG, "track: " + message); diff --git a/app/src/main/java/com/grigowashere/loratester/track/TrackRecorder.java b/app/src/main/java/com/grigowashere/loratester/track/TrackRecorder.java index 37b8446..0540912 100644 --- a/app/src/main/java/com/grigowashere/loratester/track/TrackRecorder.java +++ b/app/src/main/java/com/grigowashere/loratester/track/TrackRecorder.java @@ -136,34 +136,77 @@ public class TrackRecorder { } public void start() { - if (recording || pendingSync) { + if (recording) { return; } executor.execute(() -> { + if (pendingSync) { + syncWhenOnline(); + if (pendingSync) { + notifyError(appContext.getString(R.string.track_sync_pending)); + return; + } + } if (!networkMonitor.isOnline()) { startLocalRecording(); return; } try { - long id = serverApi.startTrack(deviceId); - synchronized (buffer) { - buffer.clear(); - } - totalPoints = 0; - trackId = id; - localStartedAt = System.currentTimeMillis() / 1000.0; - recording = true; - pendingSync = false; - persistState(false); - startTimers(); - notifyState(); + startOnlineRecording(); } catch (Exception e) { - Log.e(TAG, "start track failed", e); - notifyError(e.getMessage()); + Log.w(TAG, "start track on server failed", e); + if (isReachabilityError(e)) { + startLocalRecording(); + } else { + notifyError(e.getMessage() != null ? e.getMessage() : "start failed"); + } } }); } + /** Retry upload of a track stopped offline. */ + public void retryPendingSync() { + executor.execute(this::syncWhenOnline); + } + + private void startOnlineRecording() throws Exception { + long id = serverApi.startTrack(deviceId); + synchronized (buffer) { + buffer.clear(); + } + totalPoints = 0; + trackId = id; + localStartedAt = System.currentTimeMillis() / 1000.0; + recording = true; + pendingSync = false; + persistState(false); + startTimers(); + notifyState(); + } + + private static boolean isReachabilityError(Throwable e) { + while (e != null) { + if (e instanceof java.net.UnknownHostException + || e instanceof java.net.ConnectException + || e instanceof java.net.SocketTimeoutException) { + return true; + } + String msg = e.getMessage(); + if (msg != null) { + String lower = msg.toLowerCase(); + if (lower.contains("unable to resolve host") + || lower.contains("failed to connect") + || lower.contains("timeout") + || lower.contains("econnrefused") + || lower.contains("network is unreachable")) { + return true; + } + } + e = e.getCause(); + } + return false; + } + private void startLocalRecording() { synchronized (buffer) { buffer.clear(); @@ -265,6 +308,7 @@ public class TrackRecorder { trackId = id; flushBuffer(); persistState(false); + notifyState(); Log.i(TAG, "promoted local track to server id " + id); } catch (Exception e) { Log.w(TAG, "promote local track failed", e); diff --git a/app/src/main/java/com/grigowashere/loratester/ui/MapFragment.java b/app/src/main/java/com/grigowashere/loratester/ui/MapFragment.java index cd80ba9..d084d1a 100644 --- a/app/src/main/java/com/grigowashere/loratester/ui/MapFragment.java +++ b/app/src/main/java/com/grigowashere/loratester/ui/MapFragment.java @@ -681,6 +681,7 @@ public class MapFragment extends Fragment { trackRecorder.stop(); } else { if (trackRecorder.hasPendingSync()) { + trackRecorder.retryPendingSync(); Toast.makeText(requireContext(), R.string.track_sync_pending, Toast.LENGTH_SHORT).show(); return; }