feat(android): RemotePlayerState container for UPnP-synthesized player state
android / Build + lint + test (push) Failing after 1m28s
android / Build + lint + test (push) Failing after 1m28s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
package com.fabledsword.minstrel.player
|
||||
|
||||
/**
|
||||
* Synthesized state for the UPnP route -- what the ForwardingPlayer
|
||||
* exposes via Player.getCurrentPosition / isPlaying / etc. when the
|
||||
* remote leg is active. Not a Player; a container.
|
||||
*
|
||||
* Updates flow in from:
|
||||
* - 1Hz GetPositionInfo poll -> applyPositionInfo()
|
||||
* - Transport SOAP calls landing 200 OK -> applyTransport{Playing,Paused,Stopped}()
|
||||
* - Error paths -> applyError() (drop fallback) or recordPollFailure()
|
||||
*
|
||||
* The poll-failure counter implements the rolling-3 drop heuristic: 3
|
||||
* consecutive poll failures = remote considered dropped (returns true
|
||||
* from recordPollFailure for the caller to surface). Success resets it.
|
||||
*/
|
||||
class RemotePlayerState {
|
||||
|
||||
@Volatile var positionMs: Long = 0L; private set
|
||||
@Volatile var durationMs: Long = 0L; private set
|
||||
@Volatile var isPlaying: Boolean = false; private set
|
||||
@Volatile var currentTrackUri: String = ""; private set
|
||||
@Volatile var lastError: Throwable? = null; private set
|
||||
|
||||
private var consecutivePollFailures: Int = 0
|
||||
|
||||
fun applyPositionInfo(positionMs: Long, durationMs: Long, trackUri: String) {
|
||||
this.positionMs = positionMs
|
||||
this.durationMs = durationMs
|
||||
this.currentTrackUri = trackUri
|
||||
}
|
||||
|
||||
fun applyTransportPlaying() { isPlaying = true }
|
||||
fun applyTransportPaused() { isPlaying = false }
|
||||
fun applyTransportStopped() {
|
||||
isPlaying = false
|
||||
positionMs = 0L
|
||||
}
|
||||
|
||||
fun applyError(t: Throwable) {
|
||||
isPlaying = false
|
||||
lastError = t
|
||||
}
|
||||
|
||||
/** Returns true when the rolling threshold trips this call. */
|
||||
fun recordPollFailure(): Boolean {
|
||||
consecutivePollFailures += 1
|
||||
return consecutivePollFailures >= DROP_THRESHOLD
|
||||
}
|
||||
|
||||
fun recordPollSuccess() { consecutivePollFailures = 0 }
|
||||
|
||||
fun reset() {
|
||||
positionMs = 0L
|
||||
durationMs = 0L
|
||||
isPlaying = false
|
||||
currentTrackUri = ""
|
||||
lastError = null
|
||||
consecutivePollFailures = 0
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val DROP_THRESHOLD = 3
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user