|
|
@@ -10,6 +10,8 @@ import { DataPersistenceService, PlaylistSyncService, PlaylistSyncListener, IDat
|
|
|
import { StateSyncService, IStateSyncService, StateChangeCallback, WidgetControlCallback } from './StateSyncService';
|
|
|
import { ErrorRecoveryStrategy, IErrorRecoveryStrategy, ErrorContext, ErrorRecoveryAction } from './ErrorRecoveryStrategy';
|
|
|
import { EnhancedFormUpdateService, UpdateStats } from '../widget/EnhancedFormUpdateService';
|
|
|
+import { AvSessionController } from '../../controller/AvSessionController';
|
|
|
+import { avSession } from '@kit.AVSessionKit';
|
|
|
import json from '@ohos.util.json';
|
|
|
|
|
|
/**
|
|
|
@@ -75,11 +77,13 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
private stateSync: IStateSyncService;
|
|
|
private errorRecovery: IErrorRecoveryStrategy;
|
|
|
private widgetUpdateService: EnhancedFormUpdateService;
|
|
|
+ private avSessionController: AvSessionController | null = null; // 新增:AVSession控制器
|
|
|
private context: common.UIAbilityContext | null = null;
|
|
|
private progressTimer: number = -1;
|
|
|
private isInitialized: boolean = false;
|
|
|
private isDataRestored: boolean = false; // 新增:数据恢复完成标识
|
|
|
private currentRetryCount: number = 0;
|
|
|
+ private lastAvSessionUpdate: number = 0; // 新增:上次AVSession更新时间
|
|
|
|
|
|
private constructor() {
|
|
|
this.playerManager = PlayerManager.getInstance();
|
|
|
@@ -126,6 +130,9 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
this.widgetUpdateService.setAppContext(context);
|
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService: Widget update service initialized');
|
|
|
|
|
|
+ // 初始化AVSession控制器
|
|
|
+ this.initializeAvSession();
|
|
|
+
|
|
|
// 设置同步监听器
|
|
|
this.playlistSync.addSyncListener(this);
|
|
|
this.stateSync.subscribeToStateChanges(this);
|
|
|
@@ -145,11 +152,15 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
|
|
|
onStateChanged(state: PlayerState): void {
|
|
|
this.stateSync.broadcastState(state);
|
|
|
+ // 状态变化时更新AVSession
|
|
|
+ this.unifiedService.updateSessionPlayState(state.isPlaying);
|
|
|
// 状态变化时更新卡片
|
|
|
this.unifiedService.updateWidgetsForStateChange(state);
|
|
|
}
|
|
|
onSongChanged(song: VideoItem): void {
|
|
|
this.stateSync.broadcastSongChange(song);
|
|
|
+ // 歌曲变化时更新AVSession元数据
|
|
|
+ this.unifiedService.updateAvSessionMetadata(song);
|
|
|
// 歌曲变化时更新卡片
|
|
|
this.unifiedService.updateWidgetsForSongChange(song);
|
|
|
}
|
|
|
@@ -270,6 +281,9 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
// 注意:实际播放和状态更新会在 onPrepared 回调中开始
|
|
|
}
|
|
|
|
|
|
+ // 更新AVSession元数据
|
|
|
+ await this.updateAvSessionMetadata(currentSong);
|
|
|
+
|
|
|
// 恢复播放位置(如果有记忆播放功能)
|
|
|
this.restorePlaybackPosition(currentSong);
|
|
|
|
|
|
@@ -343,8 +357,8 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
// 保存播放状态变化
|
|
|
this.saveCurrentState();
|
|
|
|
|
|
- // 更新卡片显示暂停状态
|
|
|
- await this.updateWidgetsForPlayStateChange(false);
|
|
|
+ // 更新AVSession播放状态
|
|
|
+ this.updateSessionPlayState(false);
|
|
|
|
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService: Playback paused');
|
|
|
} catch (error) {
|
|
|
@@ -664,6 +678,12 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
// 保存当前状态
|
|
|
this.saveCurrentState();
|
|
|
|
|
|
+ // 清理AVSession
|
|
|
+ if (this.avSessionController) {
|
|
|
+ this.avSessionController.unregisterSessionListener();
|
|
|
+ this.avSessionController = null;
|
|
|
+ }
|
|
|
+
|
|
|
// 清理同步监听器
|
|
|
this.playlistSync.removeSyncListener(this);
|
|
|
this.playlistSync.release();
|
|
|
@@ -681,6 +701,257 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // ==================== AVSession 控制方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化AVSession
|
|
|
+ * 按照官方文档的要求:创建 -> 注册控制命令 -> 设置元数据 -> 激活
|
|
|
+ */
|
|
|
+ private initializeAvSession(): void {
|
|
|
+ try {
|
|
|
+ // 1. 创建AVSession控制器(音频模式)
|
|
|
+ this.avSessionController = AvSessionController.getInstance(false);
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession controller created');
|
|
|
+
|
|
|
+ // 2. 延迟设置监听器,等待AVSession创建完成
|
|
|
+ setTimeout(() => {
|
|
|
+ this.setAvSessionListener();
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession listeners configured');
|
|
|
+ }, 500);
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to initialize AVSession: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置AVSession监听器
|
|
|
+ * 注意:控制命令已在AvSessionController中注册,这里设置实际的处理逻辑
|
|
|
+ */
|
|
|
+ private setAvSessionListener(): void {
|
|
|
+ if (!this.avSessionController) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const avSession = this.avSessionController.getAvSession();
|
|
|
+ if (!avSession) {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession not available for listener setup');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 重新注册监听器,覆盖AvSessionController中的空实现
|
|
|
+ // 播放事件监听
|
|
|
+ avSession.on('play', () => {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession play command received');
|
|
|
+ this.startPlayOrResumePlay().catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession play command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 暂停事件监听
|
|
|
+ avSession.on('pause', () => {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession pause command received');
|
|
|
+ this.pause().catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession pause command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 停止事件监听
|
|
|
+ avSession.on('stop', () => {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession stop command received');
|
|
|
+ this.stop().catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession stop command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 下一首事件监听
|
|
|
+ avSession.on('playNext', () => {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession playNext command received');
|
|
|
+ this.playNext().catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession playNext command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 上一首事件监听
|
|
|
+ avSession.on('playPrevious', () => {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession playPrevious command received');
|
|
|
+ this.playPrevious().catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession playPrevious command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 拖拽进度事件监听
|
|
|
+ avSession.on('seek', (time: number) => {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession seek command received: ${time}ms`);
|
|
|
+ this.seekTo(time.toString()).catch((error: Error) => {
|
|
|
+ LogUtils.getInstance().LOGI(`AVSession seek command failed: ${error}`);
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 循环模式设置监听
|
|
|
+ avSession.on('setLoopMode', (mode: avSession.LoopMode) => {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession setLoopMode command received: ${mode}`);
|
|
|
+ // 转换AVSession循环模式到应用内部播放模式
|
|
|
+ const playMode = this.convertLoopModeToPlayMode(mode);
|
|
|
+ this.setPlayMode(playMode);
|
|
|
+ });
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession listeners set up successfully');
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to set AVSession listeners: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新AVSession播放状态
|
|
|
+ * 按照官方文档要求设置完整的播放状态信息
|
|
|
+ */
|
|
|
+ private updateSessionPlayState(isPlaying: boolean): void {
|
|
|
+ if (!this.avSessionController) {
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: AVSession controller is null');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 检查AVSession状态
|
|
|
+ this.avSessionController.checkAvSessionStatus();
|
|
|
+
|
|
|
+ const currentSong = this.playlistModel.getCurrentSong();
|
|
|
+ const currentPosition = this.getCurrentPosition();
|
|
|
+ const duration = currentSong?.duration ? Number(currentSong.duration) : 0;
|
|
|
+ const currentState = this.stateModel.getState();
|
|
|
+
|
|
|
+ // 按照官方文档要求设置完整的播放状态
|
|
|
+ const playbackState: avSession.AVPlaybackState = {
|
|
|
+ // 播放状态
|
|
|
+ state: isPlaying ? avSession.PlaybackState.PLAYBACK_STATE_PLAY : avSession.PlaybackState.PLAYBACK_STATE_PAUSE,
|
|
|
+
|
|
|
+ // 播放位置信息 - 用于进度条显示
|
|
|
+ position: {
|
|
|
+ elapsedTime: currentPosition, // 已播放时间(毫秒)
|
|
|
+ updateTime: Date.now() // 更新时间戳
|
|
|
+ },
|
|
|
+
|
|
|
+ // 播放速度
|
|
|
+ speed: currentState.speed || 1.0,
|
|
|
+
|
|
|
+ // 缓冲时间
|
|
|
+ bufferedTime: Math.max(currentPosition, 0),
|
|
|
+
|
|
|
+ // 循环模式
|
|
|
+ loopMode: this.convertPlayModeToLoopMode(currentState.playMode),
|
|
|
+
|
|
|
+ // 收藏状态
|
|
|
+ isFavorite: false // 可以根据实际收藏状态设置
|
|
|
+ };
|
|
|
+
|
|
|
+ this.avSessionController.setAvSessionPlayState(playbackState);
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession play state updated: ${isPlaying ? 'PLAYING' : 'PAUSED'}, position: ${currentPosition}ms/${duration}ms, speed: ${playbackState.speed}x, loopMode: ${playbackState.loopMode}`);
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update AVSession play state: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置当前播放模式到AVSession
|
|
|
+ */
|
|
|
+ private setCurrentPlayMode(): void {
|
|
|
+ if (!this.avSessionController) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const playMode = this.stateModel.getState().playMode;
|
|
|
+ const isPlaying = this.stateModel.getState().isPlaying;
|
|
|
+ const currentPosition = this.getCurrentPosition();
|
|
|
+
|
|
|
+ const playbackState: avSession.AVPlaybackState = {
|
|
|
+ state: isPlaying ? avSession.PlaybackState.PLAYBACK_STATE_PLAY : avSession.PlaybackState.PLAYBACK_STATE_PAUSE,
|
|
|
+ position: {
|
|
|
+ elapsedTime: currentPosition,
|
|
|
+ updateTime: Date.now()
|
|
|
+ },
|
|
|
+ bufferedTime: currentPosition,
|
|
|
+ loopMode: this.convertPlayModeToLoopMode(playMode),
|
|
|
+ isFavorite: false
|
|
|
+ };
|
|
|
+
|
|
|
+ this.avSessionController.setAvSessionPlayState(playbackState);
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession play mode updated: ${playMode}`);
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to set AVSession play mode: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换播放模式为AVSession循环模式
|
|
|
+ */
|
|
|
+ private convertPlayModeToLoopMode(playMode: PlayMode): avSession.LoopMode {
|
|
|
+ switch (playMode) {
|
|
|
+ case PlayMode.SINGLE_REPEAT:
|
|
|
+ return avSession.LoopMode.LOOP_MODE_SINGLE;
|
|
|
+ case PlayMode.NORMAL:
|
|
|
+ return avSession.LoopMode.LOOP_MODE_LIST;
|
|
|
+ case PlayMode.RANDOM:
|
|
|
+ return avSession.LoopMode.LOOP_MODE_SHUFFLE;
|
|
|
+ case PlayMode.SEQUENCE:
|
|
|
+ default:
|
|
|
+ return avSession.LoopMode.LOOP_MODE_SEQUENCE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换AVSession循环模式为播放模式
|
|
|
+ */
|
|
|
+ private convertLoopModeToPlayMode(loopMode: avSession.LoopMode): number {
|
|
|
+ switch (loopMode) {
|
|
|
+ case avSession.LoopMode.LOOP_MODE_SINGLE:
|
|
|
+ return PlayMode.SINGLE_REPEAT;
|
|
|
+ case avSession.LoopMode.LOOP_MODE_LIST:
|
|
|
+ return PlayMode.NORMAL;
|
|
|
+ case avSession.LoopMode.LOOP_MODE_SHUFFLE:
|
|
|
+ return PlayMode.RANDOM;
|
|
|
+ case avSession.LoopMode.LOOP_MODE_SEQUENCE:
|
|
|
+ default:
|
|
|
+ return PlayMode.SEQUENCE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新AVSession元数据
|
|
|
+ * 按照官方文档要求:设置必要的元数据(标题、副标题/歌手、封面图)
|
|
|
+ */
|
|
|
+ private async updateAvSessionMetadata(song: VideoItem): Promise<void> {
|
|
|
+ if (!this.avSessionController || !song) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const duration = song.duration ? Number(song.duration) : 0;
|
|
|
+ // 获取歌词内容(如果有的话)
|
|
|
+ const lyricContent = ''; // 这里可以根据需要获取歌词内容
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Setting AVSession metadata for "${song.name}" by "${song.artist || '未知艺术家'}"`);
|
|
|
+
|
|
|
+ // 调用AvSessionController设置元数据,它会处理激活逻辑
|
|
|
+ await this.avSessionController.setAVMetadataMusic(song, duration, lyricContent);
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession metadata and activation completed for ${song.name}`);
|
|
|
+
|
|
|
+ // 元数据设置完成后,同步当前播放状态
|
|
|
+ setTimeout(() => {
|
|
|
+ const currentState = this.stateModel.getState();
|
|
|
+ this.updateSessionPlayState(currentState.isPlaying);
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession state synced - playing: ${currentState.isPlaying}`);
|
|
|
+ }, 300); // 延迟300ms确保AVSession完全激活
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update AVSession metadata: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// ==================== 错误处理和恢复方法 ====================
|
|
|
|
|
|
/**
|
|
|
@@ -932,6 +1203,14 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
const duration = ijkPlayer.getDuration();
|
|
|
|
|
|
this.stateModel.updateProgress(currentPosition, duration);
|
|
|
+
|
|
|
+ // 定期更新AVSession播放状态,确保系统媒体控制界面显示正确的进度
|
|
|
+ // 但不要太频繁,避免性能问题
|
|
|
+ const now = Date.now();
|
|
|
+ if (!this.lastAvSessionUpdate || now - this.lastAvSessionUpdate > 5000) { // 每5秒更新一次
|
|
|
+ this.lastAvSessionUpdate = now;
|
|
|
+ this.updateSessionPlayState(true);
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService updateProgress error: ${error}`);
|
|
|
}
|
|
|
@@ -1359,8 +1638,16 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
// 保存播放状态变化
|
|
|
this.saveCurrentState();
|
|
|
|
|
|
+ // 立即更新AVSession播放状态
|
|
|
+ this.updateSessionPlayState(true);
|
|
|
+
|
|
|
+ // 设置当前播放模式
|
|
|
+ this.setCurrentPlayMode();
|
|
|
+
|
|
|
// 播放开始时更新卡片显示播放状态
|
|
|
this.updateWidgetsForPlayStateChange(true);
|
|
|
+
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: Playback started - AVSession state updated to PLAYING');
|
|
|
}
|
|
|
|
|
|
onPlaybackCompleted(): void {
|
|
|
@@ -1371,6 +1658,9 @@ async initialize(context: common.UIAbilityContext): Promise<void> {
|
|
|
// 保存播放状态变化
|
|
|
this.saveCurrentState();
|
|
|
|
|
|
+ // 更新AVSession播放状态
|
|
|
+ this.updateSessionPlayState(false);
|
|
|
+
|
|
|
// 更新卡片显示播放完成状态
|
|
|
this.updateWidgetsForPlayStateChange(false);
|
|
|
|