chendeben 1 жил өмнө
parent
commit
97123758f2

+ 79 - 72
entry/src/main/ets/common/service/UnifiedPlayerService.ets

@@ -410,6 +410,10 @@ interface widgeData {
   formImages: Record<string, number>;
 }
 
+interface PlayPauseInterface {
+  widgetIsPlaying?: boolean;
+}
+
 export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListener, PlayerStateCallback {
   private static instance: UnifiedPlayerService | null = null;
   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();
       if (!currentSong) {
@@ -632,7 +645,6 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       this.stateModel.updateLoadingState(true);
 
       // 简化的播放器状态检查 - 检查是否可以从暂停恢复
-      const ijkPlayer = this.playerManager.getIjkPlayer()!; // 服务就绪后播放器一定存在
       const shouldResumeFromPause = this.canResumeFromPause(ijkPlayer);
 
       if (shouldResumeFromPause) {
@@ -2450,35 +2462,39 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
 
   // ==================== WidgetControlCallback 接口实现 ====================
-
   /**
    * 处理来自卡片的播放/暂停命令
+   * @param params 包含卡片当前UI状态的对象
    */
-  async onPlayPause?(): Promise<void> {
+  public async onPlayPause(params?: PlayPauseInterface): Promise<void> {
     try {
       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 {
-        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) {
       LogUtils.getInstance().LOGI(`UnifiedPlayerService onPlayPause error: ${error}`);
-      throw new Error;
     }
   }
 
@@ -2937,81 +2953,72 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
    * 将VideoItem和PlayerState数据传入桌面卡片,控制卡片的显示
    */
   async updateAllForms(forceUpdate: boolean = false) {
-    // 定义变量在方法开始处,确保在整个方法中都可以访问
+    // 防抖和重入保护,防止短时间内重复执行更新
+    if (this.isUpdatingForms) {
+      LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: Update already in progress, skipping.');
+      return;
+    }
+
     try {
-      const now = Date.now();
       this.isUpdatingForms = true;
-      this.lastUpdateTime = now;
-      // 从数据库获取所有Form信息
-      const context = AppStorage.get('context') as common.UIAbilityContext;
+
+      const context = this.getContext();
       if (!context) {
         LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No context available');
         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) {
         LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No forms found in database');
         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}`);
     } finally {
-      // 重置更新标志
+      // 关键:在阶段一完成后就立即释放标志,允许新的状态更新(不含图片)可以被触发
+      // 阶段二的图片加载在后台进行,不应阻塞后续的状态更新
       this.isUpdatingForms = false;
     }
   }
 
-  public async getWidgetFormData(): Promise<widgeData | undefined> {
+  public async getWidgetFormData(loadImage: boolean = true): Promise<widgeData | undefined> {
     let imgMap: Record<string, number> = {};
     this.updatePlaylistStateInModelSync();
-    const now = Date.now();
-    this.isUpdatingForms = true;
-    this.lastUpdateTime = now;
     const formData: VideoItem = this.getCurrentSong() as VideoItem; // VideoItem
     const songState: PlayerState = this.getCurrentState(); // PlayerState
     
@@ -3083,7 +3090,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     let imgName: string = '';
 
     // 如果歌曲有封面图片路径,尝试处理图片
-    if (formData.pixelMapPath && formData.pixelMapPath.trim() !== '') {
+    if (loadImage && formData.pixelMapPath && formData.pixelMapPath.trim() !== '') {
       try {
         // 生成唯一的图片名称,确保每次更新时图片能正确刷新
         imgName = 'songCover_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);