Jelajahi Sumber

feat(player): add playback state bridge

onecold 4 bulan lalu
induk
melakukan
7a564c87d0

+ 79 - 0
entry/src/main/ets/controller/PlaybackStateBridge.ets

@@ -0,0 +1,79 @@
+import { AppStorage } from '@kit.ArkUI'
+import { VideoItem } from '../viewmodel/VideoItem'
+
+export interface PlaybackSnapshot {
+  queue: VideoItem[]
+  currentIndex: number
+  currentSong?: VideoItem
+  isPlaying: boolean
+  positionMs: number
+  durationMs: number
+}
+
+export class PlaybackStateBridge {
+  private static instance?: PlaybackStateBridge
+  private snapshot: PlaybackSnapshot = {
+    queue: [],
+    currentIndex: -1,
+    currentSong: undefined,
+    isPlaying: false,
+    positionMs: 0,
+    durationMs: 0
+  }
+
+  public static getInstance(): PlaybackStateBridge {
+    if (!PlaybackStateBridge.instance) {
+      PlaybackStateBridge.instance = new PlaybackStateBridge()
+    }
+    return PlaybackStateBridge.instance
+  }
+
+  public reset(): void {
+    this.snapshot = {
+      queue: [],
+      currentIndex: -1,
+      currentSong: undefined,
+      isPlaying: false,
+      positionMs: 0,
+      durationMs: 0
+    }
+    AppStorage.setOrCreate('currentSong', undefined)
+    AppStorage.setOrCreate('currentQueue', [])
+    AppStorage.setOrCreate('currentQueueIndex', -1)
+    AppStorage.setOrCreate('isPlaying', false)
+  }
+
+  public replaceQueue(queue: VideoItem[], startIndex: number): void {
+    const safeQueue = queue.slice()
+    const maxIndex = Math.max(safeQueue.length - 1, 0)
+    const safeIndex = safeQueue.length <= 0 ? -1 : Math.max(0, Math.min(startIndex, maxIndex))
+    const currentSong = safeIndex >= 0 ? safeQueue[safeIndex] : undefined
+
+    this.snapshot = {
+      ...this.snapshot,
+      queue: safeQueue,
+      currentIndex: safeIndex,
+      currentSong
+    }
+    AppStorage.setOrCreate('currentSong', currentSong)
+    AppStorage.setOrCreate('currentQueue', safeQueue)
+    AppStorage.setOrCreate('currentQueueIndex', safeIndex)
+  }
+
+  public updatePlaybackState(isPlaying: boolean, positionMs: number, durationMs: number): void {
+    this.snapshot = {
+      ...this.snapshot,
+      isPlaying,
+      positionMs,
+      durationMs
+    }
+    AppStorage.setOrCreate('isPlaying', isPlaying)
+  }
+
+  public getSnapshot(): PlaybackSnapshot {
+    return {
+      ...this.snapshot,
+      queue: this.snapshot.queue.slice()
+    }
+  }
+}

+ 40 - 0
entry/src/ohosTest/ets/test/PlaybackStateBridge.test.ets

@@ -0,0 +1,40 @@
+import { describe, expect, it } from '@ohos/hypium'
+import { PlaybackStateBridge } from '../../../main/ets/controller/PlaybackStateBridge'
+import { VideoItem } from '../../../main/ets/viewmodel/VideoItem'
+
+export default function playbackStateBridgeTest() {
+  describe('PlaybackStateBridgeTest', () => {
+    it('replaceQueueSyncsCurrentSongAndIndex', 0, () => {
+      const bridge = PlaybackStateBridge.getInstance()
+      const songs: VideoItem[] = [
+        new VideoItem('Song A', '1', '/music/a.flac', 0, 0, '2026-04-02'),
+        new VideoItem('Song B', '2', '/music/b.flac', 0, 0, '2026-04-02')
+      ]
+
+      bridge.reset()
+      bridge.replaceQueue(songs, 99)
+
+      const snapshot = bridge.getSnapshot()
+      expect(snapshot.queue.length).assertEqual(2)
+      expect(snapshot.currentIndex).assertEqual(1)
+      expect(snapshot.currentSong?.filePath ?? '').assertEqual('/music/b.flac')
+    })
+
+    it('updatePlaybackStateDoesNotDropQueue', 0, () => {
+      const bridge = PlaybackStateBridge.getInstance()
+      const songs: VideoItem[] = [
+        new VideoItem('Song A', '1', '/music/a.flac', 0, 0, '2026-04-02')
+      ]
+
+      bridge.reset()
+      bridge.replaceQueue(songs, 0)
+      bridge.updatePlaybackState(true, 1350, 4200)
+
+      const snapshot = bridge.getSnapshot()
+      expect(snapshot.isPlaying).assertTrue()
+      expect(snapshot.positionMs).assertEqual(1350)
+      expect(snapshot.durationMs).assertEqual(4200)
+      expect(snapshot.currentSong?.filePath ?? '').assertEqual('/music/a.flac')
+    })
+  })
+}