| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- import commonEventManager from '@ohos.commonEventManager';
- import { hilog } from '@kit.PerformanceAnalysisKit';
- import { common } from '@kit.AbilityKit';
- import { VideoItem } from '../../viewmodel/VideoItem';
- import { PlayerState, PlayMode } from './PlayerStateModel';
- import { PlayProgress, WidgetData } from '../widget/WidgetTypes';
- import {
- PLAYER_STATE_CHANGED_EVENT,
- PLAYER_SONG_CHANGED_EVENT,
- PLAYER_PROGRESS_CHANGED_EVENT,
- WIDGET_CONTROL_EVENT,
- WIDGET_REQUEST_STATE_EVENT
- } from '../widget/WidgetEventConstants';
- const TAG = 'StateSyncService';
- /**
- * 状态同步服务接口
- */
- export interface IStateSyncService {
- broadcastState(state: PlayerState): Promise<void>;
- broadcastSongChange(song: VideoItem): Promise<void>;
- broadcastProgress(progress: PlayProgress): Promise<void>;
- subscribeToStateChanges(callback: StateChangeCallback): void;
- unsubscribeFromStateChanges(callback: StateChangeCallback): void;
- subscribeToWidgetControl(callback: WidgetControlCallback): void;
- unsubscribeFromWidgetControl(callback: WidgetControlCallback): void;
- initialize(context: common.UIAbilityContext): Promise<void>;
- release(): void;
- }
- /**
- * 状态变化回调接口
- */
- export interface StateChangeCallback {
- onStateChanged?(state: PlayerState): void;
- onSongChanged?(song: VideoItem): void;
- onProgressChanged?(progress: PlayProgress): void;
- }
- /**
- * 卡片控制命令回调接口
- */
- export interface WidgetControlCallback {
- onPlayPause?(): Promise<void>;
- onNextSong?(): Promise<void>;
- onPreviousSong?(): Promise<void>;
- onSeekTo?(position: number): Promise<void>;
- onStateRequest?(): Promise<void>;
- }
- /**
- * 卡片控制事件数据
- */
- interface WidgetControlEventData {
- command: string;
- params?: Record<string, Object>;
- timestamp: number;
- source: string;
- }
- /**
- * 播放状态数据接口
- */
- interface PlayStateBroadcast {
- isPlaying: boolean;
- isPaused: boolean;
- isLoading: boolean;
- currentPosition: number;
- duration: number;
- }
- /**
- * 当前歌曲数据接口
- */
- interface CurrentSongBroadcast {
- id: string;
- title: string;
- artist: string;
- album: string;
- filePath: string;
- duration: number;
- }
- /**
- * 进度数据接口
- */
- interface ProgressBroadcast {
- currentPosition: number;
- duration: number;
- percentage: number;
- currentTimeText: string;
- totalTimeText: string;
- }
- /**
- * 播放列表数据接口
- */
- interface PlaylistBroadcast {
- hasNext: boolean;
- hasPrevious: boolean;
- currentIndex: number;
- totalCount: number;
- }
- /**
- * 播放器状态广播数据
- */
- interface PlayerStateBroadcastData {
- playState: PlayStateBroadcast;
- currentSong: CurrentSongBroadcast;
- progress: ProgressBroadcast;
- playlist: PlaylistBroadcast;
- }
- /**
- * 歌曲变化广播数据
- */
- interface SongChangeBroadcastData {
- currentSong: CurrentSongBroadcast;
- playlist: PlaylistBroadcast;
- }
- /**
- * 进度更新广播数据
- */
- interface ProgressBroadcastData {
- currentPosition: number;
- duration: number;
- percentage: number;
- currentTimeText: string;
- totalTimeText: string;
- }
- /**
- * 状态同步服务实现
- * 负责在不同组件和进程间同步播放状态
- */
- export class StateSyncService implements IStateSyncService {
- private static instance: StateSyncService | null = null;
- private context: common.UIAbilityContext | null = null;
- private stateChangeCallbacks: StateChangeCallback[] = [];
- private widgetControlCallbacks: WidgetControlCallback[] = [];
- private isInitialized: boolean = false;
- private lastStateBroadcastTime: number = 0;
- private lastProgressBroadcastTime: number = 0;
- private readonly STATE_BROADCAST_THROTTLE: number = 500; // 状态广播节流间隔(毫秒)
- private readonly PROGRESS_BROADCAST_THROTTLE: number = 1000; // 进度广播节流间隔(毫秒)
- private readonly COMMAND_EXECUTION_TIMEOUT: number = 5000; // 命令执行超时时间(毫秒)
- private constructor() {}
- public static getInstance(): StateSyncService {
- if (!StateSyncService.instance) {
- StateSyncService.instance = new StateSyncService();
- }
- return StateSyncService.instance;
- }
- /**
- * 初始化状态同步服务
- */
- async initialize(context: common.UIAbilityContext): Promise<void> {
- if (this.isInitialized) {
- hilog.info(0x0000, TAG, 'StateSyncService already initialized');
- return;
- }
- try {
- this.context = context;
- // 注册状态请求事件监听器
- await this.registerStateRequestListener();
-
- // 注册卡片控制事件监听器
- await this.registerWidgetControlListener();
-
- this.isInitialized = true;
- hilog.info(0x0000, TAG, 'StateSyncService initialized successfully');
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to initialize StateSyncService: ${error}`);
- throw new Error(`Failed to initialize StateSyncService: ${error}`);
- }
- }
- /**
- * 广播播放状态变化
- */
- async broadcastState(state: PlayerState): Promise<void> {
- try {
- // 节流控制,避免过于频繁的广播
- const now = Date.now();
- if (now - this.lastStateBroadcastTime < this.STATE_BROADCAST_THROTTLE) {
- return;
- }
- this.lastStateBroadcastTime = now;
- const playStateBroadcast: PlayStateBroadcast = {
- isPlaying: state.isPlaying,
- isPaused: state.isPaused,
- isLoading: state.isLoading,
- currentPosition: state.currentPosition,
- duration: state.duration
- };
- const currentSongBroadcast: CurrentSongBroadcast = {
- id: state.currentSong?.id || '',
- title: state.currentSong?.name || '暂无播放',
- artist: state.currentSong?.artist || '未知艺术家',
- album: state.currentSong?.album || '未知专辑',
- filePath: state.currentSong?.filePath || '',
- duration: state.currentSong?.duration ? parseInt(state.currentSong.duration) : 0
- };
- const progressBroadcast: ProgressBroadcast = {
- currentPosition: state.currentPosition,
- duration: state.duration,
- percentage: this.calculatePercentage(state.currentPosition, state.duration),
- currentTimeText: this.formatTime(Math.floor(state.currentPosition / 1000)),
- totalTimeText: this.formatTime(Math.floor(state.duration / 1000))
- };
- const broadcastData: PlayerStateBroadcastData = {
- playState: playStateBroadcast,
- currentSong: currentSongBroadcast,
- progress: progressBroadcast,
- playlist: {
- hasNext: state.hasNext || false,
- hasPrevious: state.hasPrevious || false,
- currentIndex: state.currentIndex,
- totalCount: state.totalCount || 0
- } as PlaylistBroadcast
- };
- const publishInfo: commonEventManager.CommonEventPublishData = {
- data: JSON.stringify(broadcastData)
- };
- await commonEventManager.publish(PLAYER_STATE_CHANGED_EVENT, publishInfo, (err) => {
- if (err) {
- hilog.error(0x0000, TAG, `Failed to broadcast state: ${err}`);
- } else {
- hilog.info(0x0000, TAG, `State broadcasted: isPlaying=${state.isPlaying}, song=${state.currentSong?.name || 'none'}`);
- }
- });
- // 通知本地监听器
- this.notifyLocalStateListeners(state);
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to broadcast state: ${error}`);
- }
- }
- /**
- * 广播歌曲切换事件
- */
- async broadcastSongChange(song: VideoItem): Promise<void> {
- try {
- const currentSongBroadcast: CurrentSongBroadcast = {
- id: song.id || '',
- title: song.name || '暂无播放',
- artist: song.artist || '未知艺术家',
- album: song.album || '未知专辑',
- filePath: song.filePath || '',
- duration: song.duration ? parseInt(song.duration) : 0
- };
- const playlistBroadcast: PlaylistBroadcast = {
- hasNext: false, // 这些值需要从播放列表服务获取
- hasPrevious: false,
- currentIndex: 0,
- totalCount: 0
- };
- const broadcastData: SongChangeBroadcastData = {
- currentSong: currentSongBroadcast,
- playlist: playlistBroadcast
- };
- const publishInfo: commonEventManager.CommonEventPublishData = {
- data: JSON.stringify(broadcastData)
- };
- commonEventManager.publish(PLAYER_SONG_CHANGED_EVENT, publishInfo, (err) => {
- if (err) {
- hilog.error(0x0000, TAG, `Failed to broadcast song change: ${err}`);
- } else {
- hilog.info(0x0000, TAG, `Song change broadcasted: ${song.name} by ${song.artist || '未知艺术家'}`);
- }
- });
- // 通知本地监听器
- this.notifyLocalSongChangeListeners(song);
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to broadcast song change: ${error}`);
- }
- }
- /**
- * 广播播放进度更新
- */
- async broadcastProgress(progress: PlayProgress): Promise<void> {
- try {
- // 节流控制,避免过于频繁的进度广播
- const now = Date.now();
- if (now - this.lastProgressBroadcastTime < this.PROGRESS_BROADCAST_THROTTLE) {
- return;
- }
- this.lastProgressBroadcastTime = now;
- const broadcastData: ProgressBroadcastData = {
- currentPosition: progress.currentPosition,
- duration: progress.duration,
- percentage: this.calculatePercentage(progress.currentPosition, progress.duration),
- currentTimeText: this.formatTime(Math.floor(progress.currentPosition / 1000)),
- totalTimeText: this.formatTime(Math.floor(progress.duration / 1000))
- };
- const publishInfo: commonEventManager.CommonEventPublishData = {
- data: JSON.stringify(broadcastData)
- };
- await commonEventManager.publish(PLAYER_PROGRESS_CHANGED_EVENT, publishInfo, (err) => {
- if (err) {
- hilog.error(0x0000, TAG, `Failed to broadcast progress: ${err}`);
- } else {
- hilog.info(0x0000, TAG, `Progress broadcasted: ${broadcastData.percentage.toFixed(1)}% (${broadcastData.currentTimeText}/${broadcastData.totalTimeText})`);
- }
- });
- // 通知本地监听器
- this.notifyLocalProgressListeners(progress);
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to broadcast progress: ${error}`);
- }
- }
- /**
- * 订阅状态变化
- */
- subscribeToStateChanges(callback: StateChangeCallback): void {
- if (this.stateChangeCallbacks.indexOf(callback) === -1) {
- this.stateChangeCallbacks.push(callback);
- hilog.info(0x0000, TAG, `State change callback registered, total: ${this.stateChangeCallbacks.length}`);
- }
- }
- /**
- * 取消订阅状态变化
- */
- unsubscribeFromStateChanges(callback: StateChangeCallback): void {
- const index = this.stateChangeCallbacks.indexOf(callback);
- if (index !== -1) {
- this.stateChangeCallbacks.splice(index, 1);
- hilog.info(0x0000, TAG, `State change callback unregistered, remaining: ${this.stateChangeCallbacks.length}`);
- }
- }
- /**
- * 订阅卡片控制事件
- */
- subscribeToWidgetControl(callback: WidgetControlCallback): void {
- if (this.widgetControlCallbacks.indexOf(callback) === -1) {
- this.widgetControlCallbacks.push(callback);
- hilog.info(0x0000, TAG, `Widget control callback registered, total: ${this.widgetControlCallbacks.length}`);
- }
- }
- /**
- * 取消订阅卡片控制事件
- */
- unsubscribeFromWidgetControl(callback: WidgetControlCallback): void {
- const index = this.widgetControlCallbacks.indexOf(callback);
- if (index !== -1) {
- this.widgetControlCallbacks.splice(index, 1);
- hilog.info(0x0000, TAG, `Widget control callback unregistered, remaining: ${this.widgetControlCallbacks.length}`);
- }
- }
- /**
- * 释放资源
- */
- release(): void {
- try {
- this.stateChangeCallbacks = [];
- this.widgetControlCallbacks = [];
- this.isInitialized = false;
- hilog.info(0x0000, TAG, 'StateSyncService released');
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to release StateSyncService: ${error}`);
- }
- }
- // ==================== 私有辅助方法 ====================
- /**
- * 注册状态请求事件监听器
- */
- private async registerStateRequestListener(): Promise<void> {
- try {
- const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
- events: [WIDGET_REQUEST_STATE_EVENT]
- };
- const subscriber = await commonEventManager.createSubscriber(subscribeInfo);
-
- await commonEventManager.subscribe(subscriber, (err, data: commonEventManager.CommonEventData) => {
- if (!err) {
- hilog.info(0x0000, TAG, `Received state request from widget: ${data.event}`);
- this.handleStateRequest(data);
- } else {
- hilog.error(0x0000, TAG, `State request listener error: ${JSON.stringify(err)}`);
- }
- });
- hilog.info(0x0000, TAG, 'State request listener registered successfully');
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to register state request listener: ${error}`);
- }
- }
- /**
- * 注册卡片控制事件监听器
- */
- private async registerWidgetControlListener(): Promise<void> {
- try {
- const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
- events: [WIDGET_CONTROL_EVENT]
- };
- const subscriber = await commonEventManager.createSubscriber(subscribeInfo);
-
- commonEventManager.subscribe(subscriber, (err, data: commonEventManager.CommonEventData) => {
- if (!err) {
- hilog.info(0x0000, TAG, `Received widget control command: ${data.event}`);
- this.handleWidgetControlCommand(data);
- } else {
- hilog.error(0x0000, TAG, `Widget control listener error: ${JSON.stringify(err)}`);
- }
- });
- hilog.info(0x0000, TAG, 'Widget control listener registered successfully');
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to register widget control listener: ${error}`);
- }
- }
- /**
- * 处理状态请求
- */
- private handleStateRequest(eventData: commonEventManager.CommonEventData): void {
- try {
- const requestData: Record<string, string> = JSON.parse(eventData.data || '{}');
- const source: string = requestData.source || 'unknown';
-
- hilog.info(0x0000, TAG, `Handling state request from: ${source}`);
-
- // 通知控制回调处理状态请求
- this.notifyWidgetControlCallbacks('onStateRequest');
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to handle state request: ${error}`);
- }
- }
- /**
- * 处理卡片控制命令
- */
- private async handleWidgetControlCommand(eventData: commonEventManager.CommonEventData): Promise<void> {
- try {
- const controlData: WidgetControlEventData = JSON.parse(eventData.data || '{}');
- const command = controlData.command;
- const params = controlData.params;
- const source = controlData.source;
- const timestamp = controlData.timestamp;
-
- hilog.info(0x0000, TAG, `Processing widget control command: ${command} from ${source}`);
-
- // 验证命令时效性(防止过期命令执行)
- const now = Date.now();
- if (now - timestamp > this.COMMAND_EXECUTION_TIMEOUT) {
- hilog.warn(0x0000, TAG, `Command ${command} expired, ignoring (age: ${now - timestamp}ms)`);
- return;
- }
-
- // 验证命令来源
- if (!this.isValidCommandSource(source)) {
- hilog.warn(0x0000, TAG, `Invalid command source: ${source}, ignoring command ${command}`);
- return;
- }
-
- // 执行命令
- await this.executeWidgetCommand(command, params);
-
- hilog.info(0x0000, TAG, `Widget control command ${command} executed successfully`);
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to handle widget control command: ${error}`);
- }
- }
- /**
- * 通知本地状态监听器
- */
- private notifyLocalStateListeners(state: PlayerState): void {
- this.stateChangeCallbacks.forEach(callback => {
- try {
- if (callback.onStateChanged) {
- callback.onStateChanged(state);
- }
- } catch (error) {
- hilog.error(0x0000, TAG, `Error in state change callback: ${error}`);
- }
- });
- }
- /**
- * 通知本地歌曲变化监听器
- */
- private notifyLocalSongChangeListeners(song: VideoItem): void {
- this.stateChangeCallbacks.forEach(callback => {
- try {
- if (callback.onSongChanged) {
- callback.onSongChanged(song);
- }
- } catch (error) {
- hilog.error(0x0000, TAG, `Error in song change callback: ${error}`);
- }
- });
- }
- /**
- * 通知本地进度监听器
- */
- private notifyLocalProgressListeners(progress: PlayProgress): void {
- this.stateChangeCallbacks.forEach(callback => {
- try {
- if (callback.onProgressChanged) {
- callback.onProgressChanged(progress);
- }
- } catch (error) {
- hilog.error(0x0000, TAG, `Error in progress change callback: ${error}`);
- }
- });
- }
- /**
- * 执行卡片控制命令
- */
- private async executeWidgetCommand(command: string, params?: Record<string, Object>): Promise<void> {
- try {
- switch (command) {
- case 'PLAY_PAUSE':
- await this.notifyWidgetControlCallbacks('onPlayPause');
- break;
- case 'NEXT_SONG':
- await this.notifyWidgetControlCallbacks('onNextSong');
- break;
- case 'PREV_SONG':
- await this.notifyWidgetControlCallbacks('onPreviousSong');
- break;
- case 'SEEK_TO':
- const position = params?.position as number || 0;
- await this.notifyWidgetControlCallbacks('onSeekTo', position);
- break;
- default:
- hilog.warn(0x0000, TAG, `Unknown widget command: ${command}`);
- }
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to execute widget command ${command}: ${error}`);
- throw new Error(`Failed to execute widget command ${command}: ${error}`);
- }
- }
- /**
- * 验证命令来源是否有效
- */
- private isValidCommandSource(source: string): boolean {
- const validSources = ['widget', 'form', 'card', 'desktop_widget'];
- return validSources.includes(source);
- }
- /**
- * 通知卡片控制回调
- */
- private async notifyWidgetControlCallbacks(method: string, ...args: Object[]): Promise<void> {
- const promises: Promise<void>[] = [];
-
- this.widgetControlCallbacks.forEach(callback => {
- try {
- let promise: Promise<void> | undefined;
-
- switch (method) {
- case 'onPlayPause':
- promise = callback.onPlayPause?.();
- break;
- case 'onNextSong':
- promise = callback.onNextSong?.();
- break;
- case 'onPreviousSong':
- promise = callback.onPreviousSong?.();
- break;
- case 'onSeekTo':
- promise = callback.onSeekTo?.(args[0] as number);
- break;
- case 'onStateRequest':
- promise = callback.onStateRequest?.();
- break;
- }
-
- if (promise) {
- promises.push(promise);
- }
- } catch (error) {
- hilog.error(0x0000, TAG, `Error in widget control callback ${method}: ${error}`);
- }
- });
-
- // 等待所有回调执行完成
- if (promises.length > 0) {
- try {
- await Promise.all(promises);
- hilog.info(0x0000, TAG, `All widget control callbacks for ${method} completed`);
- } catch (error) {
- hilog.error(0x0000, TAG, `Some widget control callbacks for ${method} failed: ${error}`);
- }
- }
- }
- /**
- * 计算播放进度百分比
- */
- private calculatePercentage(current: number, total: number): number {
- if (total <= 0) return 0;
- return Math.min(100, Math.max(0, (current / total) * 100));
- }
- /**
- * 格式化时间显示
- */
- private formatTime(seconds: number): string {
- const mins = Math.floor(seconds / 60);
- const secs = Math.floor(seconds % 60);
- return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
- }
- }
|