Skip to content

Implement Seekbar sync for now playing#109

Merged
sameerasw merged 1 commit into
sameerasw:developfrom
Mudit200408:develop
May 21, 2026
Merged

Implement Seekbar sync for now playing#109
sameerasw merged 1 commit into
sameerasw:developfrom
Mudit200408:develop

Conversation

@Mudit200408
Copy link
Copy Markdown
Contributor

All Conflicts FIXED!!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds seekbar synchronization support for “Now Playing” by sending duration/position/buffering state over the existing status channel and handling incoming seek commands, with additional suppression logic intended to prevent stale-position “jump back” behavior after a remote seek.

Changes:

  • Added a new "seekTo" media control action over WebSocket and a MediaControlUtil.seekTo() helper.
  • Extended now playing models + status JSON payload to include duration, position, isBuffering, and positionTimestamp.
  • Added seek-related suppression and a position-jump heuristic in SyncManager to trigger syncs on significant local position changes.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
app/src/main/java/com/sameerasw/airsync/utils/WebSocketMessageHandler.kt Adds handling for "seekTo" media control messages and activates seek suppression before executing seek.
app/src/main/java/com/sameerasw/airsync/utils/SyncManager.kt Adds seek suppression, tracks last sync time, and introduces position-jump detection to trigger syncs after local seeks.
app/src/main/java/com/sameerasw/airsync/utils/MediaControlUtil.kt Introduces seekTo() via MediaController.TransportControls.
app/src/main/java/com/sameerasw/airsync/utils/JsonUtil.kt Extends status JSON with seekbar fields and escapes title/artist.
app/src/main/java/com/sameerasw/airsync/utils/DeviceInfoUtil.kt Plumbs new media fields from MediaNotificationListener into AudioInfo and status JSON.
app/src/main/java/com/sameerasw/airsync/service/MediaNotificationListener.kt Computes duration/position/buffering state and a wall-clock timestamp for the position snapshot; adjusts suppression bypass logic for state changes.
app/src/main/java/com/sameerasw/airsync/domain/model/DeviceStatus.kt Extends AudioInfo and MediaInfo with duration/position/buffering/timestamp fields.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +113 to +125
// Position-jump detection: if positionMs is more than 8 seconds away from
// what we'd expect based on normal playback since the last sync, the user
// seeked on Android — trigger an immediate sync so the Mac can update.
// Guard: lastSyncTimeMs == 0 means we haven't done a successful sync yet
// (can happen if performInitialSync failed). Skip detection to avoid a
// huge elapsedMs causing a spurious forced sync.
if (!shouldSync && currentAudio.isPlaying && last.positionMs >= 0 && currentAudio.positionMs >= 0 && lastSyncTimeMs > 0) {
val elapsedMs = System.currentTimeMillis() - lastSyncTimeMs
val expectedPositionMs = last.positionMs + elapsedMs
val positionDelta = kotlin.math.abs(currentAudio.positionMs - expectedPositionMs)
if (positionDelta > 8_000L) {
shouldSync = true
Log.d(TAG, "Position jump detected (delta=${positionDelta}ms), syncing")
Comment on lines +113 to +125
// Position-jump detection: if positionMs is more than 8 seconds away from
// what we'd expect based on normal playback since the last sync, the user
// seeked on Android — trigger an immediate sync so the Mac can update.
// Guard: lastSyncTimeMs == 0 means we haven't done a successful sync yet
// (can happen if performInitialSync failed). Skip detection to avoid a
// huge elapsedMs causing a spurious forced sync.
if (!shouldSync && currentAudio.isPlaying && last.positionMs >= 0 && currentAudio.positionMs >= 0 && lastSyncTimeMs > 0) {
val elapsedMs = System.currentTimeMillis() - lastSyncTimeMs
val expectedPositionMs = last.positionMs + elapsedMs
val positionDelta = kotlin.math.abs(currentAudio.positionMs - expectedPositionMs)
if (positionDelta > 8_000L) {
shouldSync = true
Log.d(TAG, "Position jump detected (delta=${positionDelta}ms), syncing")
Comment on lines +126 to +127
val durationMs = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION) ?: -1L

Comment on lines +133 to +142
if (positionMs >= 0 && playbackState != null) {
// Map elapsedRealtime of the last state update → wall-clock ms
val elapsedAtUpdate = playbackState.lastPositionUpdateTime
val elapsedNow = android.os.SystemClock.elapsedRealtime()
val wallNow = System.currentTimeMillis()
// Wall-clock instant when playbackState.position was last set
val wallAtUpdate = wallNow - (elapsedNow - elapsedAtUpdate)
// Advance position to now (if playing) or leave frozen (if paused/buffering)
if (isPlaying) {
val timeDelta = elapsedNow - elapsedAtUpdate
Comment on lines +643 to +650
fun onMediaStateChanged(context: Context, isPlayingChanged: Boolean = false) {
if (shouldSuppressMediaUpdate()) {
Log.d(TAG, "Media state change suppressed due to recent skip command")
// Always let play/pause state changes through, even during seek/skip suppression.
// This ensures "pause immediately after Mac seek" works correctly — the Mac will
// stop its local timer without waiting for the suppression window to expire.
if (isPlayingChanged) {
Log.d(TAG, "isPlaying changed during suppression — bypassing to sync")
checkAndSyncDeviceStatus(context)
  Handle mediaControl seekTo commands on Android and sync playback
  position metadata back to macOS.

  - add seekTo transport control support
  - include duration, position, timestamp, and buffering in status payloads
  - update sync logic so seekbar state can reconcile after remote scrubs
@sameerasw sameerasw changed the title feat: Implement Seekbar sync for now playing [2/2] Implement Seekbar sync for now playing May 21, 2026
@sameerasw
Copy link
Copy Markdown
Owner

Thank you!

@sameerasw sameerasw merged commit 4803b7a into sameerasw:develop May 21, 2026
@github-project-automation github-project-automation Bot moved this from Backlog to Done in AirSync Features and Bugs May 21, 2026
Mudit200408 pushed a commit to Mudit200408/airsync-android that referenced this pull request May 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants