|
@@ -410,6 +410,10 @@ interface widgeData {
|
|
|
formImages: Record<string, number>;
|
|
formImages: Record<string, number>;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+interface PlayPauseInterface {
|
|
|
|
|
+ widgetIsPlaying?: boolean;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListener, PlayerStateCallback {
|
|
export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListener, PlayerStateCallback {
|
|
|
private static instance: UnifiedPlayerService | null = null;
|
|
private static instance: UnifiedPlayerService | null = null;
|
|
|
private playerManager: IPlayerManager;
|
|
private playerManager: IPlayerManager;
|
|
@@ -614,6 +618,15 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 关键修复:在执行任何操作之前,首先检查播放器的实际状态。
|
|
|
|
|
+ // 如果播放器已经在播放,只需同步状态并立即返回,避免不必要的重置。
|
|
|
|
|
+ const ijkPlayer = this.playerManager.getIjkPlayer()!; // 服务就绪后播放器一定存在
|
|
|
|
|
+ if (ijkPlayer.isPlaying()) {
|
|
|
|
|
+ console.log("Heanup2 UnifiedPlayerService: 检测到播放器已在播放。同步状态并退出。");
|
|
|
|
|
+ this.syncPlayingState();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 获取当前歌曲 - 如果没有就尝试恢复
|
|
// 获取当前歌曲 - 如果没有就尝试恢复
|
|
|
let currentSong = this.playlistModel.getCurrentSong();
|
|
let currentSong = this.playlistModel.getCurrentSong();
|
|
|
if (!currentSong) {
|
|
if (!currentSong) {
|
|
@@ -632,7 +645,6 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
this.stateModel.updateLoadingState(true);
|
|
this.stateModel.updateLoadingState(true);
|
|
|
|
|
|
|
|
// 简化的播放器状态检查 - 检查是否可以从暂停恢复
|
|
// 简化的播放器状态检查 - 检查是否可以从暂停恢复
|
|
|
- const ijkPlayer = this.playerManager.getIjkPlayer()!; // 服务就绪后播放器一定存在
|
|
|
|
|
const shouldResumeFromPause = this.canResumeFromPause(ijkPlayer);
|
|
const shouldResumeFromPause = this.canResumeFromPause(ijkPlayer);
|
|
|
|
|
|
|
|
if (shouldResumeFromPause) {
|
|
if (shouldResumeFromPause) {
|
|
@@ -2450,35 +2462,39 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
|
|
|
|
|
|
|
|
|
|
// ==================== WidgetControlCallback 接口实现 ====================
|
|
// ==================== WidgetControlCallback 接口实现 ====================
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* 处理来自卡片的播放/暂停命令
|
|
* 处理来自卡片的播放/暂停命令
|
|
|
|
|
+ * @param params 包含卡片当前UI状态的对象
|
|
|
*/
|
|
*/
|
|
|
- async onPlayPause?(): Promise<void> {
|
|
|
|
|
|
|
+ public async onPlayPause(params?: PlayPauseInterface): Promise<void> {
|
|
|
try {
|
|
try {
|
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService: Received play/pause command from widget');
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService: Received play/pause command from widget');
|
|
|
-
|
|
|
|
|
- // 优先检查播放器的实际状态,而不是状态模型
|
|
|
|
|
- const ijkPlayer = this.playerManager.getIjkPlayer();
|
|
|
|
|
- const isActuallyPlaying = ijkPlayer ? ijkPlayer.isPlaying() : false;
|
|
|
|
|
- const stateModelPlaying = this.getCurrentState().isPlaying;
|
|
|
|
|
-
|
|
|
|
|
- LogUtils.getInstance()
|
|
|
|
|
- .LOGI(`UnifiedPlayerService: Widget playPause - StateModel: ${stateModelPlaying}, ActualPlayer: ${isActuallyPlaying}`);
|
|
|
|
|
-
|
|
|
|
|
- // 如果播放器实际在播放,则暂停;否则开始/恢复播放
|
|
|
|
|
- if (isActuallyPlaying) {
|
|
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Player is actually playing, will pause');
|
|
|
|
|
- await this.pause();
|
|
|
|
|
|
|
+ const widgetIsPlaying = params?.widgetIsPlaying;
|
|
|
|
|
+
|
|
|
|
|
+ if (widgetIsPlaying !== undefined) {
|
|
|
|
|
+ // 新逻辑:信任卡片传递的状态
|
|
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using widget state - widgetIsPlaying=${widgetIsPlaying}`);
|
|
|
|
|
+ if (widgetIsPlaying) {
|
|
|
|
|
+ // 卡片显示的是暂停按钮(UI为播放状态),所以执行暂停
|
|
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: Widget state is PLAYING, will pause.');
|
|
|
|
|
+ await this.pause();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 卡片显示的是播放按钮(UI为暂停/停止状态),所以执行播放
|
|
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: Widget state is PAUSED, will play.');
|
|
|
|
|
+ await this.startPlayOrResumePlay();
|
|
|
|
|
+ }
|
|
|
} else {
|
|
} else {
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Player is not playing, will start/resume play');
|
|
|
|
|
- await this.startPlayOrResumePlay();
|
|
|
|
|
|
|
+ // 向后兼容的旧逻辑:根据播放器实际状态切换
|
|
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: widgetIsPlaying not provided, using fallback logic.');
|
|
|
|
|
+ const isActuallyPlaying = this.getActualPlayingState();
|
|
|
|
|
+ if (isActuallyPlaying) {
|
|
|
|
|
+ await this.pause();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ await this.startPlayOrResumePlay();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- LogUtils.getInstance().LOGI('UnifiedPlayerService: Play/pause command executed successfully');
|
|
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService onPlayPause error: ${error}`);
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService onPlayPause error: ${error}`);
|
|
|
- throw new Error;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2937,81 +2953,72 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
* 将VideoItem和PlayerState数据传入桌面卡片,控制卡片的显示
|
|
* 将VideoItem和PlayerState数据传入桌面卡片,控制卡片的显示
|
|
|
*/
|
|
*/
|
|
|
async updateAllForms(forceUpdate: boolean = false) {
|
|
async updateAllForms(forceUpdate: boolean = false) {
|
|
|
- // 定义变量在方法开始处,确保在整个方法中都可以访问
|
|
|
|
|
|
|
+ // 防抖和重入保护,防止短时间内重复执行更新
|
|
|
|
|
+ if (this.isUpdatingForms) {
|
|
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: Update already in progress, skipping.');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
- const now = Date.now();
|
|
|
|
|
this.isUpdatingForms = true;
|
|
this.isUpdatingForms = true;
|
|
|
- this.lastUpdateTime = now;
|
|
|
|
|
- // 从数据库获取所有Form信息
|
|
|
|
|
- const context = AppStorage.get('context') as common.UIAbilityContext;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ const context = this.getContext();
|
|
|
if (!context) {
|
|
if (!context) {
|
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No context available');
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No context available');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 从数据库读取所有Form信息
|
|
|
|
|
- let formInfoList: FormInfo[] = [];
|
|
|
|
|
- try {
|
|
|
|
|
- formInfoList = await FormRdbHelper.getInstance(context).queryAllForms();
|
|
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Found ${formInfoList.length} forms in database`);
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Failed to query forms from database: ${error}`);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const formInfoList = await FormRdbHelper.getInstance(context).queryAllForms();
|
|
|
if (formInfoList.length === 0) {
|
|
if (formInfoList.length === 0) {
|
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No forms found in database');
|
|
LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No forms found in database');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // --- 两阶段更新策略 ---
|
|
|
|
|
+ // 阶段一:立即更新UI状态(文本、图标等),不包含封面图,确保响应速度
|
|
|
|
|
+ const dataWithoutImage = await this.getWidgetFormData(false); // false: 不加载图片
|
|
|
|
|
+ if (!dataWithoutImage) {
|
|
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: Failed to get widget data for phase 1.');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 记录更新统计
|
|
|
|
|
- const invalidFormIds: string[] = [];
|
|
|
|
|
- let flatWidgetData = await this.getWidgetFormData();
|
|
|
|
|
-
|
|
|
|
|
- // 逐个更新所有卡片,根据卡片类型进行个性化处理
|
|
|
|
|
- for (const formInfo of formInfoList) {
|
|
|
|
|
- try {
|
|
|
|
|
- const formBindingDataInstance = formBindingData.createFormBindingData(flatWidgetData);
|
|
|
|
|
- await formProvider.updateForm(formInfo.formId, formBindingDataInstance);
|
|
|
|
|
|
|
+ const formBindingDataWithoutImage = formBindingData.createFormBindingData(dataWithoutImage);
|
|
|
|
|
+ // 并行更新所有卡片,提高效率
|
|
|
|
|
+ const updatePromisesPhase1 = formInfoList.map(formInfo =>
|
|
|
|
|
+ formProvider.updateForm(formInfo.formId, formBindingDataWithoutImage)
|
|
|
|
|
+ );
|
|
|
|
|
+ await Promise.all(updatePromisesPhase1);
|
|
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Phase 1 (quick update) completed for ${formInfoList.length} forms.`);
|
|
|
|
|
|
|
|
- LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Successfully updated form ${formInfo.formId} (${formInfo.formName})`);
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- const errorStr: string = error?.toString() || '';
|
|
|
|
|
- invalidFormIds.push(formInfo.formId);
|
|
|
|
|
- LogUtils.getInstance()
|
|
|
|
|
- .LOGI(`UnifiedPlayerService updateAllForms: Form ${formInfo.formId} (${formInfo.formName}) - ${errorStr}}`);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 阶段二:异步加载封面图,然后再次更新卡片
|
|
|
|
|
+ // 使用 setTimeout(0) 将其推到事件循环的下一个tick,避免阻塞当前流程
|
|
|
|
|
+ setTimeout(async () => {
|
|
|
|
|
+ const dataWithImage = await this.getWidgetFormData(true); // true: 加载图片(这步可能很慢)
|
|
|
|
|
+ if (!dataWithImage || Object.keys(dataWithImage.formImages).length === 0) {
|
|
|
|
|
+ LogUtils.getInstance().LOGI('UnifiedPlayerService: No image to update in Phase 2.');
|
|
|
|
|
+ return; // 没有图片或加载失败,则无需第二阶段更新
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 自动清理无效的Form ID
|
|
|
|
|
- if (invalidFormIds.length > 0) {
|
|
|
|
|
- try {
|
|
|
|
|
- for (const invalidFormId of invalidFormIds) {
|
|
|
|
|
- await FormRdbHelper.getInstance(context).deleteForm(invalidFormId);
|
|
|
|
|
- }
|
|
|
|
|
- LogUtils.getInstance()
|
|
|
|
|
- .LOGI(`UnifiedPlayerService updateAllForms: Removed ${invalidFormIds.length} invalid form IDs from database: ${invalidFormIds.join(', ')}`);
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- LogUtils.getInstance()
|
|
|
|
|
- .LOGI(`UnifiedPlayerService updateAllForms: Failed to clean invalid form IDs from database: ${error}`);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const formBindingDataWithImage = formBindingData.createFormBindingData(dataWithImage);
|
|
|
|
|
+ const updatePromisesPhase2 = formInfoList.map(formInfo =>
|
|
|
|
|
+ formProvider.updateForm(formInfo.formId, formBindingDataWithImage)
|
|
|
|
|
+ );
|
|
|
|
|
+ await Promise.all(updatePromisesPhase2);
|
|
|
|
|
+ LogUtils.getInstance().LOGI(`UnifiedPlayerService: Phase 2 (image update) completed for ${formInfoList.length} forms.`);
|
|
|
|
|
+ }, 0);
|
|
|
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
|
|
+ } catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms error: ${error}`);
|
|
LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms error: ${error}`);
|
|
|
} finally {
|
|
} finally {
|
|
|
- // 重置更新标志
|
|
|
|
|
|
|
+ // 关键:在阶段一完成后就立即释放标志,允许新的状态更新(不含图片)可以被触发
|
|
|
|
|
+ // 阶段二的图片加载在后台进行,不应阻塞后续的状态更新
|
|
|
this.isUpdatingForms = false;
|
|
this.isUpdatingForms = false;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public async getWidgetFormData(): Promise<widgeData | undefined> {
|
|
|
|
|
|
|
+ public async getWidgetFormData(loadImage: boolean = true): Promise<widgeData | undefined> {
|
|
|
let imgMap: Record<string, number> = {};
|
|
let imgMap: Record<string, number> = {};
|
|
|
this.updatePlaylistStateInModelSync();
|
|
this.updatePlaylistStateInModelSync();
|
|
|
- const now = Date.now();
|
|
|
|
|
- this.isUpdatingForms = true;
|
|
|
|
|
- this.lastUpdateTime = now;
|
|
|
|
|
const formData: VideoItem = this.getCurrentSong() as VideoItem; // VideoItem
|
|
const formData: VideoItem = this.getCurrentSong() as VideoItem; // VideoItem
|
|
|
const songState: PlayerState = this.getCurrentState(); // PlayerState
|
|
const songState: PlayerState = this.getCurrentState(); // PlayerState
|
|
|
|
|
|
|
@@ -3083,7 +3090,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
|
|
|
let imgName: string = '';
|
|
let imgName: string = '';
|
|
|
|
|
|
|
|
// 如果歌曲有封面图片路径,尝试处理图片
|
|
// 如果歌曲有封面图片路径,尝试处理图片
|
|
|
- if (formData.pixelMapPath && formData.pixelMapPath.trim() !== '') {
|
|
|
|
|
|
|
+ if (loadImage && formData.pixelMapPath && formData.pixelMapPath.trim() !== '') {
|
|
|
try {
|
|
try {
|
|
|
// 生成唯一的图片名称,确保每次更新时图片能正确刷新
|
|
// 生成唯一的图片名称,确保每次更新时图片能正确刷新
|
|
|
imgName = 'songCover_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
imgName = 'songCover_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|