style(android): consolidate ResumeController.restore returns into runCatching

restore() had 3 explicit returns (row null + decode-failure + empty
tracks) — over detekt's ReturnCount limit of 2. Folded the decode
+ empty-check into a single runCatching chain:

  runCatching { decode }
    .onFailure { dao.clear() side-effect }
    .getOrNull()
    ?.takeIf { tracks.isNotEmpty() }
    ?: return

Two returns now (row missing + the chain result null). Cleaner read
too — the side effect of dropping a corrupt row is right next to the
decode it guards.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-23 23:27:06 -04:00
parent 2a28d22a2a
commit e2f87ce940
@@ -8,7 +8,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@@ -41,21 +40,25 @@ class ResumeController @Inject constructor(
} }
suspend fun restore() { suspend fun restore() {
// runCatching folds row-missing + decode-failure + empty-tracks into
// a single null-or-go-ahead expression so detekt's ReturnCount rule
// is satisfied. The .onFailure side effect drops a corrupted row
// rather than leaving a poison pill across launches.
val row = dao.get() ?: return val row = dao.get() ?: return
val payload = val payload =
try { runCatching { json.decodeFromString<ResumePayload>(row.json) }
json.decodeFromString<ResumePayload>(row.json) .onFailure {
} catch (e: SerializationException) { Timber.w(it, "ResumeController: dropping unreadable resume state")
// Schema drift or corruption — drop the row and start clean dao.clear()
// rather than crashing or leaving a poison pill. }
Timber.w(e, "ResumeController: dropping unreadable resume state") .getOrNull()
dao.clear() ?.takeIf { it.tracks.isNotEmpty() }
return ?: return
}
if (payload.tracks.isEmpty()) return
playerController.setQueue( playerController.setQueue(
tracks = payload.tracks, tracks = payload.tracks,
initialIndex = payload.queueIndex.coerceAtLeast(0).coerceAtMost(payload.tracks.lastIndex), initialIndex = payload.queueIndex
.coerceAtLeast(0)
.coerceAtMost(payload.tracks.lastIndex),
source = payload.source, source = payload.source,
) )
// PositionMs not seeked yet — Player connection may still be // PositionMs not seeked yet — Player connection may still be