Skip to content

Commit 0b143cf

Browse files
committed
External GPS: auto-(re)connect on boot + after a drop
ExternalGpsRepository watches the underlying GATT connection state and re-issues a connect whenever it goes DISCONNECTED with a paired device, unless the rider explicitly tapped Disconnect this session. Doubles as "connect on boot" because the initial state is DISCONNECTED — a paired RaceBox gets a single attempt within ~1.5 s of app start. Backoff: 1.5 / 5 / 10 / 30 s, then 30 s forever. Keeps the radio quiet when the box is out of range while still recovering quickly from a one-off drop. Explicit user actions (Disconnect / Reconnect / Pair) set the veto flag accordingly so the loop respects intent.
1 parent 1a0faec commit 0b143cf

1 file changed

Lines changed: 58 additions & 0 deletions

File tree

app/src/main/java/com/eried/eucplanet/data/repository/ExternalGpsRepository.kt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ class ExternalGpsRepository @Inject constructor(
6666
@Volatile private var mapY: String = "Y"
6767
@Volatile private var mapZ: String = "Z"
6868

69+
/**
70+
* Tracks whether the user has explicitly disconnected this session. While
71+
* set, the auto-reconnect loop stops attempting to re-establish a
72+
* connection so the rider's deliberate action sticks until they actively
73+
* reconnect (from the Integration settings screen). Resets to false on
74+
* every app launch so a paired device naturally reconnects on boot.
75+
*/
76+
@Volatile private var explicitlyDisconnected: Boolean = false
77+
6978
init {
7079
// Bridge the connection manager's sample stream into a StateFlow so the
7180
// dashboard can render the latest fix without subscribing to a hot
@@ -102,6 +111,47 @@ class ExternalGpsRepository @Inject constructor(
102111
}
103112
}
104113

114+
// Auto-reconnect loop. Watches the underlying GATT connection state
115+
// and re-issues a connect whenever we transition to DISCONNECTED with
116+
// a paired device, unless the rider explicitly disconnected this
117+
// session. Doubles as the "connect on boot" path: on app start the
118+
// state begins as DISCONNECTED, so a paired device gets a single
119+
// attempt with no backoff almost immediately.
120+
scope.launch {
121+
var failedAttempts = 0
122+
connectionManager.connectionState.collect { state ->
123+
when (state) {
124+
ConnectionState.CONNECTED -> {
125+
failedAttempts = 0
126+
}
127+
ConnectionState.DISCONNECTED -> {
128+
val paired = settingsRepository.get().externalGpsAddress != null
129+
if (!paired || explicitlyDisconnected) return@collect
130+
// Backoff: 2 / 5 / 10 / 30 s, then 30 s forever. Keeps
131+
// the radio quiet when the box is out of range while
132+
// still recovering quickly from a single-frame drop.
133+
val delayMs = when (failedAttempts) {
134+
0 -> 1_500L
135+
1 -> 5_000L
136+
2 -> 10_000L
137+
else -> 30_000L
138+
}
139+
Log.i(TAG, "Auto-reconnect attempt ${failedAttempts + 1} after ${delayMs}ms")
140+
kotlinx.coroutines.delay(delayMs)
141+
// Re-check after the backoff: rider might have
142+
// disconnected/unpaired during the wait.
143+
val recheck = settingsRepository.get().externalGpsAddress != null
144+
if (!recheck || explicitlyDisconnected) return@collect
145+
if (connectionState.value == ConnectionState.DISCONNECTED) {
146+
failedAttempts += 1
147+
connectPaired()
148+
}
149+
}
150+
else -> { /* CONNECTING / INITIALIZING — let it complete */ }
151+
}
152+
}
153+
}
154+
105155
/*
106156
// TEMP DEMO: synthesize a RaceBox session so the UI can be reviewed
107157
// without hardware. Auto-pairs a fake device, marks the connection
@@ -219,11 +269,15 @@ class ExternalGpsRepository @Inject constructor(
219269
Log.w(TAG, "No adapter for paired source $source — skipping connect")
220270
return false
221271
}
272+
// Any explicit connect call clears the user-disconnect veto so the
273+
// auto-reconnect loop resumes.
274+
explicitlyDisconnected = false
222275
connectionManager.connect(address, adapter, buildInitWrites(adapter))
223276
return true
224277
}
225278

226279
fun connect(address: String, adapter: ExternalGpsAdapter) {
280+
explicitlyDisconnected = false
227281
connectionManager.connect(address, adapter, buildInitWrites(adapter))
228282
}
229283

@@ -248,6 +302,10 @@ class ExternalGpsRepository @Inject constructor(
248302
}
249303

250304
fun disconnect() {
305+
// User-initiated disconnect: veto the auto-reconnect loop so the
306+
// rider's tap on "Disconnect" actually sticks. They reset it by
307+
// tapping Reconnect / pairing.
308+
explicitlyDisconnected = true
251309
_currentSample.value = null
252310
connectionManager.disconnect()
253311
}

0 commit comments

Comments
 (0)