Explorar o código

成功集成播放卡片

chendeben hai 1 ano
pai
achega
67b9c4ffcb
Modificáronse 27 ficheiros con 4426 adicións e 514 borrados
  1. 2 1
      entry/src/main/ets/common/PlayStatus.ets
  2. 90 0
      entry/src/main/ets/common/widget/AvSessionWidgetListener.ets
  3. 342 0
      entry/src/main/ets/common/widget/FormLayoutManager.ets
  4. 118 0
      entry/src/main/ets/common/widget/ObjectUtils.ets
  5. 158 47
      entry/src/main/ets/common/widget/PlayerControlService.ets
  6. 11 0
      entry/src/main/ets/common/widget/PlayerStateBroadcastData.ets
  7. 376 0
      entry/src/main/ets/common/widget/ThemeSyncService.ets
  8. 445 0
      entry/src/main/ets/common/widget/WidgetConfigManager.ets
  9. 63 3
      entry/src/main/ets/common/widget/WidgetController.ets
  10. 81 6
      entry/src/main/ets/common/widget/WidgetDataManager.ets
  11. 7 1
      entry/src/main/ets/common/widget/WidgetEventConstants.ets
  12. 302 0
      entry/src/main/ets/common/widget/WidgetSizeAdapter.ets
  13. 115 0
      entry/src/main/ets/common/widget/WidgetTypeHelpers.ets
  14. 129 0
      entry/src/main/ets/common/widget/WidgetTypes.ets
  15. 1 1
      entry/src/main/ets/common/widget/WidgetUtils.ets
  16. 0 30
      entry/src/main/ets/common/widget/index.ets
  17. 110 7
      entry/src/main/ets/entryability/EntryAbility.ets
  18. 533 0
      entry/src/main/ets/entryformability/EntryFormAbility.ets
  19. 0 206
      entry/src/main/ets/entryformability/PlayerWidgetFormExtensionAbility.ets
  20. 447 0
      entry/src/main/ets/pages/WidgetConfigPage.ets
  21. 223 0
      entry/src/main/ets/pages/WidgetDeleteConfirmPage.ets
  22. 395 24
      entry/src/main/ets/view/LocalMusic.ets
  23. 184 22
      entry/src/main/ets/widget/pages/PlayerWidgetLarge.ets
  24. 162 105
      entry/src/main/ets/widget/pages/PlayerWidgetMedium.ets
  25. 130 21
      entry/src/main/ets/widget/pages/PlayerWidgetSmall.ets
  26. 2 2
      entry/src/main/module.json5
  27. 0 38
      entry/src/main/resources/base/profile/form_config.json

+ 2 - 1
entry/src/main/ets/common/PlayStatus.ets

@@ -17,5 +17,6 @@ export enum PlayStatus {
     INIT,
     PLAY,
     PAUSE,
-    DONE
+    DONE,
+    LOADING
 }

+ 90 - 0
entry/src/main/ets/common/widget/AvSessionWidgetListener.ets

@@ -0,0 +1,90 @@
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { WidgetData, PlayState, SongInfo, PlayProgress, PlaylistState } from './WidgetTypes';
+import { WidgetTypeHelpers } from './WidgetTypeHelpers';
+
+const TAG = 'Heanup AvSessionWidgetListener';
+
+/**
+ * AvSession卡片监听器
+ * 负责监听媒体会话状态变化并同步到卡片
+ * 简化版本,避免API兼容性问题
+ */
+export class AvSessionWidgetListener {
+  private static instance: AvSessionWidgetListener | null = null;
+  private stateListeners: Array<(data: WidgetData) => void> = [];
+  private isInitialized: boolean = false;
+  private lastWidgetData: WidgetData | null = null;
+
+  private constructor() {
+    this.initializeAvSessionManager();
+  }
+
+  public static getInstance(): AvSessionWidgetListener {
+    if (!AvSessionWidgetListener.instance) {
+      AvSessionWidgetListener.instance = new AvSessionWidgetListener();
+    }
+    return AvSessionWidgetListener.instance;
+  }
+
+  /**
+   * 初始化AvSession管理器
+   */
+  private async initializeAvSessionManager(): Promise<void> {
+    try {
+      this.isInitialized = true;
+      hilog.info(0x0000, TAG, 'AvSession manager initialized (simplified version)');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to initialize AvSession manager: ${error}`);
+    }
+  }
+
+  /**
+   * 注册状态监听器
+   */
+  public addStateListener(callback: (data: WidgetData) => void): void {
+    this.stateListeners.push(callback);
+    
+    // 立即返回默认数据
+    const defaultData = this.getDefaultWidgetData();
+    callback(defaultData);
+    
+    hilog.info(0x0000, TAG, 'State listener registered');
+  }
+
+  /**
+   * 移除状态监听器
+   */
+  public removeStateListener(callback: (data: WidgetData) => void): void {
+    const index = this.stateListeners.indexOf(callback);
+    if (index > -1) {
+      this.stateListeners.splice(index, 1);
+    }
+    hilog.info(0x0000, TAG, 'State listener removed');
+  }
+
+  /**
+   * 获取当前卡片数据
+   */
+  public getCurrentWidgetData(): WidgetData {
+    if (this.lastWidgetData) {
+      return this.lastWidgetData;
+    }
+    return this.getDefaultWidgetData();
+  }
+
+  /**
+   * 销毁监听器
+   */
+  public destroy(): void {
+    this.stateListeners = [];
+    this.lastWidgetData = null;
+    hilog.info(0x0000, TAG, 'AvSession widget listener destroyed');
+  }
+
+  /**
+   * 获取默认卡片数据
+   */
+  private getDefaultWidgetData(): WidgetData {
+    return WidgetTypeHelpers.createDefaultWidgetData();
+  }
+}

+ 342 - 0
entry/src/main/ets/common/widget/FormLayoutManager.ets

@@ -0,0 +1,342 @@
+import { WidgetSize, WidgetData, FormattedWidgetData, WidgetConfig, ContainerPadding, ButtonSize, FontSizeConfig, SpacingConfig, ShowElementsConfig, ResponsiveLayoutParams } from './WidgetTypes';
+import hilog from '@ohos.hilog';
+
+const TAG = 'FormLayoutManager';
+
+/**
+ * 卡片布局管理器
+ * 负责处理多尺寸卡片的布局适配和UI重构
+ * 需求: 5.4, 5.5
+ */
+export class FormLayoutManager {
+  private static instance: FormLayoutManager;
+
+  /**
+   * 获取单例实例
+   */
+  public static getInstance(): FormLayoutManager {
+    if (!FormLayoutManager.instance) {
+      FormLayoutManager.instance = new FormLayoutManager();
+    }
+    return FormLayoutManager.instance;
+  }
+
+  private constructor() {
+    hilog.info(0x0000, TAG, 'FormLayoutManager initialized');
+  }
+
+  /**
+   * 根据卡片尺寸适配数据
+   * @param widgetData 原始卡片数据
+   * @param size 卡片尺寸
+   * @returns 适配后的格式化数据
+   */
+  public adaptDataForSize(widgetData: WidgetData, size: WidgetSize): FormattedWidgetData {
+    hilog.info(0x0000, TAG, `Adapting data for size: ${size}`);
+
+    const baseData = this.formatBaseData(widgetData);
+    
+    switch (size) {
+      case WidgetSize.SMALL:
+        return this.adaptForSmallWidget(baseData, widgetData);
+      case WidgetSize.MEDIUM:
+        return this.adaptForMediumWidget(baseData, widgetData);
+      case WidgetSize.LARGE:
+        return this.adaptForLargeWidget(baseData, widgetData);
+      default:
+        hilog.warn(0x0000, TAG, `Unknown widget size: ${size}, using medium as default`);
+        return this.adaptForMediumWidget(baseData, widgetData);
+    }
+  }
+
+  /**
+   * 获取卡片尺寸对应的页面路径
+   * @param size 卡片尺寸
+   * @returns 页面路径
+   */
+  public getWidgetPagePath(size: WidgetSize): string {
+    switch (size) {
+      case WidgetSize.SMALL:
+        return 'widget/pages/PlayerWidgetSmall';
+      case WidgetSize.MEDIUM:
+        return 'widget/pages/PlayerWidgetMedium';
+      case WidgetSize.LARGE:
+        return 'widget/pages/PlayerWidgetLarge';
+      default:
+        hilog.warn(0x0000, TAG, `Unknown widget size: ${size}, using medium as default`);
+        return 'widget/pages/PlayerWidgetMedium';
+    }
+  }
+
+  /**
+   * 检查尺寸变化并返回是否需要重构UI
+   * @param oldSize 旧尺寸
+   * @param newSize 新尺寸
+   * @returns 是否需要重构UI
+   */
+  public shouldRebuildUI(oldSize: WidgetSize, newSize: WidgetSize): boolean {
+    const needsRebuild = oldSize !== newSize;
+    hilog.info(0x0000, TAG, `Size change from ${oldSize} to ${newSize}, needs rebuild: ${needsRebuild}`);
+    return needsRebuild;
+  }
+
+  /**
+   * 获取卡片尺寸的显示配置
+   * @param size 卡片尺寸
+   * @returns 显示配置
+   */
+  public getSizeDisplayConfig(size: WidgetSize): WidgetConfig {
+    const baseConfig: WidgetConfig = {
+      size: size,
+      theme: 'auto',
+      showProgress: true,
+      showCover: true
+    };
+
+    switch (size) {
+      case WidgetSize.SMALL:
+        const smallConfig: WidgetConfig = {
+          size: baseConfig.size,
+          theme: baseConfig.theme,
+          showProgress: false,
+          showCover: false
+        };
+        return smallConfig;
+      case WidgetSize.MEDIUM:
+        const mediumConfig: WidgetConfig = {
+          size: baseConfig.size,
+          theme: baseConfig.theme,
+          showProgress: true,
+          showCover: false
+        };
+        return mediumConfig;
+      case WidgetSize.LARGE:
+        const largeConfig: WidgetConfig = {
+          size: baseConfig.size,
+          theme: baseConfig.theme,
+          showProgress: true,
+          showCover: true
+        };
+        return largeConfig;
+      default:
+        return baseConfig;
+    }
+  }
+
+  /**
+   * 验证卡片尺寸是否有效
+   * @param size 卡片尺寸
+   * @returns 是否有效
+   */
+  public isValidSize(size: string): boolean {
+    return Object.values(WidgetSize).includes(size as WidgetSize);
+  }
+
+  /**
+   * 从字符串解析卡片尺寸
+   * @param sizeStr 尺寸字符串
+   * @returns 卡片尺寸枚举
+   */
+  public parseSizeFromString(sizeStr: string): WidgetSize {
+    if (this.isValidSize(sizeStr)) {
+      return sizeStr as WidgetSize;
+    }
+    
+    // 尝试从维度字符串解析 (如 "2*1", "4*2", "4*3")
+    switch (sizeStr) {
+      case '2*1':
+      case '2x1':
+        return WidgetSize.SMALL;
+      case '4*2':
+      case '4x2':
+        return WidgetSize.MEDIUM;
+      case '4*3':
+      case '4x3':
+        return WidgetSize.LARGE;
+      default:
+        hilog.warn(0x0000, TAG, `Unknown size string: ${sizeStr}, using medium as default`);
+        return WidgetSize.MEDIUM;
+    }
+  }
+
+  /**
+   * 获取响应式布局参数
+   * @param size 卡片尺寸
+   * @returns 布局参数
+   */
+  public getResponsiveLayoutParams(size: WidgetSize): ResponsiveLayoutParams {
+    switch (size) {
+      case WidgetSize.SMALL:
+        const smallPadding: ContainerPadding = { left: 16, right: 16, top: 8, bottom: 8 };
+        const smallButtonSize: ButtonSize = { width: 32, height: 32 };
+        const smallPlayButtonSize: ButtonSize = { width: 36, height: 36 };
+        const smallFontSize: FontSizeConfig = { title: 14, artist: 12, time: 10 };
+        const smallSpacing: SpacingConfig = { horizontal: 8, vertical: 4 };
+        const smallShowElements: ShowElementsConfig = {
+          progress: false,
+          cover: false,
+          album: false,
+          time: false
+        };
+        return {
+          containerPadding: smallPadding,
+          buttonSize: smallButtonSize,
+          playButtonSize: smallPlayButtonSize,
+          fontSize: smallFontSize,
+          spacing: smallSpacing,
+          showElements: smallShowElements
+        };
+      case WidgetSize.MEDIUM:
+        const mediumPadding: ContainerPadding = { left: 16, right: 16, top: 16, bottom: 16 };
+        const mediumButtonSize: ButtonSize = { width: 36, height: 36 };
+        const mediumPlayButtonSize: ButtonSize = { width: 44, height: 44 };
+        const mediumFontSize: FontSizeConfig = { title: 16, artist: 12, time: 10 };
+        const mediumSpacing: SpacingConfig = { horizontal: 12, vertical: 8 };
+        const mediumShowElements: ShowElementsConfig = {
+          progress: true,
+          cover: false,
+          album: true,
+          time: true
+        };
+        return {
+          containerPadding: mediumPadding,
+          buttonSize: mediumButtonSize,
+          playButtonSize: mediumPlayButtonSize,
+          fontSize: mediumFontSize,
+          spacing: mediumSpacing,
+          showElements: mediumShowElements
+        };
+      case WidgetSize.LARGE:
+        const largePadding: ContainerPadding = { left: 16, right: 16, top: 16, bottom: 16 };
+        const largeButtonSize: ButtonSize = { width: 40, height: 40 };
+        const largePlayButtonSize: ButtonSize = { width: 52, height: 52 };
+        const largeFontSize: FontSizeConfig = { title: 16, artist: 13, time: 11 };
+        const largeSpacing: SpacingConfig = { horizontal: 16, vertical: 16 };
+        const largeShowElements: ShowElementsConfig = {
+          progress: true,
+          cover: true,
+          album: true,
+          time: true
+        };
+        return {
+          containerPadding: largePadding,
+          buttonSize: largeButtonSize,
+          playButtonSize: largePlayButtonSize,
+          fontSize: largeFontSize,
+          spacing: largeSpacing,
+          showElements: largeShowElements
+        };
+      default:
+        return this.getResponsiveLayoutParams(WidgetSize.MEDIUM);
+    }
+  }
+
+  /**
+   * 格式化基础数据
+   */
+  private formatBaseData(widgetData: WidgetData): FormattedWidgetData {
+    return {
+      isPlaying: widgetData.playState.isPlaying,
+      isPaused: widgetData.playState.isPaused,
+      isLoading: widgetData.playState.isLoading,
+      songTitle: widgetData.currentSong.title || '暂无播放',
+      songArtist: widgetData.currentSong.artist || '未知艺术家',
+      songAlbum: widgetData.currentSong.album || '未知专辑',
+      coverImage: widgetData.currentSong.coverImagePath || '',
+      currentTime: widgetData.progress.currentTimeText || '00:00',
+      totalTime: widgetData.progress.totalTimeText || '00:00',
+      progressPercentage: widgetData.progress.percentage || 0,
+      hasNext: widgetData.playlist.hasNext,
+      hasPrevious: widgetData.playlist.hasPrevious,
+      showProgress: widgetData.config.showProgress,
+      showCover: widgetData.config.showCover,
+      widgetSize: widgetData.config.size.toString(),
+      timestamp: Date.now()
+    };
+  }
+
+  /**
+   * 适配小尺寸卡片数据
+   */
+  private adaptForSmallWidget(baseData: FormattedWidgetData, widgetData: WidgetData): FormattedWidgetData {
+    const result: FormattedWidgetData = {
+      isPlaying: baseData.isPlaying,
+      isPaused: baseData.isPaused,
+      isLoading: baseData.isLoading,
+      songTitle: this.truncateText(baseData.songTitle, 20), // 小卡片只显示歌曲标题,如果标题过长则截断
+      songArtist: baseData.songArtist,
+      songAlbum: baseData.songAlbum,
+      coverImage: baseData.coverImage,
+      currentTime: baseData.currentTime,
+      totalTime: baseData.totalTime,
+      progressPercentage: baseData.progressPercentage,
+      hasNext: baseData.hasNext,
+      hasPrevious: baseData.hasPrevious,
+      showProgress: false,
+      showCover: false,
+      widgetSize: WidgetSize.SMALL,
+      timestamp: baseData.timestamp
+    };
+    return result;
+  }
+
+  /**
+   * 适配中等尺寸卡片数据
+   */
+  private adaptForMediumWidget(baseData: FormattedWidgetData, widgetData: WidgetData): FormattedWidgetData {
+    const result: FormattedWidgetData = {
+      isPlaying: baseData.isPlaying,
+      isPaused: baseData.isPaused,
+      isLoading: baseData.isLoading,
+      songTitle: this.truncateText(baseData.songTitle, 30), // 中等卡片显示更多信息,但仍需要适当截断
+      songArtist: this.truncateText(baseData.songArtist, 25),
+      songAlbum: this.truncateText(baseData.songAlbum, 25),
+      coverImage: baseData.coverImage,
+      currentTime: baseData.currentTime,
+      totalTime: baseData.totalTime,
+      progressPercentage: baseData.progressPercentage,
+      hasNext: baseData.hasNext,
+      hasPrevious: baseData.hasPrevious,
+      showProgress: true,
+      showCover: false,
+      widgetSize: WidgetSize.MEDIUM,
+      timestamp: baseData.timestamp
+    };
+    return result;
+  }
+
+  /**
+   * 适配大尺寸卡片数据
+   */
+  private adaptForLargeWidget(baseData: FormattedWidgetData, widgetData: WidgetData): FormattedWidgetData {
+    const result: FormattedWidgetData = {
+      isPlaying: baseData.isPlaying,
+      isPaused: baseData.isPaused,
+      isLoading: baseData.isLoading,
+      songTitle: this.truncateText(baseData.songTitle, 40), // 大卡片可以显示完整信息
+      songArtist: this.truncateText(baseData.songArtist, 35),
+      songAlbum: this.truncateText(baseData.songAlbum, 35),
+      coverImage: baseData.coverImage,
+      currentTime: baseData.currentTime,
+      totalTime: baseData.totalTime,
+      progressPercentage: baseData.progressPercentage,
+      hasNext: baseData.hasNext,
+      hasPrevious: baseData.hasPrevious,
+      showProgress: true,
+      showCover: true,
+      widgetSize: WidgetSize.LARGE,
+      timestamp: baseData.timestamp
+    };
+    return result;
+  }
+
+  /**
+   * 截断文本
+   */
+  private truncateText(text: string, maxLength: number): string {
+    if (!text || text.length <= maxLength) {
+      return text;
+    }
+    return text.substring(0, maxLength - 3) + '...';
+  }
+}

+ 118 - 0
entry/src/main/ets/common/widget/ObjectUtils.ets

@@ -0,0 +1,118 @@
+import { WidgetPersonalConfig, UserPreferences } from './WidgetConfigManager';
+import { ThemeInfo } from './ThemeSyncService';
+
+/**
+ * 对象工具类
+ * 提供ArkTS兼容的对象操作方法
+ */
+export class ObjectUtils {
+  /**
+   * 克隆WidgetPersonalConfig对象
+   */
+  static cloneWidgetConfig(config: WidgetPersonalConfig): WidgetPersonalConfig {
+    return {
+      showProgress: config.showProgress,
+      showCover: config.showCover,
+      showAlbum: config.showAlbum,
+      showTime: config.showTime,
+      theme: config.theme,
+      backgroundColor: config.backgroundColor,
+      textColor: config.textColor,
+      accentColor: config.accentColor,
+      buttonSize: config.buttonSize,
+      fontSize: config.fontSize,
+      borderRadius: config.borderRadius,
+      autoUpdate: config.autoUpdate,
+      updateInterval: config.updateInterval,
+      clickBehavior: config.clickBehavior,
+      enableAnimations: config.enableAnimations,
+      enableHapticFeedback: config.enableHapticFeedback,
+      enableShadow: config.enableShadow
+    };
+  }
+
+  /**
+   * 克隆UserPreferences对象
+   */
+  static cloneUserPreferences(prefs: UserPreferences): UserPreferences {
+    return {
+      defaultTheme: prefs.defaultTheme,
+      defaultSize: prefs.defaultSize,
+      syncWithMainApp: prefs.syncWithMainApp,
+      syncThemeColor: prefs.syncThemeColor,
+      enableCache: prefs.enableCache,
+      cacheExpiry: prefs.cacheExpiry,
+      showSongInfo: prefs.showSongInfo,
+      showArtistInfo: prefs.showArtistInfo,
+      showAlbumCover: prefs.showAlbumCover
+    };
+  }
+
+  /**
+   * 克隆ThemeInfo对象
+   */
+  static cloneThemeInfo(theme: ThemeInfo): ThemeInfo {
+    return {
+      theme: theme.theme,
+      primaryColor: theme.primaryColor,
+      backgroundColor: theme.backgroundColor,
+      textColor: theme.textColor,
+      accentColor: theme.accentColor,
+      isDarkMode: theme.isDarkMode
+    };
+  }
+
+  /**
+   * 合并UserPreferences对象
+   */
+  static mergeUserPreferences(target: UserPreferences, source: Partial<UserPreferences>): UserPreferences {
+    const result = ObjectUtils.cloneUserPreferences(target);
+    
+    if (source.defaultTheme !== undefined) result.defaultTheme = source.defaultTheme;
+    if (source.defaultSize !== undefined) result.defaultSize = source.defaultSize;
+    if (source.syncWithMainApp !== undefined) result.syncWithMainApp = source.syncWithMainApp;
+    if (source.syncThemeColor !== undefined) result.syncThemeColor = source.syncThemeColor;
+    if (source.enableCache !== undefined) result.enableCache = source.enableCache;
+    if (source.cacheExpiry !== undefined) result.cacheExpiry = source.cacheExpiry;
+    if (source.showSongInfo !== undefined) result.showSongInfo = source.showSongInfo;
+    if (source.showArtistInfo !== undefined) result.showArtistInfo = source.showArtistInfo;
+    if (source.showAlbumCover !== undefined) result.showAlbumCover = source.showAlbumCover;
+    
+    return result;
+  }
+
+  /**
+   * 合并ThemeInfo对象
+   */
+  static mergeThemeInfo(target: ThemeInfo, source: Partial<ThemeInfo>): ThemeInfo {
+    const result = ObjectUtils.cloneThemeInfo(target);
+    
+    if (source.theme !== undefined) result.theme = source.theme;
+    if (source.primaryColor !== undefined) result.primaryColor = source.primaryColor;
+    if (source.backgroundColor !== undefined) result.backgroundColor = source.backgroundColor;
+    if (source.textColor !== undefined) result.textColor = source.textColor;
+    if (source.accentColor !== undefined) result.accentColor = source.accentColor;
+    if (source.isDarkMode !== undefined) result.isDarkMode = source.isDarkMode;
+    
+    return result;
+  }
+
+  /**
+   * 通用对象克隆方法
+   */
+  static assign<T>(obj: T): T {
+    if (obj === null || typeof obj !== 'object') {
+      return obj;
+    }
+
+    const cloned: Record<string, Object> = {};
+    const keys = Object.keys(obj);
+    for (let i = 0; i < keys.length; i++) {
+      const key = keys[i];
+      const objRecord = obj as Record<string, Object>;
+      cloned[key] = objRecord[key];
+    }
+    
+    return cloned as T;
+  }
+}

+ 158 - 47
entry/src/main/ets/common/widget/PlayerControlService.ets

@@ -3,6 +3,8 @@ import Want from '@ohos.app.ability.Want';
 import common from '@ohos.app.ability.common';
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { WidgetData, WidgetCommand, EventData, RequestData, WidgetControlParams } from './WidgetTypes';
+import { ObjectUtils } from './ObjectUtils';
+import { WidgetTypeHelpers } from './WidgetTypeHelpers';
 import { 
   WIDGET_CONTROL_EVENT,
   WIDGET_REQUEST_STATE_EVENT,
@@ -12,8 +14,20 @@ import {
   APP_BUNDLE_NAME,
   APP_ABILITY_NAME
 } from './WidgetEventConstants';
+import { AvSessionWidgetListener } from './AvSessionWidgetListener';
+
+const TAG = 'Heanup PlayerControlService';
+
+/**
+ * 启动参数接口
+ */
+interface LaunchParameters {
+  page: string;
+  source: string;
+  timestamp: string;
+}
+
 
-const TAG = 'PlayerControlService';
 
 /**
  * 播放器控制服务
@@ -22,9 +36,12 @@ const TAG = 'PlayerControlService';
 export class PlayerControlService {
   private stateListeners: Array<(data: WidgetData) => void> = [];
   private isListenerRegistered: boolean = false;
+  private avSessionListener: AvSessionWidgetListener;
 
   constructor() {
+    this.avSessionListener = AvSessionWidgetListener.getInstance();
     this.initializeEventListener();
+    this.initializeAvSessionListener();
   }
 
   /**
@@ -96,7 +113,10 @@ export class PlayerControlService {
    */
   async getCurrentPlayState(): Promise<WidgetData> {
     try {
-      // 请求当前状态
+      // 优先从AvSession获取当前状态
+      const avSessionData = this.avSessionListener.getCurrentWidgetData();
+      
+      // 同时请求CommonEvent状态作为备用
       const requestData: RequestData = { timestamp: Date.now() };
       const requestInfo: commonEventManager.CommonEventPublishData = {
         data: JSON.stringify(requestData)
@@ -107,41 +127,94 @@ export class PlayerControlService {
         }
       });
 
-      // 返回默认状态,实际状态会通过事件监听器更新
-      return this.getDefaultWidgetData();
+      return avSessionData;
     } catch (error) {
       hilog.error(0x0000, TAG, `Failed to get current play state: ${error}`);
       return this.getDefaultWidgetData();
     }
   }
 
+  /**
+   * 初始化AvSession监听器
+   */
+  private initializeAvSessionListener(): void {
+    try {
+      // 注册AvSession状态监听器
+      this.avSessionListener.addStateListener((data: WidgetData) => {
+        // 将AvSession的状态变化转发给所有监听器
+        this.stateListeners.forEach((listener: (data: WidgetData) => void) => {
+          try {
+            listener(data);
+          } catch (error) {
+            hilog.error(0x0000, TAG, `Error in AvSession state listener: ${error}`);
+          }
+        });
+      });
+      
+      hilog.info(0x0000, TAG, 'AvSession listener initialized successfully');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to initialize AvSession listener: ${error}`);
+    }
+  }
+
   /**
    * 注册状态变化监听器
    */
   registerStateListener(callback: (data: WidgetData) => void): void {
     this.stateListeners.push(callback);
+    
+    // 立即获取当前AvSession状态并回调
+    try {
+      const currentData = this.avSessionListener.getCurrentWidgetData();
+      callback(currentData);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Error getting current AvSession data: ${error}`);
+    }
+    
     hilog.info(0x0000, TAG, 'State listener registered');
   }
 
   /**
    * 启动主应用
    */
-  async launchMainApp(page?: string): Promise<boolean> {
+  async launchMainApp(page?: string, params?: Record<string, Object>): Promise<boolean> {
     try {
+      interface LaunchParameters {
+        page: string;
+        source: string;
+        timestamp: string;
+      }
+      
+      const baseParams: LaunchParameters = {
+        page: page || 'main',
+        source: 'widget', // 标识来源是卡片
+        timestamp: Date.now().toString()
+      };
+
+      // 创建Want对象
       const want: Want = {
         bundleName: APP_BUNDLE_NAME,
         abilityName: APP_ABILITY_NAME,
         parameters: {
-          page: page || 'main',
-          source: 'widget', // 标识来源是卡片
-          timestamp: Date.now().toString()
+          page: baseParams.page,
+          source: baseParams.source,
+          timestamp: baseParams.timestamp
         }
       };
 
+      // 添加额外参数
+      if (params && want.parameters) {
+        const paramKeys = Object.keys(params);
+        for (let i = 0; i < paramKeys.length; i++) {
+          const key = paramKeys[i];
+          want.parameters[key] = params[key];
+        }
+      }
+
       const context = getContext() as common.UIAbilityContext;
       await context.startAbility(want);
       
-      hilog.info(0x0000, TAG, `Main app launched with page: ${page}`);
+      hilog.info(0x0000, TAG, `Main app launched with page: ${page}, params: ${JSON.stringify(params)}`);
       return true;
     } catch (error) {
       hilog.error(0x0000, TAG, `Failed to launch main app: ${error}`);
@@ -171,7 +244,15 @@ export class PlayerControlService {
   private handlePlayerStateChange(eventData: commonEventManager.CommonEventData): void {
     try {
       const data = JSON.parse(eventData.data || '{}') as Object;
-      const widgetData = this.convertToWidgetData(data);
+      let widgetData: WidgetData;
+
+      if (eventData.event === PLAYER_PROGRESS_CHANGED_EVENT) {
+        // 处理进度更新事件
+        widgetData = this.updateProgressData(data);
+      } else {
+        // 处理完整状态更新事件
+        widgetData = this.convertToWidgetData(data);
+      }
       
       // 通知所有监听器
       this.stateListeners.forEach((listener: (data: WidgetData) => void) => {
@@ -182,58 +263,88 @@ export class PlayerControlService {
         }
       });
       
-      hilog.info(0x0000, TAG, 'Player state change handled');
+      hilog.info(0x0000, TAG, `Player ${eventData.event} handled`);
     } catch (error) {
       hilog.error(0x0000, TAG, `Failed to handle player state change: ${error}`);
     }
   }
 
+  /**
+   * 更新进度数据
+   */
+  private updateProgressData(progressData: Object): WidgetData {
+    try {
+      const data: Record<string, Object> = progressData as Record<string, Object>;
+      const currentData = this.getDefaultWidgetData();
+      
+      // 只更新进度相关数据
+      currentData.progress = {
+        currentPosition: (data['currentPosition'] as number) || 0,
+        duration: (data['duration'] as number) || 0,
+        percentage: (data['percentage'] as number) || 0,
+        currentTimeText: (data['currentTimeText'] as string) || '00:00',
+        totalTimeText: (data['totalTimeText'] as string) || '00:00'
+      };
+      
+      return currentData;
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to update progress data: ${error}`);
+      return this.getDefaultWidgetData();
+    }
+  }
+
   /**
    * 转换播放器数据为卡片数据格式
    */
   private convertToWidgetData(playerData: Object): WidgetData {
-    // 返回默认的卡片数据,避免索引访问
-    return this.getDefaultWidgetData();
+    try {
+      const data: Record<string, Object> = playerData as Record<string, Object>;
+      
+      return {
+        playState: {
+          isPlaying: (data['isPlaying'] as boolean) || false,
+          isPaused: (data['isPaused'] as boolean) || true,
+          isLoading: (data['isLoading'] as boolean) || false
+        },
+        currentSong: {
+          id: ((data['currentSong'] as Record<string, Object>)?.['id'] as string) || '',
+          title: ((data['currentSong'] as Record<string, Object>)?.['title'] as string) || '暂无播放',
+          artist: ((data['currentSong'] as Record<string, Object>)?.['artist'] as string) || '未知艺术家',
+          album: ((data['currentSong'] as Record<string, Object>)?.['album'] as string) || '未知专辑',
+          coverImagePath: ((data['currentSong'] as Record<string, Object>)?.['coverImagePath'] as string) || '',
+          duration: ((data['currentSong'] as Record<string, Object>)?.['duration'] as number) || 0
+        },
+        progress: {
+          currentPosition: ((data['progress'] as Record<string, Object>)?.['currentPosition'] as number) || 0,
+          duration: ((data['progress'] as Record<string, Object>)?.['duration'] as number) || 0,
+          percentage: ((data['progress'] as Record<string, Object>)?.['percentage'] as number) || 0,
+          currentTimeText: ((data['progress'] as Record<string, Object>)?.['currentTimeText'] as string) || '00:00',
+          totalTimeText: ((data['progress'] as Record<string, Object>)?.['totalTimeText'] as string) || '00:00'
+        },
+        playlist: {
+          hasNext: ((data['playlist'] as Record<string, Object>)?.['hasNext'] as boolean) || false,
+          hasPrevious: ((data['playlist'] as Record<string, Object>)?.['hasPrevious'] as boolean) || false,
+          currentIndex: ((data['playlist'] as Record<string, Object>)?.['currentIndex'] as number) || 0,
+          totalCount: ((data['playlist'] as Record<string, Object>)?.['totalCount'] as number) || 0
+        },
+        config: {
+          size: 'medium',
+          theme: 'auto',
+          showProgress: true,
+          showCover: true
+        }
+      };
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to convert player data: ${error}`);
+      return this.getDefaultWidgetData();
+    }
   }
 
   /**
    * 获取默认卡片数据
    */
   private getDefaultWidgetData(): WidgetData {
-    return {
-      playState: {
-        isPlaying: false,
-        isPaused: true,
-        isLoading: false
-      },
-      currentSong: {
-        id: '',
-        title: '暂无播放',
-        artist: '未知艺术家',
-        album: '未知专辑',
-        coverImagePath: '',
-        duration: 0
-      },
-      progress: {
-        currentPosition: 0,
-        duration: 0,
-        percentage: 0,
-        currentTimeText: '00:00',
-        totalTimeText: '00:00'
-      },
-      playlist: {
-        hasNext: false,
-        hasPrevious: false,
-        currentIndex: 0,
-        totalCount: 0
-      },
-      config: {
-        size: 'medium',
-        theme: 'auto',
-        showProgress: true,
-        showCover: true
-      }
-    };
+    return WidgetTypeHelpers.createDefaultWidgetData();
   }
 
   /**

+ 11 - 0
entry/src/main/ets/common/widget/PlayerStateBroadcastData.ets

@@ -0,0 +1,11 @@
+import { PlayState, SongInfo, PlayProgress, PlaylistState } from './WidgetTypes';
+
+/**
+ * 播放器状态广播数据接口
+ */
+export interface PlayerStateBroadcastData {
+  playState: PlayState;
+  currentSong: SongInfo;
+  progress: PlayProgress;
+  playlist: PlaylistState;
+}

+ 376 - 0
entry/src/main/ets/common/widget/ThemeSyncService.ets

@@ -0,0 +1,376 @@
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { WidgetTheme, WidgetSize } from './WidgetTypes';
+import { WidgetConfigManager, WidgetPersonalConfig } from './WidgetConfigManager';
+import commonEventManager from '@ohos.commonEventManager';
+import { ObjectUtils } from './ObjectUtils';
+
+const TAG = 'ThemeSyncService';
+
+/**
+ * 主题信息接口
+ */
+export interface ThemeInfo {
+  theme: WidgetTheme;
+  primaryColor: string;
+  backgroundColor: string;
+  textColor: string;
+  accentColor: string;
+  isDarkMode: boolean;
+}
+
+/**
+ * 主题变化事件接口
+ */
+export interface ThemeChangeEvent {
+  type: 'theme_changed';
+  themeInfo: ThemeInfo;
+  timestamp: number;
+}
+
+/**
+ * 主题变化事件数据接口
+ */
+export interface ThemeChangeEventData {
+  theme: WidgetTheme;
+  colors: ThemeColors;
+}
+
+/**
+ * 主题颜色接口
+ */
+export interface ThemeColors {
+  backgroundColor: string;
+  textColor: string;
+  accentColor: string;
+}
+
+/**
+ * 主题同步服务
+ * 负责与主应用的主题色彩同步
+ * 需求: 5.4, 6.3
+ */
+export class ThemeSyncService {
+  private static instance: ThemeSyncService;
+  private configManager: WidgetConfigManager;
+  private currentTheme: ThemeInfo | null = null;
+  private themeChangeListeners: Set<ThemeChangeListener> = new Set();
+  private isListening: boolean = false;
+
+  /**
+   * 获取单例实例
+   */
+  public static getInstance(): ThemeSyncService {
+    if (!ThemeSyncService.instance) {
+      ThemeSyncService.instance = new ThemeSyncService();
+    }
+    return ThemeSyncService.instance;
+  }
+
+  private constructor() {
+    this.configManager = WidgetConfigManager.getInstance();
+    hilog.info(0x0000, TAG, 'ThemeSyncService initialized');
+  }
+
+  /**
+   * 启动主题同步监听
+   */
+  public async startThemeSync(): Promise<void> {
+    if (this.isListening) {
+      hilog.warn(0x0000, TAG, 'Theme sync already started');
+      return;
+    }
+
+    try {
+      // 订阅主应用主题变化事件
+      await this.subscribeToThemeChanges();
+      
+      // 获取当前主题
+      await this.loadCurrentTheme();
+      
+      this.isListening = true;
+      hilog.info(0x0000, TAG, 'Theme sync started successfully');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to start theme sync: ${error}`);
+    }
+  }
+
+  /**
+   * 停止主题同步监听
+   */
+  public async stopThemeSync(): Promise<void> {
+    if (!this.isListening) {
+      return;
+    }
+
+    try {
+      // 取消订阅主题变化事件
+      await this.unsubscribeFromThemeChanges();
+      
+      this.isListening = false;
+      hilog.info(0x0000, TAG, 'Theme sync stopped');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to stop theme sync: ${error}`);
+    }
+  }
+
+  /**
+   * 获取当前主题信息
+   */
+  public getCurrentTheme(): ThemeInfo | null {
+    return this.currentTheme;
+  }
+
+  /**
+   * 手动同步主题
+   */
+  public async syncTheme(): Promise<void> {
+    try {
+      const themeInfo = await this.fetchThemeFromMainApp();
+      if (themeInfo) {
+        await this.applyThemeToAllWidgets(themeInfo);
+        hilog.info(0x0000, TAG, 'Theme synced manually');
+      }
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to sync theme manually: ${error}`);
+    }
+  }
+
+  /**
+   * 注册主题变化监听器
+   * @param listener 监听器
+   */
+  public addThemeChangeListener(listener: ThemeChangeListener): void {
+    this.themeChangeListeners.add(listener);
+    hilog.info(0x0000, TAG, 'Theme change listener added');
+  }
+
+  /**
+   * 移除主题变化监听器
+   * @param listener 监听器
+   */
+  public removeThemeChangeListener(listener: ThemeChangeListener): void {
+    this.themeChangeListeners.delete(listener);
+    hilog.info(0x0000, TAG, 'Theme change listener removed');
+  }
+
+  /**
+   * 应用主题到指定卡片
+   * @param formId 卡片ID
+   * @param themeInfo 主题信息
+   */
+  public async applyThemeToWidget(formId: string, themeInfo: ThemeInfo): Promise<void> {
+    try {
+      // 获取当前配置
+      const currentConfig = await this.configManager.getWidgetConfig(formId, WidgetSize.MEDIUM);
+      
+      // 应用主题色彩
+      const updatedConfig: WidgetPersonalConfig = ObjectUtils.cloneWidgetConfig(currentConfig);
+      updatedConfig.theme = themeInfo.theme;
+      updatedConfig.backgroundColor = themeInfo.backgroundColor;
+      updatedConfig.textColor = themeInfo.textColor;
+      updatedConfig.accentColor = themeInfo.accentColor;
+      
+      // 保存更新后的配置
+      await this.configManager.saveWidgetConfig(formId, WidgetSize.MEDIUM, updatedConfig);
+      
+      hilog.info(0x0000, TAG, `Theme applied to widget: ${formId}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to apply theme to widget ${formId}: ${error}`);
+    }
+  }
+
+  /**
+   * 获取系统主题信息
+   */
+  public async getSystemTheme(): Promise<ThemeInfo> {
+    try {
+      // 从系统获取主题信息
+      const isDarkMode = await this.isSystemDarkMode();
+      
+      return {
+        theme: isDarkMode ? WidgetTheme.DARK : WidgetTheme.LIGHT,
+        primaryColor: isDarkMode ? '#FFFFFF' : '#000000',
+        backgroundColor: isDarkMode ? '#1C1C1E' : '#FFFFFF',
+        textColor: isDarkMode ? '#FFFFFF' : '#000000',
+        accentColor: '#FF007DFF',
+        isDarkMode: isDarkMode
+      };
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to get system theme: ${error}`);
+      return this.getDefaultTheme();
+    }
+  }
+
+  /**
+   * 创建自定义主题
+   * @param customColors 自定义颜色
+   */
+  public createCustomTheme(customColors: Partial<ThemeInfo>): ThemeInfo {
+    const defaultTheme = this.getDefaultTheme();
+    
+    const customTheme: ThemeInfo = ObjectUtils.mergeThemeInfo(defaultTheme, customColors);
+    customTheme.theme = WidgetTheme.LIGHT; // 自定义主题默认为浅色
+    return customTheme;
+  }
+
+  /**
+   * 订阅主题变化事件
+   */
+  private async subscribeToThemeChanges(): Promise<void> {
+    try {
+      const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
+        events: ['usual.event.THEME_CHANGED', 'usual.event.DARK_MODE_CHANGED']
+      };
+      
+      const subscriber = await commonEventManager.createSubscriber(subscribeInfo);
+      await commonEventManager.subscribe(subscriber, (err, data) => {
+        if (err) {
+          hilog.error(0x0000, TAG, `Theme change subscription error: ${err}`);
+          return;
+        }
+        
+        this.handleThemeChangeEvent(data);
+      });
+      
+      hilog.info(0x0000, TAG, 'Subscribed to theme change events');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to subscribe to theme changes: ${error}`);
+    }
+  }
+
+  /**
+   * 取消订阅主题变化事件
+   */
+  private async unsubscribeFromThemeChanges(): Promise<void> {
+    try {
+      // 这里应该取消订阅,但由于API限制,暂时留空
+      hilog.info(0x0000, TAG, 'Unsubscribed from theme change events');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to unsubscribe from theme changes: ${error}`);
+    }
+  }
+
+  /**
+   * 处理主题变化事件
+   */
+  private async handleThemeChangeEvent(eventData: commonEventManager.CommonEventData): Promise<void> {
+    try {
+      hilog.info(0x0000, TAG, 'Theme change event received');
+      
+      // 重新加载主题
+      await this.loadCurrentTheme();
+      
+      // 通知监听器
+      if (this.currentTheme) {
+        this.notifyThemeChangeListeners(this.currentTheme);
+      }
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to handle theme change event: ${error}`);
+    }
+  }
+
+  /**
+   * 加载当前主题
+   */
+  private async loadCurrentTheme(): Promise<void> {
+    try {
+      const themeInfo = await this.fetchThemeFromMainApp();
+      if (themeInfo) {
+        this.currentTheme = themeInfo;
+        hilog.info(0x0000, TAG, 'Current theme loaded');
+      }
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to load current theme: ${error}`);
+      this.currentTheme = this.getDefaultTheme();
+    }
+  }
+
+  /**
+   * 从主应用获取主题信息
+   */
+  private async fetchThemeFromMainApp(): Promise<ThemeInfo | null> {
+    try {
+      // 这里应该从主应用的主题管理器获取主题信息
+      // 暂时返回系统主题
+      return await this.getSystemTheme();
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to fetch theme from main app: ${error}`);
+      return null;
+    }
+  }
+
+  /**
+   * 应用主题到所有卡片
+   */
+  private async applyThemeToAllWidgets(themeInfo: ThemeInfo): Promise<void> {
+    try {
+      await this.configManager.updateAllWidgetConfigs((config) => {
+        const updatedConfig = ObjectUtils.cloneWidgetConfig(config);
+        updatedConfig.theme = themeInfo.theme;
+        updatedConfig.backgroundColor = themeInfo.backgroundColor;
+        updatedConfig.textColor = themeInfo.textColor;
+        updatedConfig.accentColor = themeInfo.accentColor;
+        return updatedConfig;
+      });
+      
+      hilog.info(0x0000, TAG, 'Theme applied to all widgets');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to apply theme to all widgets: ${error}`);
+    }
+  }
+
+  /**
+   * 通知主题变化监听器
+   */
+  private notifyThemeChangeListeners(themeInfo: ThemeInfo): void {
+    const event: ThemeChangeEvent = {
+      type: 'theme_changed',
+      themeInfo: themeInfo,
+      timestamp: Date.now()
+    };
+    
+    const listeners = Array.from(this.themeChangeListeners);
+    for (let i = 0; i < listeners.length; i++) {
+      try {
+        listeners[i].onThemeChanged(event);
+      } catch (error) {
+        hilog.error(0x0000, TAG, `Error notifying theme change listener: ${error}`);
+      }
+    }
+  }
+
+  /**
+   * 检查系统是否为深色模式
+   */
+  private async isSystemDarkMode(): Promise<boolean> {
+    try {
+      // 这里应该检查系统的深色模式设置
+      // 暂时返回false(浅色模式)
+      return false;
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to check system dark mode: ${error}`);
+      return false;
+    }
+  }
+
+  /**
+   * 获取默认主题
+   */
+  private getDefaultTheme(): ThemeInfo {
+    return {
+      theme: WidgetTheme.LIGHT,
+      primaryColor: '#000000',
+      backgroundColor: '#FFFFFF',
+      textColor: '#E6000000',
+      accentColor: '#FF007DFF',
+      isDarkMode: false
+    };
+  }
+}
+
+/**
+ * 主题变化监听器接口
+ */
+export interface ThemeChangeListener {
+  onThemeChanged(event: ThemeChangeEvent): void;
+}

+ 445 - 0
entry/src/main/ets/common/widget/WidgetConfigManager.ets

@@ -0,0 +1,445 @@
+import preferences from '@ohos.data.preferences';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { WidgetSize, WidgetTheme, WidgetConfig } from './WidgetTypes';
+import { ObjectUtils } from './ObjectUtils';
+
+const TAG = 'WidgetConfigManager';
+const CONFIG_PREFERENCES_NAME = 'widget_config_prefs';
+
+/**
+ * 卡片个性化配置接口
+ */
+export interface WidgetPersonalConfig {
+  // 显示配置
+  showProgress: boolean;
+  showCover: boolean;
+  showAlbum: boolean;
+  showTime: boolean;
+  
+  // 主题配置
+  theme: WidgetTheme;
+  backgroundColor: string;
+  textColor: string;
+  accentColor: string;
+  
+  // 布局配置
+  buttonSize: 'small' | 'medium' | 'large';
+  fontSize: 'small' | 'medium' | 'large';
+  borderRadius: number;
+  
+  // 行为配置
+  autoUpdate: boolean;
+  updateInterval: number;
+  clickBehavior: 'play_pause' | 'open_app' | 'open_player';
+  
+  // 高级配置
+  enableAnimations: boolean;
+  enableHapticFeedback: boolean;
+  enableShadow: boolean;
+}
+
+/**
+ * 用户偏好设置接口
+ */
+export interface UserPreferences {
+  // 全局偏好
+  defaultTheme: WidgetTheme;
+  defaultSize: WidgetSize;
+  
+  // 同步设置
+  syncWithMainApp: boolean;
+  syncThemeColor: boolean;
+  
+  // 性能设置
+  enableCache: boolean;
+  cacheExpiry: number;
+  
+  // 隐私设置
+  showSongInfo: boolean;
+  showArtistInfo: boolean;
+  showAlbumCover: boolean;
+}
+
+/**
+ * 卡片配置管理器
+ * 负责卡片个性化设置和用户偏好管理
+ * 需求: 5.4, 6.3
+ */
+export class WidgetConfigManager {
+  private static instance: WidgetConfigManager;
+  private preferencesStore: preferences.Preferences | null = null;
+  private configCache: Map<string, WidgetPersonalConfig> = new Map();
+  private userPreferences: UserPreferences | null = null;
+
+  /**
+   * 获取单例实例
+   */
+  public static getInstance(): WidgetConfigManager {
+    if (!WidgetConfigManager.instance) {
+      WidgetConfigManager.instance = new WidgetConfigManager();
+    }
+    return WidgetConfigManager.instance;
+  }
+
+  private constructor() {
+    this.initPreferences();
+    hilog.info(0x0000, TAG, 'WidgetConfigManager initialized');
+  }
+
+  /**
+   * 初始化配置存储
+   */
+  private async initPreferences(): Promise<void> {
+    try {
+      this.preferencesStore = await preferences.getPreferences(getContext(), CONFIG_PREFERENCES_NAME);
+      await this.loadUserPreferences();
+      hilog.info(0x0000, TAG, 'Config preferences initialized successfully');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to initialize config preferences: ${error}`);
+    }
+  }
+
+  /**
+   * 获取卡片个性化配置
+   * @param formId 卡片ID
+   * @param size 卡片尺寸
+   * @returns 个性化配置
+   */
+  public async getWidgetConfig(formId: string, size: WidgetSize): Promise<WidgetPersonalConfig> {
+    // 先从缓存获取
+    const cacheKey = `${formId}_${size}`;
+    if (this.configCache.has(cacheKey)) {
+      return this.configCache.get(cacheKey)!;
+    }
+
+    try {
+      if (!this.preferencesStore) {
+        await this.initPreferences();
+      }
+
+      const configKey = `config_${formId}`;
+      const configStr = await this.preferencesStore?.get(configKey, '') as string;
+      
+      let config: WidgetPersonalConfig;
+      if (configStr) {
+        config = JSON.parse(configStr) as WidgetPersonalConfig;
+      } else {
+        config = this.getDefaultConfig(size);
+      }
+
+      // 应用主题色彩同步
+      if (this.userPreferences?.syncThemeColor) {
+        config = await this.syncThemeColors(config);
+      }
+
+      // 缓存配置
+      this.configCache.set(cacheKey, config);
+      
+      hilog.info(0x0000, TAG, `Widget config loaded for form: ${formId}`);
+      return config;
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to get widget config: ${error}`);
+      return this.getDefaultConfig(size);
+    }
+  }
+
+  /**
+   * 保存卡片个性化配置
+   * @param formId 卡片ID
+   * @param size 卡片尺寸
+   * @param config 配置对象
+   */
+  public async saveWidgetConfig(formId: string, size: WidgetSize, config: WidgetPersonalConfig): Promise<void> {
+    try {
+      if (!this.preferencesStore) {
+        await this.initPreferences();
+      }
+
+      const configKey = `config_${formId}`;
+      await this.preferencesStore?.put(configKey, JSON.stringify(config));
+      await this.preferencesStore?.flush();
+
+      // 更新缓存
+      const cacheKey = `${formId}_${size}`;
+      this.configCache.set(cacheKey, config);
+
+      hilog.info(0x0000, TAG, `Widget config saved for form: ${formId}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to save widget config: ${error}`);
+    }
+  }
+
+  /**
+   * 获取用户偏好设置
+   */
+  public async getUserPreferences(): Promise<UserPreferences> {
+    if (this.userPreferences) {
+      return this.userPreferences;
+    }
+
+    await this.loadUserPreferences();
+    return this.userPreferences || this.getDefaultUserPreferences();
+  }
+
+  /**
+   * 保存用户偏好设置
+   * @param preferences 用户偏好
+   */
+  public async saveUserPreferences(preferences: UserPreferences): Promise<void> {
+    try {
+      if (!this.preferencesStore) {
+        await this.initPreferences();
+      }
+
+      await this.preferencesStore?.put('user_preferences', JSON.stringify(preferences));
+      await this.preferencesStore?.flush();
+
+      this.userPreferences = preferences;
+
+      hilog.info(0x0000, TAG, 'User preferences saved successfully');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to save user preferences: ${error}`);
+    }
+  }
+
+  /**
+   * 同步主题色彩
+   * @param config 当前配置
+   * @returns 同步后的配置
+   */
+  public async syncThemeColors(config: WidgetPersonalConfig): Promise<WidgetPersonalConfig> {
+    try {
+      // 从主应用获取主题色彩
+      const mainAppTheme = await this.getMainAppTheme();
+      
+      if (mainAppTheme) {
+        const updatedConfig = ObjectUtils.cloneWidgetConfig(config);
+        updatedConfig.theme = mainAppTheme.theme;
+        updatedConfig.backgroundColor = mainAppTheme.backgroundColor;
+        updatedConfig.textColor = mainAppTheme.textColor;
+        updatedConfig.accentColor = mainAppTheme.accentColor;
+        return updatedConfig;
+      }
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to sync theme colors: ${error}`);
+    }
+
+    return config;
+  }
+
+  /**
+   * 重置卡片配置为默认值
+   * @param formId 卡片ID
+   * @param size 卡片尺寸
+   */
+  public async resetWidgetConfig(formId: string, size: WidgetSize): Promise<void> {
+    const defaultConfig = this.getDefaultConfig(size);
+    await this.saveWidgetConfig(formId, size, defaultConfig);
+    
+    hilog.info(0x0000, TAG, `Widget config reset to default for form: ${formId}`);
+  }
+
+  /**
+   * 批量更新所有卡片配置
+   * @param updateFn 更新函数
+   */
+  public async updateAllWidgetConfigs(updateFn: (config: WidgetPersonalConfig) => WidgetPersonalConfig): Promise<void> {
+    try {
+      if (!this.preferencesStore) {
+        await this.initPreferences();
+      }
+
+      const allKeys = await this.preferencesStore?.getAll();
+      const allKeysRecord: Record<string, Object> = allKeys as Record<string, Object> || {};
+      const configKeys = Object.keys(allKeysRecord).filter(key => key.startsWith('config_'));
+
+      for (let i = 0; i < configKeys.length; i++) {
+        const key = configKeys[i];
+        const configStr = allKeysRecord[key] as string;
+        if (configStr) {
+          const config = JSON.parse(configStr) as WidgetPersonalConfig;
+          const updatedConfig = updateFn(config);
+          await this.preferencesStore?.put(key, JSON.stringify(updatedConfig));
+        }
+      }
+
+      await this.preferencesStore?.flush();
+      
+      // 清除缓存以强制重新加载
+      this.configCache.clear();
+
+      hilog.info(0x0000, TAG, `Updated ${configKeys.length} widget configs`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to update all widget configs: ${error}`);
+    }
+  }
+
+  /**
+   * 删除卡片配置
+   * @param formId 卡片ID
+   */
+  public async removeWidgetConfig(formId: string): Promise<void> {
+    try {
+      if (!this.preferencesStore) {
+        await this.initPreferences();
+      }
+
+      const configKey = `config_${formId}`;
+      await this.preferencesStore?.delete(configKey);
+      await this.preferencesStore?.flush();
+
+      // 清除相关缓存
+      const keysToRemove: string[] = [];
+      const cacheKeys = Array.from(this.configCache.keys());
+      for (let i = 0; i < cacheKeys.length; i++) {
+        const key = cacheKeys[i];
+        if (key.startsWith(formId)) {
+          keysToRemove.push(key);
+        }
+      }
+      for (let i = 0; i < keysToRemove.length; i++) {
+        this.configCache.delete(keysToRemove[i]);
+      }
+
+      hilog.info(0x0000, TAG, `Widget config removed for form: ${formId}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to remove widget config: ${error}`);
+    }
+  }
+
+  /**
+   * 获取默认配置
+   */
+  private getDefaultConfig(size: WidgetSize): WidgetPersonalConfig {
+    const baseConfig: WidgetPersonalConfig = {
+      // 显示配置
+      showProgress: true,
+      showCover: true,
+      showAlbum: true,
+      showTime: true,
+      
+      // 主题配置
+      theme: WidgetTheme.AUTO,
+      backgroundColor: '#FFFFFF',
+      textColor: '#E6000000',
+      accentColor: '#FF007DFF',
+      
+      // 布局配置
+      buttonSize: 'medium',
+      fontSize: 'medium',
+      borderRadius: 12,
+      
+      // 行为配置
+      autoUpdate: true,
+      updateInterval: 1000,
+      clickBehavior: 'play_pause',
+      
+      // 高级配置
+      enableAnimations: true,
+      enableHapticFeedback: true,
+      enableShadow: true
+    };
+
+    // 根据尺寸调整默认配置
+    if (size === WidgetSize.SMALL) {
+      const smallConfig = ObjectUtils.cloneWidgetConfig(baseConfig);
+      smallConfig.showProgress = false;
+      smallConfig.showCover = false;
+      smallConfig.showAlbum = false;
+      smallConfig.showTime = false;
+      smallConfig.buttonSize = 'small';
+      smallConfig.fontSize = 'small';
+      smallConfig.borderRadius = 8;
+      return smallConfig;
+    } else if (size === WidgetSize.MEDIUM) {
+      const mediumConfig = ObjectUtils.cloneWidgetConfig(baseConfig);
+      mediumConfig.showCover = false;
+      mediumConfig.buttonSize = 'medium';
+      mediumConfig.fontSize = 'medium';
+      mediumConfig.borderRadius = 12;
+      return mediumConfig;
+    } else if (size === WidgetSize.LARGE) {
+      const largeConfig = ObjectUtils.cloneWidgetConfig(baseConfig);
+      largeConfig.buttonSize = 'large';
+      largeConfig.fontSize = 'large';
+      largeConfig.borderRadius = 16;
+      return largeConfig;
+    } else {
+      return baseConfig;
+    }
+  }
+
+  /**
+   * 获取默认用户偏好
+   */
+  private getDefaultUserPreferences(): UserPreferences {
+    return {
+      // 全局偏好
+      defaultTheme: WidgetTheme.AUTO,
+      defaultSize: WidgetSize.MEDIUM,
+      
+      // 同步设置
+      syncWithMainApp: true,
+      syncThemeColor: true,
+      
+      // 性能设置
+      enableCache: true,
+      cacheExpiry: 30000,
+      
+      // 隐私设置
+      showSongInfo: true,
+      showArtistInfo: true,
+      showAlbumCover: true
+    };
+  }
+
+  /**
+   * 加载用户偏好设置
+   */
+  private async loadUserPreferences(): Promise<void> {
+    try {
+      if (!this.preferencesStore) {
+        return;
+      }
+
+      const prefsStr = await this.preferencesStore.get('user_preferences', '') as string;
+      if (prefsStr) {
+        this.userPreferences = JSON.parse(prefsStr) as UserPreferences;
+      } else {
+        this.userPreferences = this.getDefaultUserPreferences();
+      }
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to load user preferences: ${error}`);
+      this.userPreferences = this.getDefaultUserPreferences();
+    }
+  }
+
+  /**
+   * 从主应用获取主题信息
+   */
+  private async getMainAppTheme(): Promise<MainAppTheme | null> {
+    try {
+      // 这里应该从主应用的主题管理器获取主题信息
+      // 暂时返回默认主题
+      return {
+        theme: WidgetTheme.AUTO,
+        backgroundColor: '#FFFFFF',
+        textColor: '#E6000000',
+        accentColor: '#FF007DFF'
+      };
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to get main app theme: ${error}`);
+      return null;
+    }
+  }
+}
+
+/**
+ * 主应用主题接口
+ */
+interface MainAppTheme {
+  theme: WidgetTheme;
+  backgroundColor: string;
+  textColor: string;
+  accentColor: string;
+}

+ 63 - 3
entry/src/main/ets/common/widget/WidgetController.ets

@@ -5,7 +5,11 @@ import {
   PREV_SONG_EVENT, 
   SEEK_TO_EVENT, 
   OPEN_APP_EVENT, 
-  OPEN_PLAYER_EVENT 
+  OPEN_PLAYER_EVENT,
+  LONG_PRESS_MENU_EVENT,
+  WIDGET_SETTINGS_EVENT,
+  WIDGET_DELETE_EVENT,
+  WIDGET_CONFIG_PAGE_EVENT
 } from './WidgetEventConstants';
 
 /**
@@ -14,7 +18,7 @@ import {
 function postCardAction(context: Object, data: WidgetActionData): void {
   try {
     const message: string = JSON.stringify(data);
-    console.info(`Posting card action: ${message}`);
+    console.info(`Posting card action: ${message}`);//Posting card action: {"action":"play_pause","params":{}}
     
     const globalObj: ESObject = globalThis as ESObject;
     if (globalObj.postCardAction && typeof globalObj.postCardAction === 'function') {
@@ -111,7 +115,7 @@ export class WidgetController {
    * 处理打开播放器页面点击
    */
   static handleOpenPlayer(context: Object): void {
-    console.info('Open player clicked');
+    console.info('Heanup Open player clicked');
     
     const actionData: WidgetActionData = {
       action: OPEN_PLAYER_EVENT,
@@ -165,4 +169,60 @@ export class WidgetController {
   static getPlayButtonIcon(isPlaying: boolean): Resource {
     return isPlaying ? $r('app.media.hm_pause') : $r('app.media.hm_play');
   }
+
+  /**
+   * 处理长按菜单显示
+   */
+  static handleLongPressMenu(context: Object): void {
+    console.info('Long press menu triggered');
+    
+    const actionData: WidgetActionData = {
+      action: LONG_PRESS_MENU_EVENT,
+      params: new Object()
+    };
+    
+    postCardAction(context, actionData);
+  }
+
+  /**
+   * 处理卡片设置点击
+   */
+  static handleWidgetSettings(context: Object): void {
+    console.info('Widget settings clicked');
+    
+    const actionData: WidgetActionData = {
+      action: WIDGET_SETTINGS_EVENT,
+      params: new Object()
+    };
+    
+    postCardAction(context, actionData);
+  }
+
+  /**
+   * 处理卡片删除点击
+   */
+  static handleWidgetDelete(context: Object): void {
+    console.info('Widget delete clicked');
+    
+    const actionData: WidgetActionData = {
+      action: WIDGET_DELETE_EVENT,
+      params: new Object()
+    };
+    
+    postCardAction(context, actionData);
+  }
+
+  /**
+   * 处理卡片配置界面跳转
+   */
+  static handleWidgetConfigPage(context: Object): void {
+    console.info('Widget config page clicked');
+    
+    const actionData: WidgetActionData = {
+      action: WIDGET_CONFIG_PAGE_EVENT,
+      params: new Object()
+    };
+    
+    postCardAction(context, actionData);
+  }
 }

+ 81 - 6
entry/src/main/ets/common/widget/WidgetDataManager.ets

@@ -87,7 +87,7 @@ export class WidgetDataManager {
   /**
    * 保存卡片数据
    */
-  async saveWidgetData(formId: string, data: WidgetData): Promise<void> {
+  async saveWidgetData(formId: string, data: WidgetData | FormattedWidgetData): Promise<void> {
     try {
       if (!this.preferencesStore) {
         await this.initPreferences();
@@ -129,13 +129,24 @@ export class WidgetDataManager {
   /**
    * 更新卡片显示
    */
-  async updateWidget(formId: string, data: WidgetData): Promise<void> {
+  async updateWidget(formId: string, data: WidgetData | FormattedWidgetData): Promise<void> {
     try {
-      // 保存数据到本地存储
-      await this.saveWidgetData(formId, data);
+      let formattedData: FormattedWidgetData;
       
-      // 格式化数据用于卡片显示
-      const formattedData = this.formatDataForWidget(data);
+      // 检查数据类型,如果已经是格式化数据则直接使用
+      if (this.isFormattedWidgetData(data)) {
+        formattedData = data as FormattedWidgetData;
+        
+        // 如果是格式化数据,需要转换回WidgetData进行存储
+        const widgetData = this.convertToWidgetData(formattedData);
+        await this.saveWidgetData(formId, widgetData);
+      } else {
+        // 保存原始数据到本地存储
+        await this.saveWidgetData(formId, data as WidgetData);
+        
+        // 格式化数据用于卡片显示
+        formattedData = this.formatDataForWidget(data as WidgetData);
+      }
       
       // 创建卡片绑定数据
       const formData = formBindingData.createFormBindingData(formattedData);
@@ -395,4 +406,68 @@ export class WidgetDataManager {
       hilog.error(0x0000, TAG, `Failed to preload cache: ${error}`);
     }
   }
+
+  /**
+   * 检查是否为格式化的卡片数据
+   */
+  private isFormattedWidgetData(data: WidgetData | FormattedWidgetData): boolean {
+    // FormattedWidgetData有timestamp字段,而WidgetData没有
+    return (data as FormattedWidgetData).timestamp !== undefined && typeof (data as FormattedWidgetData).timestamp === 'number';
+  }
+
+  /**
+   * 将格式化数据转换为WidgetData
+   */
+  private convertToWidgetData(formattedData: FormattedWidgetData): WidgetData {
+    const widgetData: WidgetData = {
+      playState: {
+        isPlaying: formattedData.isPlaying,
+        isPaused: formattedData.isPaused,
+        isLoading: formattedData.isLoading
+      },
+      currentSong: {
+        id: '', // FormattedWidgetData中没有id,使用空字符串
+        title: formattedData.songTitle,
+        artist: formattedData.songArtist,
+        album: formattedData.songAlbum,
+        coverImagePath: formattedData.coverImage,
+        duration: 0 // FormattedWidgetData中没有duration,使用0
+      },
+      progress: {
+        currentPosition: 0, // 需要从时间文本反推,这里简化处理
+        duration: 0,
+        percentage: formattedData.progressPercentage,
+        currentTimeText: formattedData.currentTime,
+        totalTimeText: formattedData.totalTime
+      },
+      playlist: {
+        hasNext: formattedData.hasNext,
+        hasPrevious: formattedData.hasPrevious,
+        currentIndex: 0,
+        totalCount: 0
+      },
+      config: {
+        size: this.parseWidgetSize(formattedData.widgetSize),
+        theme: WidgetTheme.AUTO,
+        showProgress: formattedData.showProgress,
+        showCover: formattedData.showCover
+      }
+    };
+    return widgetData;
+  }
+
+  /**
+   * 解析卡片尺寸字符串
+   */
+  private parseWidgetSize(sizeStr: string): WidgetSize {
+    switch (sizeStr.toLowerCase()) {
+      case 'small':
+        return WidgetSize.SMALL;
+      case 'large':
+        return WidgetSize.LARGE;
+      case 'medium':
+      default:
+        return WidgetSize.MEDIUM;
+    }
+  }
 }

+ 7 - 1
entry/src/main/ets/common/widget/WidgetEventConstants.ets

@@ -12,6 +12,12 @@ export const SEEK_TO_EVENT = 'seek_to';
 export const OPEN_APP_EVENT = 'open_app';
 export const OPEN_PLAYER_EVENT = 'open_player';
 
+// 长按配置事件
+export const LONG_PRESS_MENU_EVENT = 'long_press_menu';
+export const WIDGET_SETTINGS_EVENT = 'widget_settings';
+export const WIDGET_DELETE_EVENT = 'widget_delete';
+export const WIDGET_CONFIG_PAGE_EVENT = 'widget_config_page';
+
 // 其他事件
 export const REFRESH_DATA_EVENT = 'refresh_data';
 export const TOGGLE_FAVORITE_EVENT = 'toggle_favorite';
@@ -34,7 +40,7 @@ export const PLAYER_SONG_CHANGED_EVENT = 'com.ttmusic.player.song.changed';
 export const PLAYER_PROGRESS_CHANGED_EVENT = 'com.ttmusic.player.progress.changed';
 
 // 应用信息
-export const APP_BUNDLE_NAME = 'com.ttmusic.app';
+export const APP_BUNDLE_NAME = 'com.xgplayer.ttmusic.hm';
 export const APP_ABILITY_NAME = 'EntryAbility';
 
 // 页面路由

+ 302 - 0
entry/src/main/ets/common/widget/WidgetSizeAdapter.ets

@@ -0,0 +1,302 @@
+import { WidgetSize, WidgetData, FormattedWidgetData, WidgetConfig, MaxTextLengthConfig } from './WidgetTypes';
+import { FormLayoutManager } from './FormLayoutManager';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import Want from '@ohos.app.ability.Want';
+
+const TAG = 'Heanup-WidgetSizeAdapter';
+
+/**
+ * 卡片尺寸适配器
+ * 处理卡片尺寸变化时的UI重构逻辑
+ * 需求: 5.4, 5.5
+ */
+export class WidgetSizeAdapter {
+  private static instance: WidgetSizeAdapter;
+  private layoutManager: FormLayoutManager;
+  private sizeChangeListeners: Map<string, SizeChangeListener> = new Map();
+
+  /**
+   * 获取单例实例
+   */
+  public static getInstance(): WidgetSizeAdapter {
+    if (!WidgetSizeAdapter.instance) {
+      WidgetSizeAdapter.instance = new WidgetSizeAdapter();
+    }
+    return WidgetSizeAdapter.instance;
+  }
+
+  private constructor() {
+    this.layoutManager = FormLayoutManager.getInstance();
+    hilog.info(0x0000, TAG, 'WidgetSizeAdapter initialized');
+  }
+
+  /**
+   * 从Want参数中检测卡片尺寸
+   * @param want Want对象
+   * @returns 卡片尺寸
+   */
+  public detectSizeFromWant(want: Want): WidgetSize {
+    try {
+      // 从Want参数中获取卡片维度信息
+      const formDimension = want.parameters?.['ohos.extra.param.key.form_dimension'] as number;
+      const formName = want.parameters?.['ohos.extra.param.key.form_name'] as string;
+      
+      hilog.info(0x0000, TAG, `Detecting size from Want - dimension: ${formDimension}, name: ${formName}`);
+      
+      // 根据维度参数判断尺寸
+      if (formDimension !== undefined) {
+        return this.mapDimensionToSize(formDimension);
+      }
+      
+      // 如果没有维度参数,尝试从表单名称推断
+      if (formName) {
+        return this.mapFormNameToSize(formName);
+      }
+      
+      // 默认返回中等尺寸
+      hilog.warn(0x0000, TAG, 'Unable to detect size from Want, using medium as default');
+      return WidgetSize.MEDIUM;
+      
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Error detecting size from Want: ${error}`);
+      return WidgetSize.MEDIUM;
+    }
+  }
+
+  /**
+   * 处理卡片尺寸变化
+   * @param formId 卡片ID
+   * @param oldSize 旧尺寸
+   * @param newSize 新尺寸
+   * @param widgetData 卡片数据
+   * @returns 适配后的数据
+   */
+  public handleSizeChange(
+    formId: string, 
+    oldSize: WidgetSize, 
+    newSize: WidgetSize, 
+    widgetData: WidgetData
+  ): FormattedWidgetData {
+    hilog.info(0x0000, TAG, `Handling size change for form ${formId}: ${oldSize} -> ${newSize}`);
+    
+    // 检查是否需要重构UI
+    const needsRebuild = this.layoutManager.shouldRebuildUI(oldSize, newSize);
+    
+    if (needsRebuild) {
+      // 通知监听器尺寸变化
+      this.notifySizeChange(formId, oldSize, newSize);
+      
+      // 更新卡片配置
+      const sizeConfig = this.layoutManager.getSizeDisplayConfig(newSize);
+      const updatedConfig: WidgetConfig = {
+        size: sizeConfig.size,
+        theme: sizeConfig.theme,
+        showProgress: sizeConfig.showProgress,
+        showCover: sizeConfig.showCover
+      };
+      widgetData.config = updatedConfig;
+    }
+    
+    // 适配数据到新尺寸
+    return this.layoutManager.adaptDataForSize(widgetData, newSize);
+  }
+
+  /**
+   * 注册尺寸变化监听器
+   * @param formId 卡片ID
+   * @param listener 监听器
+   */
+  public registerSizeChangeListener(formId: string, listener: SizeChangeListener): void {
+    this.sizeChangeListeners.set(formId, listener);
+    hilog.info(0x0000, TAG, `Size change listener registered for form: ${formId}`);
+  }
+
+  /**
+   * 注销尺寸变化监听器
+   * @param formId 卡片ID
+   */
+  public unregisterSizeChangeListener(formId: string): void {
+    this.sizeChangeListeners.delete(formId);
+    hilog.info(0x0000, TAG, `Size change listener unregistered for form: ${formId}`);
+  }
+
+  /**
+   * 获取卡片的推荐配置
+   * @param size 卡片尺寸
+   * @returns 推荐配置
+   */
+  public getRecommendedConfig(size: WidgetSize): WidgetSizeConfig {
+    switch (size) {
+      case WidgetSize.SMALL:
+        const smallMaxTextLength: MaxTextLengthConfig = {
+          title: 20,
+          artist: 0,
+          album: 0
+        };
+        const smallConfig: WidgetSizeConfig = {
+          size: size,
+          displayElements: ['title', 'controls'],
+          maxTextLength: smallMaxTextLength,
+          layoutPriority: ['controls', 'title'],
+          animationDuration: 200
+        };
+        return smallConfig;
+      case WidgetSize.MEDIUM:
+        const mediumMaxTextLength: MaxTextLengthConfig = {
+          title: 30,
+          artist: 25,
+          album: 25
+        };
+        const mediumConfig: WidgetSizeConfig = {
+          size: size,
+          displayElements: ['title', 'artist', 'album', 'controls', 'progress'],
+          maxTextLength: mediumMaxTextLength,
+          layoutPriority: ['controls', 'title', 'artist', 'progress'],
+          animationDuration: 300
+        };
+        return mediumConfig;
+      case WidgetSize.LARGE:
+        const largeMaxTextLength: MaxTextLengthConfig = {
+          title: 40,
+          artist: 35,
+          album: 35
+        };
+        const largeConfig: WidgetSizeConfig = {
+          size: size,
+          displayElements: ['cover', 'title', 'artist', 'album', 'controls', 'progress'],
+          maxTextLength: largeMaxTextLength,
+          layoutPriority: ['cover', 'controls', 'title', 'artist', 'album', 'progress'],
+          animationDuration: 400
+        };
+        return largeConfig;
+      default:
+        return this.getRecommendedConfig(WidgetSize.MEDIUM);
+    }
+  }
+
+  /**
+   * 验证尺寸变化的合法性
+   * @param oldSize 旧尺寸
+   * @param newSize 新尺寸
+   * @returns 是否合法
+   */
+  public validateSizeChange(oldSize: WidgetSize, newSize: WidgetSize): boolean {
+    // 检查尺寸是否有效
+    if (!this.layoutManager.isValidSize(oldSize) || !this.layoutManager.isValidSize(newSize)) {
+      hilog.error(0x0000, TAG, `Invalid size change: ${oldSize} -> ${newSize}`);
+      return false;
+    }
+    
+    // 所有尺寸变化都是合法的
+    return true;
+  }
+
+  /**
+   * 获取尺寸变化的动画配置
+   * @param oldSize 旧尺寸
+   * @param newSize 新尺寸
+   * @returns 动画配置
+   */
+  public getSizeChangeAnimation(oldSize: WidgetSize, newSize: WidgetSize): AnimationConfig {
+    const isExpanding = this.isSizeExpanding(oldSize, newSize);
+    
+    const animationConfig: AnimationConfig = {
+      duration: isExpanding ? 300 : 250,
+      curve: isExpanding ? 'ease-out' : 'ease-in',
+      delay: 0,
+      iterations: 1,
+      playMode: 'normal'
+    };
+    return animationConfig;
+  }
+
+  /**
+   * 通知尺寸变化
+   */
+  private notifySizeChange(formId: string, oldSize: WidgetSize, newSize: WidgetSize): void {
+    const listener = this.sizeChangeListeners.get(formId);
+    if (listener) {
+      try {
+        listener.onSizeChanged(formId, oldSize, newSize);
+      } catch (error) {
+        hilog.error(0x0000, TAG, `Error notifying size change listener: ${error}`);
+      }
+    }
+  }
+
+  /**
+   * 将维度参数映射到尺寸枚举
+   */
+  private mapDimensionToSize(dimension: number): WidgetSize {
+    // HarmonyOS卡片维度常量映射
+    switch (dimension) {
+      case 1: // 2x1
+        return WidgetSize.SMALL;
+      case 2: // 4x2
+        return WidgetSize.MEDIUM;
+      case 3: // 4x3
+        return WidgetSize.LARGE;
+      default:
+        hilog.warn(0x0000, TAG, `Unknown dimension: ${dimension}, using medium as default`);
+        return WidgetSize.MEDIUM;
+    }
+  }
+
+  /**
+   * 将表单名称映射到尺寸枚举
+   */
+  private mapFormNameToSize(formName: string): WidgetSize {
+    const lowerName = formName.toLowerCase();
+    
+    if (lowerName.includes('small') || lowerName.includes('2x1')) {
+      return WidgetSize.SMALL;
+    } else if (lowerName.includes('large') || lowerName.includes('4x3')) {
+      return WidgetSize.LARGE;
+    } else if (lowerName.includes('medium') || lowerName.includes('4x2')) {
+      return WidgetSize.MEDIUM;
+    }
+    
+    // 默认返回中等尺寸
+    return WidgetSize.MEDIUM;
+  }
+
+  /**
+   * 判断尺寸是否在扩大
+   */
+  private isSizeExpanding(oldSize: WidgetSize, newSize: WidgetSize): boolean {
+    const sizeOrder = [WidgetSize.SMALL, WidgetSize.MEDIUM, WidgetSize.LARGE];
+    const oldIndex = sizeOrder.indexOf(oldSize);
+    const newIndex = sizeOrder.indexOf(newSize);
+    
+    return newIndex > oldIndex;
+  }
+}
+
+/**
+ * 尺寸变化监听器接口
+ */
+export interface SizeChangeListener {
+  onSizeChanged(formId: string, oldSize: WidgetSize, newSize: WidgetSize): void;
+}
+
+/**
+ * 卡片尺寸配置接口
+ */
+export interface WidgetSizeConfig {
+  size: WidgetSize;
+  displayElements: string[];
+  maxTextLength: MaxTextLengthConfig;
+  layoutPriority: string[];
+  animationDuration: number;
+}
+
+/**
+ * 动画配置接口
+ */
+export interface AnimationConfig {
+  duration: number;
+  curve: string; // 使用字符串代替Curve枚举
+  delay: number;
+  iterations: number;
+  playMode: string; // 使用字符串代替PlayMode枚举
+}

+ 115 - 0
entry/src/main/ets/common/widget/WidgetTypeHelpers.ets

@@ -0,0 +1,115 @@
+import { WidgetPersonalConfig, UserPreferences } from './WidgetConfigManager';
+import { WidgetData, PlayState, SongInfo, PlayProgress, PlaylistState, WidgetConfig } from './WidgetTypes';
+
+/**
+ * Widget类型辅助工具
+ * 提供类型安全的对象创建和转换方法
+ */
+export class WidgetTypeHelpers {
+  
+  /**
+   * 创建默认的PlayState
+   */
+  static createDefaultPlayState(): PlayState {
+    return {
+      isPlaying: false,
+      isPaused: true,
+      isLoading: false
+    };
+  }
+
+  /**
+   * 创建默认的SongInfo
+   */
+  static createDefaultSongInfo(): SongInfo {
+    return {
+      id: '',
+      title: '暂无播放',
+      artist: '未知艺术家',
+      album: '未知专辑',
+      coverImagePath: '',
+      duration: 0
+    };
+  }
+
+  /**
+   * 创建默认的PlayProgress
+   */
+  static createDefaultPlayProgress(): PlayProgress {
+    return {
+      currentPosition: 0,
+      duration: 0,
+      percentage: 0,
+      currentTimeText: '00:00',
+      totalTimeText: '00:00'
+    };
+  }
+
+  /**
+   * 创建默认的PlaylistState
+   */
+  static createDefaultPlaylistState(): PlaylistState {
+    return {
+      hasNext: false,
+      hasPrevious: false,
+      currentIndex: 0,
+      totalCount: 0
+    };
+  }
+
+  /**
+   * 创建默认的WidgetConfig
+   */
+  static createDefaultWidgetConfig(): WidgetConfig {
+    return {
+      size: 'medium',
+      theme: 'auto',
+      showProgress: true,
+      showCover: true
+    };
+  }
+
+  /**
+   * 创建默认的WidgetData
+   */
+  static createDefaultWidgetData(): WidgetData {
+    return {
+      playState: WidgetTypeHelpers.createDefaultPlayState(),
+      currentSong: WidgetTypeHelpers.createDefaultSongInfo(),
+      progress: WidgetTypeHelpers.createDefaultPlayProgress(),
+      playlist: WidgetTypeHelpers.createDefaultPlaylistState(),
+      config: WidgetTypeHelpers.createDefaultWidgetConfig()
+    };
+  }
+
+  /**
+   * 创建事件发布数据
+   */
+  static createEventPublishData(data: Object): EventPublishData {
+    return {
+      bundleName: 'com.xgplayer.ttmusic.hm',
+      data: JSON.stringify(data)
+    };
+  }
+
+  /**
+   * 创建Want参数
+   */
+  static createWantParameters(params: Record<string, string>): Record<string, Object> {
+    const result: Record<string, Object> = {};
+    const keys = Object.keys(params);
+    for (let i = 0; i < keys.length; i++) {
+      const key = keys[i];
+      result[key] = params[key];
+    }
+    return result;
+  }
+}
+
+/**
+ * 事件发布数据接口
+ */
+export interface EventPublishData {
+  bundleName: string;
+  data: string;
+}

+ 129 - 0
entry/src/main/ets/common/widget/WidgetTypes.ets

@@ -74,6 +74,15 @@ export interface PlaylistState {
   totalCount: number;
 }
 
+/**
+ * 投播信息接口
+ */
+export interface CastingInfo {
+  isCasting: boolean;
+  deviceName: string;
+  deviceType: number;
+}
+
 /**
  * 卡片配置接口
  */
@@ -93,6 +102,7 @@ export interface WidgetData {
   progress: PlayProgress;
   playlist: PlaylistState;
   config: WidgetConfig;
+  castingInfo?: CastingInfo;
 }
 
 /**
@@ -216,4 +226,123 @@ export interface WidgetEventParams {
  */
 export interface WidgetSeekParams {
   percentage: number;
+}
+
+/**
+ * 容器内边距接口
+ */
+export interface ContainerPadding {
+  left: number;
+  right: number;
+  top: number;
+  bottom: number;
+}
+
+/**
+ * 按钮尺寸接口
+ */
+export interface ButtonSize {
+  width: number;
+  height: number;
+}
+
+/**
+ * 字体大小配置接口
+ */
+export interface FontSizeConfig {
+  title: number;
+  artist: number;
+  time: number;
+}
+
+/**
+ * 间距配置接口
+ */
+export interface SpacingConfig {
+  horizontal: number;
+  vertical: number;
+}
+
+/**
+ * 显示元素配置接口
+ */
+export interface ShowElementsConfig {
+  progress: boolean;
+  cover: boolean;
+  album: boolean;
+  time: boolean;
+}
+
+/**
+ * 文本长度配置接口
+ */
+export interface MaxTextLengthConfig {
+  title: number;
+  artist: number;
+  album: number;
+}
+
+/**
+ * 响应式布局参数接口
+ */
+export interface ResponsiveLayoutParams {
+  containerPadding: ContainerPadding;
+  buttonSize: ButtonSize;
+  playButtonSize: ButtonSize;
+  fontSize: FontSizeConfig;
+  spacing: SpacingConfig;
+  showElements: ShowElementsConfig;
+}
+
+/**
+ * 卡片尺寸配置接口
+ */
+export interface WidgetSizeConfig {
+  size: WidgetSize;
+  displayElements: string[];
+  maxTextLength: MaxTextLengthConfig;
+  layoutPriority: string[];
+  animationDuration: number;
+}
+
+/**
+ * 动画配置接口
+ */
+export interface AnimationConfig {
+  duration: number;
+  curve: string;
+  delay?: number;
+  iterations?: number;
+}
+
+/**
+ * 尺寸变化监听器接口
+ */
+export interface SizeChangeListener {
+  onSizeChanged(formId: string, oldSize: WidgetSize, newSize: WidgetSize): void;
+}
+
+/**
+ * 播放状态变化数据接口
+ */
+export interface PlayStateChangeData {
+  playState: PlayState;
+  progress: PlayProgress;
+}
+
+/**
+ * 歌曲变化数据接口
+ */
+export interface SongChangeData {
+  currentSong: SongInfo;
+}
+
+/**
+ * 播放器状态广播数据接口
+ */
+export interface PlayerStateBroadcastData {
+  playState: PlayState;
+  currentSong: SongInfo;
+  progress: PlayProgress;
+  playlist: PlaylistState;
 }

+ 1 - 1
entry/src/main/ets/common/widget/WidgetUtils.ets

@@ -1,7 +1,7 @@
 import { hilog } from '@kit.PerformanceAnalysisKit';
 import { WidgetActionData } from './WidgetTypes';
 
-const TAG = 'WidgetUtils';
+const TAG = 'Heanup WidgetUtils';
 
 /**
  * 卡片工具函数

+ 0 - 30
entry/src/main/ets/common/widget/index.ets

@@ -1,30 +0,0 @@
-/**
- * 卡片模块统一导出
- */
-
-// 类型定义
-export * from './WidgetTypes';
-
-// 常量定义
-export * from './WidgetEventConstants';
-
-// 核心服务
-export { WidgetDataManager } from './WidgetDataManager';
-export { PlayerControlService } from './PlayerControlService';
-
-// 控制器
-export { WidgetController } from './WidgetController';
-export { SimpleWidgetController } from './SimpleWidgetController';
-
-// 工具函数
-export * from './WidgetUtils';
-export * from './SimpleWidgetUtils';
-
-// 测试辅助
-export { WidgetTestHelper } from './WidgetTestHelper';
-export { WidgetSimpleTest } from './WidgetSimpleTest';
-export { TypeValidation } from './TypeValidation';
-export { FinalTypeCheck } from './FinalTypeCheck';
-export { WidgetDiagnostics } from './WidgetDiagnostics';
-export { WidgetConfigValidator } from './WidgetConfigValidator';
-export { WidgetQuickFix } from './WidgetQuickFix';

+ 110 - 7
entry/src/main/ets/entryability/EntryAbility.ets

@@ -1,5 +1,4 @@
 
-
 /**
  * 应用主Ability入口文件
  * 功能:
@@ -27,6 +26,24 @@ import { SpiderMan } from '@simplepeng/spider-man';
 import { smartMobilityCommon } from '@kit.CarKit';
 import { url } from '@kit.ArkTS';
 
+/**
+ * 卡片配置页面参数接口
+ */
+interface WidgetConfigParams extends Record<string, Object> {
+  formId: string;
+  currentSize: string;
+  currentConfig: string;
+  returnToWidget: boolean;
+}
+
+/**
+ * 卡片删除确认页面参数接口
+ */
+interface WidgetDeleteParams extends Record<string, Object> {
+  formId: string;
+  action: string;
+}
+
 /**
  * 主Ability类,继承自UIAbility
  * 负责:
@@ -49,11 +66,11 @@ export default class EntryAbility extends UIAbility {
      */
     private onWindowSizeChange: (windowSize: window.Size) => void = async (windowSize: window.Size) => {
         // 获取宽度断点并更新全局状态
-        let widthBp: WidthBreakpoint = this.uiContext!.getWindowWidthBreakpoint();
+        let widthBp = this.uiContext!.getWindowWidthBreakpoint();
         AppStorage.setOrCreate('currentWidthBreakpoint', widthBp);
 
         // 获取高度断点并更新全局状态
-        let heightBp: HeightBreakpoint = this.uiContext!.getWindowHeightBreakpoint();
+        let heightBp = this.uiContext!.getWindowHeightBreakpoint();
         AppStorage.setOrCreate('currentHeightBreakpoint', heightBp);
         // 记录尺寸变化日志
         // LogUtil.info('pura onWindowSizeChange currentHeightBreakpoint= '+heightBp);
@@ -89,6 +106,7 @@ export default class EntryAbility extends UIAbility {
         setTimeout(async ()=>{
             this.loadDoWant(want)
             await this.handleParam(want)
+            this.handleWidgetConfigParams(want)
         },2000)
 
         this.handleWeChatCallIfNeed(want)
@@ -102,6 +120,7 @@ export default class EntryAbility extends UIAbility {
         super.onNewWant(want, launchParam);
         this.loadDoWant(want)
         await this.handleParam(want)
+        this.handleWidgetConfigParams(want)
         this.handleWeChatCallIfNeed(want)
 
     }
@@ -182,7 +201,7 @@ export default class EntryAbility extends UIAbility {
 
             windowClass = data;
             // LogUtil.info( 'getMainWindow = ');
-            globalThis.windowClass = data // 赋值给全局变量windowClass
+            GlobalContext.getContext().setObject('windowClass', data); // 使用GlobalContext替代globalThis
 
             windowClass.setWindowLayoutFullScreen(true).then(() => {
                 console.info('Succeeded in setting the window layout to full-screen mode.');
@@ -210,7 +229,6 @@ export default class EntryAbility extends UIAbility {
 
         AppStorage.setOrCreate('windowStage',windowStage);
 
-        GlobalContext.getContext().setObject('windowClass',windowClass)
         hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
 
         windowStage.loadContent('pages/SplashIndex', (err, data) => {
@@ -221,8 +239,8 @@ export default class EntryAbility extends UIAbility {
             //一多断点开发
             windowStage.getMainWindow().then((data: window.Window) => {
                 this.uiContext = data.getUIContext();
-                let widthBp: WidthBreakpoint = this.uiContext.getWindowWidthBreakpoint();
-                let heightBp: HeightBreakpoint = this.uiContext.getWindowHeightBreakpoint();
+                let widthBp = this.uiContext.getWindowWidthBreakpoint();
+                let heightBp = this.uiContext.getWindowHeightBreakpoint();
                 AppStorage.setOrCreate('currentWidthBreakpoint', widthBp);
                 AppStorage.setOrCreate('currentHeightBreakpoint', heightBp);
                 LogUtil.info( 'getMainWindow currentHeightBreakpoint= '+heightBp);
@@ -301,4 +319,89 @@ export default class EntryAbility extends UIAbility {
         emitter.emit({ eventId: 333 }, eventData); // 发送广播通知更新doSwipBack
     }
 
+    /**
+     * 处理卡片配置相关参数
+     * 根据传入的页面参数跳转到对应的卡片配置页面
+     */
+    private handleWidgetConfigParams(want: Want): void {
+        try {
+            const params = want.parameters;
+            if (!params) return;
+
+            const page = params['page'] as string;
+            const source = params['source'] as string;
+
+            // 只处理来自卡片的请求
+            if (source !== 'widget') return;
+
+            hilog.info(0x0000, 'testTag', `Widget config request: page=${page}`);
+
+            // 延迟执行页面跳转,确保应用已完全启动
+            setTimeout(() => {
+                this.navigateToWidgetConfigPage(page, params);
+            }, 1500);
+
+        } catch (error) {
+            hilog.error(0x0000, 'testTag', `Failed to handle widget config params: ${error}`);
+        }
+    }
+
+    /**
+     * 导航到卡片配置页面
+     */
+    private navigateToWidgetConfigPage(page: string, params: Record<string, Object>): void {
+        try {
+            const windowStage = AppStorage.get('windowStage') as window.WindowStage;
+            if (!windowStage) {
+                hilog.error(0x0000, 'testTag', 'WindowStage not available for widget config navigation');
+                return;
+            }
+
+            let targetPage = '';
+            let pageParams: Record<string, Object> = {};
+
+            switch (page) {
+                case 'widget_settings':
+                case 'widget_config':
+                    targetPage = 'pages/WidgetConfigPage';
+                    const widgetConfigParams: WidgetConfigParams = {
+                        formId: (params['formId'] as string) || '',
+                        currentSize: (params['currentSize'] as string) || 'medium',
+                        currentConfig: (params['currentConfig'] as string) || '{}',
+                        returnToWidget: (params['returnToWidget'] as boolean) || false
+                    };
+                    pageParams = widgetConfigParams;
+                    break;
+
+                case 'widget_delete_confirm':
+                    targetPage = 'pages/WidgetDeleteConfirmPage';
+                    const widgetDeleteParams: WidgetDeleteParams = {
+                        formId: (params['formId'] as string) || '',
+                        action: (params['action'] as string) || 'confirm_delete'
+                    };
+                    pageParams = widgetDeleteParams;
+                    break;
+
+                default:
+                    hilog.warn(0x0000, 'testTag', `Unknown widget config page: ${page}`);
+                    return;
+            }
+
+            // 使用windowStage加载对应页面
+            windowStage.loadContent(targetPage, (err, data) => {
+                if (err.code) {
+                    hilog.error(0x0000, 'testTag', `Failed to load widget config page: ${JSON.stringify(err)}`);
+                    return;
+                }
+
+                // 将参数存储到AppStorage中,供页面使用
+                AppStorage.setOrCreate('widgetConfigParams', pageParams);
+                hilog.info(0x0000, 'testTag', `Widget config page loaded: ${targetPage}`);
+            });
+
+        } catch (error) {
+            hilog.error(0x0000, 'testTag', `Failed to navigate to widget config page: ${error}`);
+        }
+    }
+
 }

+ 533 - 0
entry/src/main/ets/entryformability/EntryFormAbility.ets

@@ -0,0 +1,533 @@
+import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
+import { Configuration, Want } from '@kit.AbilityKit';
+import { BusinessError } from '@kit.BasicServicesKit';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+
+import { WidgetDataManager } from '../common/widget/WidgetDataManager';
+import { PlayerControlService } from '../common/widget/PlayerControlService';
+import { WidgetCommand, WidgetControlParams, WidgetData, WidgetSize } from '../common/widget/WidgetTypes';
+import { FormLayoutManager } from '../common/widget/FormLayoutManager';
+import { WidgetSizeAdapter, SizeChangeListener } from '../common/widget/WidgetSizeAdapter';
+import { WidgetConfigManager, UserPreferences } from '../common/widget/WidgetConfigManager';
+import { ThemeSyncService, ThemeChangeListener, ThemeChangeEvent, ThemeInfo } from '../common/widget/ThemeSyncService';
+import {
+  UserPreferencesService,
+  PreferencesChangeListener,
+  PreferencesChangeEvent
+} from '../common/widget/UserPreferencesService';
+import { ObjectUtils } from '../common/widget/ObjectUtils';
+import {
+  PLAY_PAUSE_EVENT,
+  NEXT_SONG_EVENT,
+  PREV_SONG_EVENT,
+  SEEK_TO_EVENT,
+  OPEN_APP_EVENT,
+  OPEN_PLAYER_EVENT,
+  LONG_PRESS_MENU_EVENT,
+  WIDGET_SETTINGS_EVENT,
+  WIDGET_DELETE_EVENT,
+  WIDGET_CONFIG_PAGE_EVENT,
+  PAGE_MAIN,
+  PAGE_PLAYER
+} from '../common/widget/WidgetEventConstants';
+
+const TAG = 'Heanup';
+
+/**
+ * 卡片设置参数接口
+ */
+interface WidgetSettingsParams extends Record<string, Object> {
+  formId: string;
+  currentSize: string;
+  currentConfig: string;
+}
+
+/**
+ * 卡片配置参数接口
+ */
+interface WidgetConfigParams extends Record<string, Object> {
+  formId: string;
+  configType: string;
+  configValue: string;
+}
+
+/**
+ * 桌面播放器卡片扩展能力
+ * 负责处理卡片的生命周期管理和用户交互事件
+ * 支持多尺寸适配和UI重构
+ */
+export default class EntryFormAbility extends FormExtensionAbility
+implements SizeChangeListener, ThemeChangeListener, PreferencesChangeListener {
+  private widgetDataManager: WidgetDataManager = new WidgetDataManager();
+  private playerControlService: PlayerControlService = new PlayerControlService();
+  private layoutManager: FormLayoutManager = FormLayoutManager.getInstance();
+  private sizeAdapter: WidgetSizeAdapter = WidgetSizeAdapter.getInstance();
+  private configManager: WidgetConfigManager = WidgetConfigManager.getInstance();
+  private themeSyncService: ThemeSyncService = ThemeSyncService.getInstance();
+  private userPreferencesService: UserPreferencesService = UserPreferencesService.getInstance();
+  private formSizeMap: Map<string, WidgetSize> = new Map();
+
+  /**
+   * 卡片创建时调用
+   */
+  onAddForm(want: Want): formBindingData.FormBindingData {
+    hilog.info(0x0000, TAG, 'onAddForm called');
+
+    const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string;
+    const formName = want.parameters?.['ohos.extra.param.key.form_name'] as string;
+    const tempFlag = want.parameters?.['ohos.extra.param.key.form_temporary'] as boolean;
+
+    hilog.info(0x0000, TAG, `Form added: ${formId}, name: ${formName}, temp: ${tempFlag}`);
+
+    // 检测卡片尺寸
+    const widgetSize = this.sizeAdapter.detectSizeFromWant(want);
+    this.formSizeMap.set(formId, widgetSize);
+
+    hilog.info(0x0000, TAG, `Detected widget size: ${widgetSize} for form: ${formId}`);
+
+    // 注册各种监听器
+    this.sizeAdapter.registerSizeChangeListener(formId, this);
+    this.themeSyncService.addThemeChangeListener(this);
+    this.userPreferencesService.addPreferencesChangeListener(this);
+
+    // 初始化卡片数据
+    this.initializeWidget(formId, widgetSize);
+
+    // 获取适配后的初始数据
+    const initialData = this.widgetDataManager.getInitialWidgetData();
+    const adaptedData = this.layoutManager.adaptDataForSize(initialData, widgetSize);
+
+    return formBindingData.createFormBindingData(adaptedData);
+  }
+
+  /**
+   * 卡片更新时调用
+   */
+  onUpdateForm(formId: string): void {
+    hilog.info(0x0000, TAG, `onUpdateForm called: ${formId}`);
+
+    // 获取最新的播放状态数据
+    this.updateWidgetData(formId);
+  }
+
+  /**
+   * 卡片删除时调用
+   */
+  onRemoveForm(formId: string): void {
+    hilog.info(0x0000, TAG, `onRemoveForm called: ${formId}`);
+
+    // 注销各种监听器
+    this.sizeAdapter.unregisterSizeChangeListener(formId);
+    this.themeSyncService.removeThemeChangeListener(this);
+    this.userPreferencesService.removePreferencesChangeListener(this);
+
+    // 清理卡片尺寸记录
+    this.formSizeMap.delete(formId);
+
+    // 清理卡片相关数据和配置
+    this.widgetDataManager.removeWidgetData(formId);
+    this.configManager.removeWidgetConfig(formId);
+  }
+
+  /**
+   * 卡片可见性变化时调用
+   */
+  onVisibilityChange(newStatus: Record<string, number>): void {
+    hilog.info(0x0000, TAG, 'onVisibilityChange called');
+
+    const formIds = Object.keys(newStatus);
+    for (let i = 0; i < formIds.length; i++) {
+      const formId = formIds[i];
+      const isVisible = newStatus[formId] === 1;
+      hilog.info(0x0000, TAG, `Form ${formId} visibility: ${isVisible}`);
+
+      if (isVisible) {
+        // 卡片变为可见时,更新数据
+        this.updateWidgetData(formId);
+      }
+    }
+  }
+
+  /**
+   * 处理卡片事件(用户交互)
+   */
+  onFormEvent(formId: string, message: string): void {
+    hilog.info(0x0000, TAG, `onFormEvent called: ${formId}, message: ${message}`);
+
+    try {
+      const eventData = JSON.parse(message) as Object;
+      this.handleWidgetEvent(formId, eventData);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to parse form event: ${error}`);
+    }
+  }
+
+  /**
+   * 卡片配置更新时调用
+   */
+  onConfigurationUpdate(newConfig: Object): void {
+    hilog.info(0x0000, TAG, 'onConfigurationUpdate called');
+
+    // 启动主题同步服务
+    this.themeSyncService.startThemeSync();
+
+    // 更新所有卡片以适应新配置
+    this.widgetDataManager.updateAllWidgets();
+  }
+
+  /**
+   * 实现SizeChangeListener接口
+   * 处理卡片尺寸变化事件
+   */
+  onSizeChanged(formId: string, oldSize: WidgetSize, newSize: WidgetSize): void {
+    hilog.info(0x0000, TAG, `Size changed for form ${formId}: ${oldSize} -> ${newSize}`);
+
+    // 更新尺寸记录
+    this.formSizeMap.set(formId, newSize);
+
+    // 立即更新卡片数据以适应新尺寸
+    this.updateWidgetData(formId);
+  }
+
+  /**
+   * 处理卡片尺寸变化(系统调用)
+   * @param newStatus 新的尺寸状态
+   */
+  onAcquireFormState(want: Want): number {
+    hilog.info(0x0000, TAG, 'onAcquireFormState called');
+
+    const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string;
+
+    if (formId) {
+      // 检测新的尺寸
+      const newSize = this.sizeAdapter.detectSizeFromWant(want);
+      const oldSize = this.formSizeMap.get(formId);
+
+      if (oldSize && this.sizeAdapter.validateSizeChange(oldSize, newSize)) {
+        // 处理尺寸变化
+        this.handleFormSizeChange(formId, oldSize, newSize);
+      }
+    }
+
+    // 返回卡片状态 - 使用数字常量代替枚举
+    return 1; // READY状态
+  }
+
+  /**
+   * 实现ThemeChangeListener接口
+   * 处理主题变化事件
+   */
+  onThemeChanged(event: ThemeChangeEvent): void {
+    hilog.info(0x0000, TAG, `Theme changed: ${event.themeInfo.theme}`);
+
+    // 更新所有卡片的主题
+    this.updateAllWidgetsTheme(event.themeInfo);
+  }
+
+  /**
+   * 实现PreferencesChangeListener接口
+   * 处理用户偏好设置变化事件
+   */
+  onPreferencesChanged(event: PreferencesChangeEvent): void {
+    hilog.info(0x0000, TAG, `Preferences changed: ${event.changedKeys.join(', ')}`);
+
+    // 根据变化的设置更新卡片
+    this.handlePreferencesChange(event.changedKeys, event.newPreferences);
+  }
+
+  /**
+   * 初始化卡片
+   */
+  private async initializeWidget(formId: string, widgetSize: WidgetSize): Promise<void> {
+    try {
+      // 注册播放状态监听
+      this.playerControlService.registerStateListener((data: WidgetData) => {
+        // 根据卡片尺寸适配数据
+        const currentSize = this.formSizeMap.get(formId) || WidgetSize.MEDIUM;
+        const adaptedData = this.layoutManager.adaptDataForSize(data, currentSize);
+        this.widgetDataManager.updateWidget(formId, adaptedData);
+      });
+
+      // 获取当前播放状态
+      const currentState = await this.playerControlService.getCurrentPlayState();
+
+      // 获取卡片个性化配置
+      const personalConfig = await this.configManager.getWidgetConfig(formId, widgetSize);
+
+      // 应用尺寸和个性化配置
+      const sizeConfig = this.layoutManager.getSizeDisplayConfig(widgetSize);
+      currentState.config = ObjectUtils.assign(currentState.config);
+      currentState.config.showProgress = personalConfig.showProgress;
+      currentState.config.showCover = personalConfig.showCover;
+      currentState.config.theme = personalConfig.theme;
+
+      // 适配数据到指定尺寸
+      const adaptedData = this.layoutManager.adaptDataForSize(currentState, widgetSize);
+      await this.widgetDataManager.saveWidgetData(formId, adaptedData);
+
+      hilog.info(0x0000, TAG, `Widget ${formId} initialized successfully with size: ${widgetSize}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to initialize widget ${formId}: ${error}`);
+    }
+  }
+
+  /**
+   * 更新卡片数据
+   */
+  private async updateWidgetData(formId: string): Promise<void> {
+    try {
+      const currentState = await this.playerControlService.getCurrentPlayState();
+
+      // 获取卡片当前尺寸
+      const currentSize = this.formSizeMap.get(formId) || WidgetSize.MEDIUM;
+
+      // 适配数据到当前尺寸
+      const adaptedData = this.layoutManager.adaptDataForSize(currentState, currentSize);
+      await this.widgetDataManager.updateWidget(formId, adaptedData);
+
+      hilog.info(0x0000, TAG, `Widget ${formId} updated successfully with size: ${currentSize}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to update widget ${formId}: ${error}`);
+    }
+  }
+
+  /**
+   * 处理卡片事件
+   */
+  private async handleWidgetEvent(formId: string, eventData: Object): Promise<void> {
+    try {
+      // 解析事件数据
+      const actionData: Record<string, Object> = eventData as Record<string, Object>;
+      const action: string = (actionData['action'] as string) || 'play_pause';
+      const params: Object = (actionData['params'] as Object) || new Object();
+
+      hilog.info(0x0000, TAG, `Handling widget event,action ${action}`);
+      hilog.info(0x0000, TAG, `Handling widget event,actionData ${JSON.stringify(actionData)}`);
+      const sendParams: Record<string, Object> = params as Record<string, Object>;
+
+
+      switch (sendParams['func']) {
+        case PLAY_PAUSE_EVENT:
+          await this.playerControlService.sendControlCommand(WidgetCommand.PLAY_PAUSE);
+          break;
+
+        case NEXT_SONG_EVENT:
+          await this.playerControlService.sendControlCommand(WidgetCommand.NEXT_SONG);
+          break;
+
+        case PREV_SONG_EVENT:
+          await this.playerControlService.sendControlCommand(WidgetCommand.PREV_SONG);
+          break;
+
+        case SEEK_TO_EVENT:
+          const seekParams: Record<string, Object> = sendParams['params'] as Record<string, Object>;
+          const controlParams: WidgetControlParams = {
+            percentage: (seekParams['percentage'] as number) || 0,
+            position: 0
+          };
+          await this.playerControlService.sendControlCommand(WidgetCommand.SEEK_TO, controlParams);
+          break;
+
+        case OPEN_APP_EVENT:
+          // 启动主应用到首页
+          await this.playerControlService.launchMainApp(PAGE_MAIN);
+          break;
+
+        case OPEN_PLAYER_EVENT:
+          // 启动主应用到播放器页面
+          await this.playerControlService.launchMainApp(PAGE_PLAYER);
+          break;
+
+        case LONG_PRESS_MENU_EVENT:
+          // 处理长按菜单显示(由系统处理,这里记录日志)
+          hilog.info(0x0000, TAG, 'Long press menu event triggered');
+          break;
+
+        case WIDGET_SETTINGS_EVENT:
+          // 打开卡片设置页面
+          await this.handleWidgetSettings(formId);
+          break;
+
+        case WIDGET_DELETE_EVENT:
+          // 处理卡片删除
+          await this.handleWidgetDelete(formId);
+          break;
+
+        case WIDGET_CONFIG_PAGE_EVENT:
+          // 跳转到卡片配置界面
+          await this.handleWidgetConfigPage(formId);
+          break;
+
+        default:
+          hilog.warn(0x0000, TAG, `Unknown widget action: ${action}`);
+          return;
+      }
+
+      // 处理完命令后更新卡片状态
+      setTimeout((): void => {
+        this.updateWidgetData(formId);
+      }, 200);
+
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to handle widget event: ${error}`);
+    }
+  }
+
+  /**
+   * 处理表单尺寸变化
+   */
+  private async handleFormSizeChange(formId: string, oldSize: WidgetSize, newSize: WidgetSize): Promise<void> {
+    try {
+      hilog.info(0x0000, TAG, `Handling form size change: ${formId} from ${oldSize} to ${newSize}`);
+
+      // 获取当前播放状态
+      const currentState = await this.playerControlService.getCurrentPlayState();
+
+      // 使用适配器处理尺寸变化
+      const adaptedData = this.sizeAdapter.handleSizeChange(formId, oldSize, newSize, currentState);
+
+      // 更新卡片数据
+      await this.widgetDataManager.updateWidget(formId, adaptedData);
+
+      hilog.info(0x0000, TAG, `Form size change handled successfully for: ${formId}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to handle form size change: ${error}`);
+    }
+  }
+
+  /**
+   * 更新所有卡片的主题
+   */
+  private async updateAllWidgetsTheme(themeInfo: ThemeInfo): Promise<void> {
+    try {
+      // 更新所有卡片配置中的主题信息
+      await this.configManager.updateAllWidgetConfigs((config) => {
+        const updatedConfig = ObjectUtils.assign(config);
+        updatedConfig.theme = themeInfo.theme;
+        updatedConfig.backgroundColor = themeInfo.backgroundColor;
+        updatedConfig.textColor = themeInfo.textColor;
+        updatedConfig.accentColor = themeInfo.accentColor;
+        return updatedConfig;
+      });
+
+      // 刷新所有卡片显示
+      await this.widgetDataManager.updateAllWidgets();
+
+      hilog.info(0x0000, TAG, 'All widgets theme updated');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to update all widgets theme: ${error}`);
+    }
+  }
+
+  /**
+   * 处理偏好设置变化
+   */
+  private async handlePreferencesChange(changedKeys: string[], newPreferences: UserPreferences): Promise<void> {
+    try {
+      // 检查是否需要重新同步主题
+      if (changedKeys.includes('syncThemeColor') && newPreferences.syncThemeColor) {
+        await this.themeSyncService.syncTheme();
+      }
+
+      // 检查是否需要更新缓存设置
+      if (changedKeys.includes('enableCache') || changedKeys.includes('cacheExpiry')) {
+        // 这里可以更新缓存相关设置
+        hilog.info(0x0000, TAG, 'Cache settings updated');
+      }
+
+      // 检查隐私设置变化
+      const privacyKeys = ['showSongInfo', 'showArtistInfo', 'showAlbumCover'];
+      const hasPrivacyChange = changedKeys.some(key => privacyKeys.includes(key));
+
+      if (hasPrivacyChange) {
+        // 更新所有卡片的显示配置
+        await this.configManager.updateAllWidgetConfigs((config) => {
+          const updatedConfig = ObjectUtils.assign(config);
+          updatedConfig.showCover = (newPreferences.showAlbumCover as boolean) && config.showCover;
+          return updatedConfig;
+        });
+
+        // 刷新所有卡片显示
+        await this.widgetDataManager.updateAllWidgets();
+      }
+
+      hilog.info(0x0000, TAG, 'Preferences change handled successfully');
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to handle preferences change: ${error}`);
+    }
+  }
+
+  /**
+   * 处理卡片设置
+   */
+  private async handleWidgetSettings(formId: string): Promise<void> {
+    try {
+      hilog.info(0x0000, TAG, `Opening widget settings for form: ${formId}`);
+
+      // 获取当前卡片配置
+      const currentSize = this.formSizeMap.get(formId) || WidgetSize.MEDIUM;
+      const currentConfig = await this.configManager.getWidgetConfig(formId, currentSize);
+
+      // 启动主应用到卡片设置页面,传递卡片ID和当前配置
+      const settingsParams: WidgetSettingsParams = {
+        formId: formId,
+        currentSize: currentSize,
+        currentConfig: JSON.stringify(currentConfig)
+      };
+
+      await this.playerControlService.launchMainApp('widget_settings', settingsParams);
+
+      hilog.info(0x0000, TAG, `Widget settings opened for form: ${formId}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to open widget settings: ${error}`);
+    }
+  }
+
+  /**
+   * 处理卡片删除
+   */
+  private async handleWidgetDelete(formId: string): Promise<void> {
+    try {
+      hilog.info(0x0000, TAG, `Processing widget delete for form: ${formId}`);
+
+      // 显示删除确认对话框(通过主应用)
+      const deleteParams: WidgetConfigParams = {
+        formId: formId,
+        configType: 'delete',
+        configValue: 'confirm_delete'
+      };
+
+      await this.playerControlService.launchMainApp('widget_delete_confirm', deleteParams);
+
+      hilog.info(0x0000, TAG, `Widget delete confirmation shown for form: ${formId}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to process widget delete: ${error}`);
+    }
+  }
+
+  /**
+   * 处理卡片配置界面跳转
+   */
+  private async handleWidgetConfigPage(formId: string): Promise<void> {
+    try {
+      hilog.info(0x0000, TAG, `Opening widget config page for form: ${formId}`);
+
+      // 获取当前卡片信息
+      const currentSize = this.formSizeMap.get(formId) || WidgetSize.MEDIUM;
+      const currentConfig = await this.configManager.getWidgetConfig(formId, currentSize);
+
+      // 启动主应用到卡片配置页面
+      const configParams: WidgetConfigParams = {
+        formId: formId,
+        configType: 'full_config',
+        configValue: JSON.stringify(currentConfig)
+      };
+
+      await this.playerControlService.launchMainApp('widget_config', configParams);
+
+      hilog.info(0x0000, TAG, `Widget config page opened for form: ${formId}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to open widget config page: ${error}`);
+    }
+  }
+}

+ 0 - 206
entry/src/main/ets/entryformability/PlayerWidgetFormExtensionAbility.ets

@@ -1,206 +0,0 @@
-import formBindingData from '@ohos.app.form.formBindingData';
-import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
-import formProvider from '@ohos.app.form.formProvider';
-import Want from '@ohos.app.ability.Want';
-import { hilog } from '@kit.PerformanceAnalysisKit';
-import { WidgetDataManager } from '../common/widget/WidgetDataManager';
-import { PlayerControlService } from '../common/widget/PlayerControlService';
-import { WidgetCommand, WidgetControlParams, WidgetData } from '../common/widget/WidgetTypes';
-import { 
-  PLAY_PAUSE_EVENT, 
-  NEXT_SONG_EVENT, 
-  PREV_SONG_EVENT, 
-  SEEK_TO_EVENT, 
-  OPEN_APP_EVENT, 
-  OPEN_PLAYER_EVENT,
-  PAGE_MAIN,
-  PAGE_PLAYER
-} from '../common/widget/WidgetEventConstants';
-
-const TAG = 'PlayerWidgetFormExtensionAbility';
-
-/**
- * 桌面播放器卡片扩展能力
- * 负责处理卡片的生命周期管理和用户交互事件
- */
-export default class PlayerWidgetFormExtensionAbility extends FormExtensionAbility {
-  private widgetDataManager: WidgetDataManager = new WidgetDataManager();
-  private playerControlService: PlayerControlService = new PlayerControlService();
-
-  /**
-   * 卡片创建时调用
-   */
-  onAddForm(want: Want): formBindingData.FormBindingData {
-    hilog.info(0x0000, TAG, 'onAddForm called');
-    
-    const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string;
-    const formName = want.parameters?.['ohos.extra.param.key.form_name'] as string;
-    const tempFlag = want.parameters?.['ohos.extra.param.key.form_temporary'] as boolean;
-    
-    hilog.info(0x0000, TAG, `Form added: ${formId}, name: ${formName}, temp: ${tempFlag}`);
-    
-    // 初始化卡片数据
-    this.initializeWidget(formId);
-    
-    // 获取初始数据
-    const initialData = this.widgetDataManager.getInitialWidgetData();
-    
-    return formBindingData.createFormBindingData(initialData);
-  }
-
-  /**
-   * 卡片更新时调用
-   */
-  onUpdateForm(formId: string): void {
-    hilog.info(0x0000, TAG, `onUpdateForm called: ${formId}`);
-    
-    // 获取最新的播放状态数据
-    this.updateWidgetData(formId);
-  }
-
-  /**
-   * 卡片删除时调用
-   */
-  onRemoveForm(formId: string): void {
-    hilog.info(0x0000, TAG, `onRemoveForm called: ${formId}`);
-    
-    // 清理卡片相关数据
-    this.widgetDataManager.removeWidgetData(formId);
-  }
-
-  /**
-   * 卡片可见性变化时调用
-   */
-  onVisibilityChange(newStatus: Record<string, number>): void {
-    hilog.info(0x0000, TAG, 'onVisibilityChange called');
-    
-    const formIds = Object.keys(newStatus);
-    for (let i = 0; i < formIds.length; i++) {
-      const formId = formIds[i];
-      const isVisible = newStatus[formId] === 1;
-      hilog.info(0x0000, TAG, `Form ${formId} visibility: ${isVisible}`);
-      
-      if (isVisible) {
-        // 卡片变为可见时,更新数据
-        this.updateWidgetData(formId);
-      }
-    }
-  }
-
-  /**
-   * 处理卡片事件(用户交互)
-   */
-  onFormEvent(formId: string, message: string): void {
-    hilog.info(0x0000, TAG, `onFormEvent called: ${formId}, message: ${message}`);
-    
-    try {
-      const eventData = JSON.parse(message) as Object;
-      this.handleWidgetEvent(formId, eventData);
-    } catch (error) {
-      hilog.error(0x0000, TAG, `Failed to parse form event: ${error}`);
-    }
-  }
-
-  /**
-   * 卡片配置更新时调用
-   */
-  onConfigurationUpdate(newConfig: Object): void {
-    hilog.info(0x0000, TAG, 'onConfigurationUpdate called');
-    
-    // 更新所有卡片以适应新配置
-    this.widgetDataManager.updateAllWidgets();
-  }
-
-  /**
-   * 初始化卡片
-   */
-  private async initializeWidget(formId: string): Promise<void> {
-    try {
-      // 注册播放状态监听
-      this.playerControlService.registerStateListener((data: WidgetData) => {
-        this.widgetDataManager.updateWidget(formId, data);
-      });
-      
-      // 获取当前播放状态
-      const currentState = await this.playerControlService.getCurrentPlayState();
-      await this.widgetDataManager.saveWidgetData(formId, currentState);
-      
-      hilog.info(0x0000, TAG, `Widget ${formId} initialized successfully`);
-    } catch (error) {
-      hilog.error(0x0000, TAG, `Failed to initialize widget ${formId}: ${error}`);
-    }
-  }
-
-  /**
-   * 更新卡片数据
-   */
-  private async updateWidgetData(formId: string): Promise<void> {
-    try {
-      const currentState = await this.playerControlService.getCurrentPlayState();
-      await this.widgetDataManager.updateWidget(formId, currentState);
-      
-      hilog.info(0x0000, TAG, `Widget ${formId} updated successfully`);
-    } catch (error) {
-      hilog.error(0x0000, TAG, `Failed to update widget ${formId}: ${error}`);
-    }
-  }
-
-  /**
-   * 处理卡片事件
-   */
-  private async handleWidgetEvent(formId: string, eventData: Object): Promise<void> {
-    try {
-      // 解析事件数据
-      const actionData: ESObject = eventData as ESObject;
-      const action: string = (actionData['action'] as string) || 'play_pause';
-      const params: Object = (actionData['params'] as Object) || new Object();
-      
-      hilog.info(0x0000, TAG, `Handling widget event: ${action}`);
-      
-      switch (action) {
-        case PLAY_PAUSE_EVENT:
-          await this.playerControlService.sendControlCommand(WidgetCommand.PLAY_PAUSE);
-          break;
-          
-        case NEXT_SONG_EVENT:
-          await this.playerControlService.sendControlCommand(WidgetCommand.NEXT_SONG);
-          break;
-          
-        case PREV_SONG_EVENT:
-          await this.playerControlService.sendControlCommand(WidgetCommand.PREV_SONG);
-          break;
-          
-        case SEEK_TO_EVENT:
-          const seekParams: ESObject = params as ESObject;
-          const controlParams: WidgetControlParams = { 
-            percentage: (seekParams['percentage'] as number) || 0,
-            position: 0 
-          };
-          await this.playerControlService.sendControlCommand(WidgetCommand.SEEK_TO, controlParams);
-          break;
-          
-        case OPEN_APP_EVENT:
-          // 启动主应用到首页
-          await this.playerControlService.launchMainApp(PAGE_MAIN);
-          break;
-          
-        case OPEN_PLAYER_EVENT:
-          // 启动主应用到播放器页面
-          await this.playerControlService.launchMainApp(PAGE_PLAYER);
-          break;
-          
-        default:
-          hilog.warn(0x0000, TAG, `Unknown widget action: ${action}`);
-          return;
-      }
-      
-      // 处理完命令后更新卡片状态
-      setTimeout((): void => {
-        this.updateWidgetData(formId);
-      }, 200);
-      
-    } catch (error) {
-      hilog.error(0x0000, TAG, `Failed to handle widget event: ${error}`);
-    }
-  }
-}

+ 447 - 0
entry/src/main/ets/pages/WidgetConfigPage.ets

@@ -0,0 +1,447 @@
+import { router } from '@kit.ArkUI';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import window from '@ohos.window';
+import { WidgetConfigManager } from '../common/widget/WidgetConfigManager';
+import { WidgetSize, WidgetTheme, WidgetConfig } from '../common/widget/WidgetTypes';
+
+const TAG = 'WidgetConfigPage';
+
+/**
+ * 卡片配置页面
+ * 提供卡片个性化设置选项
+ * 需求: 6.3, 6.5
+ */
+@Entry
+@Component
+struct WidgetConfigPage {
+  @State formId: string = '';
+  @State currentSize: WidgetSize = WidgetSize.MEDIUM;
+  @State showProgress: boolean = true;
+  @State showCover: boolean = true;
+  @State selectedTheme: WidgetTheme = WidgetTheme.AUTO;
+  @State isLoading: boolean = false;
+  @State returnToWidget: boolean = false;
+
+  private configManager: WidgetConfigManager = WidgetConfigManager.getInstance();
+
+  aboutToAppear(): void {
+    this.loadConfigFromParams();
+  }
+
+  /**
+   * 从路由参数加载配置
+   */
+  private loadConfigFromParams(): void {
+    try {
+      // 优先从AppStorage获取参数(来自widget启动)
+      const appStorageParams = AppStorage.get('widgetConfigParams') as Record<string, Object>;
+      let params: Record<string, Object>;
+      
+      if (appStorageParams) {
+        params = appStorageParams;
+        // 清除AppStorage中的参数
+        AppStorage.delete('widgetConfigParams');
+      } else {
+        // 回退到router参数(来自应用内导航)
+        params = router.getParams() as Record<string, Object>;
+      }
+      
+      this.formId = (params['formId'] as string) || '';
+      this.currentSize = (params['currentSize'] as WidgetSize) || WidgetSize.MEDIUM;
+      this.returnToWidget = (params['returnToWidget'] as boolean) || false;
+      
+      const configStr = params['currentConfig'] as string;
+      if (configStr) {
+        const config = JSON.parse(configStr) as WidgetConfig;
+        this.showProgress = config.showProgress;
+        this.showCover = config.showCover;
+        this.selectedTheme = config.theme as WidgetTheme;
+      }
+      
+      hilog.info(0x0000, TAG, `Loaded config for form: ${this.formId}, size: ${this.currentSize}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to load config from params: ${error}`);
+    }
+  }
+
+  /**
+   * 保存配置
+   */
+  private async saveConfig(): Promise<void> {
+    if (this.isLoading) return;
+    
+    this.isLoading = true;
+    
+    try {
+      const newConfig: WidgetConfig = {
+        size: this.currentSize,
+        theme: this.selectedTheme,
+        showProgress: this.showProgress,
+        showCover: this.showCover
+      };
+      
+      await this.configManager.saveWidgetConfig(this.formId, this.currentSize, newConfig);
+      
+      hilog.info(0x0000, TAG, `Config saved for form: ${this.formId}`);
+      
+      // 返回上一页
+      if (this.returnToWidget) {
+        // 如果是从卡片启动的,返回到主页面
+        const windowStage = AppStorage.get('windowStage') as window.WindowStage;
+        if (windowStage) {
+          windowStage.loadContent('pages/NewIndex');
+        }
+      } else {
+        // 如果是从应用内导航的,使用router返回
+        router.back();
+      }
+      
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to save config: ${error}`);
+    } finally {
+      this.isLoading = false;
+    }
+  }
+
+  /**
+   * 取消配置
+   */
+  private cancelConfig(): void {
+    if (this.returnToWidget) {
+      // 如果是从卡片启动的,返回到主页面
+      const windowStage = AppStorage.get('windowStage') as window.WindowStage;
+      if (windowStage) {
+        windowStage.loadContent('pages/NewIndex');
+      }
+    } else {
+      // 如果是从应用内导航的,使用router返回
+      router.back();
+    }
+  }
+
+  /**
+   * 获取主题显示名称
+   */
+  private getThemeDisplayName(theme: WidgetTheme): string {
+    switch (theme) {
+      case WidgetTheme.AUTO:
+        return '跟随系统';
+      case WidgetTheme.LIGHT:
+        return '浅色主题';
+      case WidgetTheme.DARK:
+        return '深色主题';
+      default:
+        return '跟随系统';
+    }
+  }
+
+  /**
+   * 获取尺寸显示名称
+   */
+  private getSizeDisplayName(size: WidgetSize): string {
+    switch (size) {
+      case WidgetSize.SMALL:
+        return '小尺寸 (2×1)';
+      case WidgetSize.MEDIUM:
+        return '中等尺寸 (4×2)';
+      case WidgetSize.LARGE:
+        return '大尺寸 (4×3)';
+      default:
+        return '中等尺寸 (4×2)';
+    }
+  }
+
+  build() {
+    Column() {
+      // 标题栏
+      Row() {
+        Button('取消')
+          .fontSize(16)
+          .fontColor('#FF007DFF')
+          .backgroundColor(Color.Transparent)
+          .onClick(() => this.cancelConfig())
+
+        Text('卡片设置')
+          .fontSize(18)
+          .fontWeight(FontWeight.Medium)
+          .fontColor('#E6000000')
+          .layoutWeight(1)
+          .textAlign(TextAlign.Center)
+
+        Button('保存')
+          .fontSize(16)
+          .fontColor(this.isLoading ? '#99007DFF' : '#FF007DFF')
+          .backgroundColor(Color.Transparent)
+          .enabled(!this.isLoading)
+          .onClick(() => this.saveConfig())
+      }
+      .width('100%')
+      .height(56)
+      .padding({ left: 16, right: 16 })
+      .justifyContent(FlexAlign.SpaceBetween)
+      .alignItems(VerticalAlign.Center)
+      .backgroundColor('#FFFFFF')
+      .border({
+        width: { bottom: 0.5 },
+        color: '#E5E5E5'
+      })
+
+      // 配置内容
+      Scroll() {
+        Column() {
+          // 卡片信息
+          Column() {
+            Text('卡片信息')
+              .fontSize(14)
+              .fontColor('#99000000')
+              .fontWeight(FontWeight.Medium)
+              .width('100%')
+              .textAlign(TextAlign.Start)
+              .margin({ bottom: 12 })
+
+            Row() {
+              Text('卡片尺寸')
+                .fontSize(16)
+                .fontColor('#E6000000')
+                .layoutWeight(1)
+
+              Text(this.getSizeDisplayName(this.currentSize))
+                .fontSize(16)
+                .fontColor('#99000000')
+            }
+            .width('100%')
+            .height(48)
+            .padding({ left: 16, right: 16 })
+            .backgroundColor('#FFFFFF')
+            .borderRadius(8)
+            .justifyContent(FlexAlign.SpaceBetween)
+            .alignItems(VerticalAlign.Center)
+
+            Row() {
+              Text('卡片ID')
+                .fontSize(16)
+                .fontColor('#E6000000')
+                .layoutWeight(1)
+
+              Text(this.formId.substring(0, 8) + '...')
+                .fontSize(14)
+                .fontColor('#99000000')
+            }
+            .width('100%')
+            .height(48)
+            .padding({ left: 16, right: 16 })
+            .backgroundColor('#FFFFFF')
+            .borderRadius(8)
+            .justifyContent(FlexAlign.SpaceBetween)
+            .alignItems(VerticalAlign.Center)
+            .margin({ top: 8 })
+          }
+          .width('100%')
+          .margin({ bottom: 24 })
+
+          // 显示设置
+          Column() {
+            Text('显示设置')
+              .fontSize(14)
+              .fontColor('#99000000')
+              .fontWeight(FontWeight.Medium)
+              .width('100%')
+              .textAlign(TextAlign.Start)
+              .margin({ bottom: 12 })
+
+            // 显示进度条
+            Row() {
+              Column() {
+                Text('显示播放进度')
+                  .fontSize(16)
+                  .fontColor('#E6000000')
+                  .width('100%')
+                  .textAlign(TextAlign.Start)
+
+                Text('显示播放进度条和时间信息')
+                  .fontSize(12)
+                  .fontColor('#99000000')
+                  .width('100%')
+                  .textAlign(TextAlign.Start)
+                  .margin({ top: 2 })
+              }
+              .layoutWeight(1)
+              .alignItems(HorizontalAlign.Start)
+
+              Toggle({ type: ToggleType.Switch, isOn: this.showProgress })
+                .selectedColor('#FF007DFF')
+                .switchPointColor('#FFFFFF')
+                .onChange((isOn: boolean) => {
+                  this.showProgress = isOn;
+                })
+            }
+            .width('100%')
+            .padding({ left: 16, right: 16, top: 12, bottom: 12 })
+            .backgroundColor('#FFFFFF')
+            .borderRadius(8)
+            .justifyContent(FlexAlign.SpaceBetween)
+            .alignItems(VerticalAlign.Center)
+
+            // 显示专辑封面
+            if (this.currentSize === WidgetSize.LARGE) {
+              Row() {
+                Column() {
+                  Text('显示专辑封面')
+                    .fontSize(16)
+                    .fontColor('#E6000000')
+                    .width('100%')
+                    .textAlign(TextAlign.Start)
+
+                  Text('在大尺寸卡片中显示专辑封面')
+                    .fontSize(12)
+                    .fontColor('#99000000')
+                    .width('100%')
+                    .textAlign(TextAlign.Start)
+                    .margin({ top: 2 })
+                }
+                .layoutWeight(1)
+                .alignItems(HorizontalAlign.Start)
+
+                Toggle({ type: ToggleType.Switch, isOn: this.showCover })
+                  .selectedColor('#FF007DFF')
+                  .switchPointColor('#FFFFFF')
+                  .onChange((isOn: boolean) => {
+                    this.showCover = isOn;
+                  })
+              }
+              .width('100%')
+              .padding({ left: 16, right: 16, top: 12, bottom: 12 })
+              .backgroundColor('#FFFFFF')
+              .borderRadius(8)
+              .justifyContent(FlexAlign.SpaceBetween)
+              .alignItems(VerticalAlign.Center)
+              .margin({ top: 8 })
+            }
+          }
+          .width('100%')
+          .margin({ bottom: 24 })
+
+          // 主题设置
+          Column() {
+            Text('主题设置')
+              .fontSize(14)
+              .fontColor('#99000000')
+              .fontWeight(FontWeight.Medium)
+              .width('100%')
+              .textAlign(TextAlign.Start)
+              .margin({ bottom: 12 })
+
+            Column() {
+              ForEach([WidgetTheme.AUTO, WidgetTheme.LIGHT, WidgetTheme.DARK], (theme: WidgetTheme, index: number) => {
+                Row() {
+                  Text(this.getThemeDisplayName(theme))
+                    .fontSize(16)
+                    .fontColor('#E6000000')
+                    .layoutWeight(1)
+
+                  Radio({ value: theme, group: 'theme' })
+                    .checked(this.selectedTheme === theme)
+                    .onChange((isChecked: boolean) => {
+                      if (isChecked) {
+                        this.selectedTheme = theme;
+                      }
+                    })
+                }
+                .width('100%')
+                .height(48)
+                .padding({ left: 16, right: 16 })
+                .justifyContent(FlexAlign.SpaceBetween)
+                .alignItems(VerticalAlign.Center)
+                .stateStyles({
+                  pressed: {
+                    .backgroundColor('#F5F5F5')
+                  },
+                  normal: {
+                    .backgroundColor('#FFFFFF')
+                  }
+                })
+                .onClick(() => {
+                  this.selectedTheme = theme;
+                })
+
+                if (index < 2) {
+                  Divider()
+                    .color('#F0F0F0')
+                    .strokeWidth(0.5)
+                    .margin({ left: 16, right: 16 })
+                }
+              })
+            }
+            .width('100%')
+            .backgroundColor('#FFFFFF')
+            .borderRadius(8)
+          }
+          .width('100%')
+          .margin({ bottom: 24 })
+
+          // 操作按钮
+          Column() {
+            Text('卡片操作')
+              .fontSize(14)
+              .fontColor('#99000000')
+              .fontWeight(FontWeight.Medium)
+              .width('100%')
+              .textAlign(TextAlign.Start)
+              .margin({ bottom: 12 })
+
+            Button('删除卡片')
+              .width('100%')
+              .height(48)
+              .fontSize(16)
+              .fontColor('#FF4444')
+              .backgroundColor('#FFFFFF')
+              .border({
+                width: 1,
+                color: '#FF4444',
+                radius: 8
+              })
+              .onClick(() => {
+                // 显示删除确认对话框
+                AlertDialog.show({
+                  title: '删除卡片',
+                  message: '确定要删除这个卡片吗?删除后无法恢复。',
+                  primaryButton: {
+                    value: '取消',
+                    action: () => {
+                      hilog.info(0x0000, TAG, 'Delete cancelled');
+                    }
+                  },
+                  secondaryButton: {
+                    value: '删除',
+                    fontColor: '#FF4444',
+                    action: () => {
+                      hilog.info(0x0000, TAG, 'Widget delete confirmed');
+                      // 这里应该调用删除卡片的逻辑
+                      if (this.returnToWidget) {
+                        // 如果是从卡片启动的,返回到主页面
+                        const windowStage = AppStorage.get('windowStage') as window.WindowStage;
+                        if (windowStage) {
+                          windowStage.loadContent('pages/NewIndex');
+                        }
+                      } else {
+                        // 如果是从应用内导航的,使用router返回
+                        router.back();
+                      }
+                    }
+                  }
+                });
+              })
+          }
+          .width('100%')
+        }
+        .width('100%')
+        .padding({ left: 16, right: 16, top: 16, bottom: 32 })
+      }
+      .layoutWeight(1)
+      .backgroundColor('#F8F8F8')
+    }
+    .width('100%')
+    .height('100%')
+    .backgroundColor('#F8F8F8')
+  }
+}

+ 223 - 0
entry/src/main/ets/pages/WidgetDeleteConfirmPage.ets

@@ -0,0 +1,223 @@
+import { router } from '@kit.ArkUI';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import window from '@ohos.window';
+import { WidgetConfigManager } from '../common/widget/WidgetConfigManager';
+import { WidgetDataManager } from '../common/widget/WidgetDataManager';
+
+const TAG = 'WidgetDeleteConfirmPage';
+
+/**
+ * 卡片删除确认页面
+ * 提供卡片删除确认和相关操作
+ * 需求: 6.3, 6.5
+ */
+@Entry
+@Component
+struct WidgetDeleteConfirmPage {
+  @State formId: string = '';
+  @State isDeleting: boolean = false;
+
+  private configManager: WidgetConfigManager = WidgetConfigManager.getInstance();
+  private dataManager: WidgetDataManager = new WidgetDataManager();
+
+  aboutToAppear(): void {
+    this.loadParamsFromRouter();
+  }
+
+  /**
+   * 从路由参数加载数据
+   */
+  private loadParamsFromRouter(): void {
+    try {
+      // 优先从AppStorage获取参数(来自widget启动)
+      const appStorageParams = AppStorage.get('widgetConfigParams') as Record<string, Object>;
+      let params: Record<string, Object>;
+      
+      if (appStorageParams) {
+        params = appStorageParams;
+        // 清除AppStorage中的参数
+        AppStorage.delete('widgetConfigParams');
+      } else {
+        // 回退到router参数(来自应用内导航)
+        params = router.getParams() as Record<string, Object>;
+      }
+      
+      this.formId = (params['formId'] as string) || '';
+      
+      hilog.info(0x0000, TAG, `Loaded delete confirmation for form: ${this.formId}`);
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to load params: ${error}`);
+    }
+  }
+
+  /**
+   * 确认删除卡片
+   */
+  private async confirmDelete(): Promise<void> {
+    if (this.isDeleting) return;
+    
+    this.isDeleting = true;
+    
+    try {
+      // 清理卡片相关数据
+      await this.configManager.removeWidgetConfig(this.formId);
+      await this.dataManager.removeWidgetData(this.formId);
+      
+      hilog.info(0x0000, TAG, `Widget ${this.formId} deleted successfully`);
+      
+      // 显示删除成功提示
+      AlertDialog.show({
+        title: '删除成功',
+        message: '卡片已成功删除',
+        primaryButton: {
+          value: '确定',
+          action: () => {
+            // 返回到主页面
+            const windowStage = AppStorage.get('windowStage') as window.WindowStage;
+            if (windowStage) {
+              windowStage.loadContent('pages/NewIndex');
+            } else {
+              router.back();
+            }
+          }
+        }
+      });
+      
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to delete widget: ${error}`);
+      
+      // 显示删除失败提示
+      AlertDialog.show({
+        title: '删除失败',
+        message: '删除卡片时发生错误,请稍后重试',
+        primaryButton: {
+          value: '确定',
+          action: () => {
+            // 保持在当前页面
+          }
+        }
+      });
+    } finally {
+      this.isDeleting = false;
+    }
+  }
+
+  /**
+   * 取消删除
+   */
+  private cancelDelete(): void {
+    // 返回到主页面(因为删除确认页面通常是从卡片启动的)
+    const windowStage = AppStorage.get('windowStage') as window.WindowStage;
+    if (windowStage) {
+      windowStage.loadContent('pages/NewIndex');
+    } else {
+      router.back();
+    }
+  }
+
+  build() {
+    Column() {
+      // 标题栏
+      Row() {
+        Button('取消')
+          .fontSize(16)
+          .fontColor('#FF007DFF')
+          .backgroundColor(Color.Transparent)
+          .onClick(() => this.cancelDelete())
+
+        Text('删除卡片')
+          .fontSize(18)
+          .fontWeight(FontWeight.Medium)
+          .fontColor('#E6000000')
+          .layoutWeight(1)
+          .textAlign(TextAlign.Center)
+
+        // 占位,保持布局平衡
+        Text('')
+          .width(48)
+      }
+      .width('100%')
+      .height(56)
+      .padding({ left: 16, right: 16 })
+      .justifyContent(FlexAlign.SpaceBetween)
+      .alignItems(VerticalAlign.Center)
+      .backgroundColor('#FFFFFF')
+      .border({
+        width: { bottom: 0.5 },
+        color: '#E5E5E5'
+      })
+
+      // 内容区域
+      Column() {
+        // 警告图标
+        Image($r('app.media.ic_warning'))
+          .width(64)
+          .height(64)
+          .fillColor('#FF9500')
+          .margin({ bottom: 24 })
+
+        // 标题
+        Text('确认删除卡片')
+          .fontSize(20)
+          .fontWeight(FontWeight.Medium)
+          .fontColor('#E6000000')
+          .margin({ bottom: 16 })
+
+        // 描述信息
+        Text('删除后,该卡片将从桌面移除,所有相关的个性化设置也将被清除。此操作无法撤销。')
+          .fontSize(16)
+          .fontColor('#99000000')
+          .textAlign(TextAlign.Center)
+          .lineHeight(24)
+          .margin({ bottom: 8 })
+
+        // 卡片信息
+        if (this.formId) {
+          Text(`卡片ID: ${this.formId.substring(0, 8)}...`)
+            .fontSize(14)
+            .fontColor('#66000000')
+            .margin({ bottom: 32 })
+        }
+
+        // 操作按钮
+        Column() {
+          // 删除按钮
+          Button(this.isDeleting ? '删除中...' : '确认删除')
+            .width('100%')
+            .height(48)
+            .fontSize(16)
+            .fontColor('#FFFFFF')
+            .backgroundColor(this.isDeleting ? '#CC4444' : '#FF4444')
+            .borderRadius(8)
+            .enabled(!this.isDeleting)
+            .onClick(() => this.confirmDelete())
+
+          // 取消按钮
+          Button('取消')
+            .width('100%')
+            .height(48)
+            .fontSize(16)
+            .fontColor('#FF007DFF')
+            .backgroundColor('#FFFFFF')
+            .border({
+              width: 1,
+              color: '#FF007DFF',
+              radius: 8
+            })
+            .margin({ top: 12 })
+            .onClick(() => this.cancelDelete())
+        }
+        .width('100%')
+        .padding({ left: 32, right: 32 })
+      }
+      .layoutWeight(1)
+      .width('100%')
+      .padding({ left: 24, right: 24, top: 64 })
+      .justifyContent(FlexAlign.Start)
+      .alignItems(HorizontalAlign.Center)
+    }
+    .width('100%')
+    .height('100%')
+    .backgroundColor('#F8F8F8')
+  }
+}

+ 395 - 24
entry/src/main/ets/view/LocalMusic.ets

@@ -23,6 +23,7 @@ import Logger from '../common/util/Logger';
 import { photoAccessHelper } from '@kit.MediaLibraryKit';
 import { Utility } from '../common/util/Utility';
 import { BusinessError, emitter } from '@kit.BasicServicesKit';
+import commonEventManager from '@ohos.commonEventManager';
 import { BubbleBean } from '../viewmodel/BubbleBean';
 import { PopupPosition, XPopup } from '@chinalike/popup';
 import { common, ConfigurationConstant } from '@kit.AbilityKit';
@@ -36,6 +37,15 @@ import { effectKit } from '@kit.ArkGraphics2D';
 import { ColorConversion } from '../common/util/ColorConversion';
 import { LyricController, LyricParser, LyricView2 } from '@seagazer/cclyric';
 import { AvSessionController } from '../controller/AvSessionController';
+import { AvSessionWidgetListener } from '../common/widget/AvSessionWidgetListener';
+import { WidgetData, PlayProgress, PlayState, SongInfo, PlaylistState, PlayerStateBroadcastData } from '../common/widget/WidgetTypes';
+import {
+  WIDGET_CONTROL_EVENT,
+  WIDGET_REQUEST_STATE_EVENT,
+  PLAYER_STATE_CHANGED_EVENT,
+  PLAYER_SONG_CHANGED_EVENT,
+  PLAYER_PROGRESS_CHANGED_EVENT
+} from '../common/widget/WidgetEventConstants';
 import {
 // DeviceChangeReason,
   IjkMediaPlayer,
@@ -77,6 +87,7 @@ import { TextNodeController } from './PipLyricTextBuilder';
 import { IndexerView } from './IndexerView';
 import { KeyCode } from '@kit.InputKit';
 import { KnockController } from '../controller/KnockController';
+import { WidgetCommand, EventData, WidgetControlParams } from '../common/widget/WidgetTypes';
 // import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
 const TAG = 'LocalMusic';
 
@@ -186,6 +197,9 @@ export struct LocalMusic {
   @State isCircleBtn: boolean = false //是否圆形播放按钮
   @State twoFingerType: number = 3 //双支放大缩小的类型
   @State isScrollHide: boolean = false //是否滚动隐藏
+  private widgetEventSubscriber: commonEventManager.CommonEventSubscriber | null = null; // 卡片事件订阅者
+  private lastProgressBroadcastTime: number = 0; // 上次进度广播时间
+  private readonly PROGRESS_BROADCAST_INTERVAL: number = 1000; // 进度广播间隔(1秒)
   @State isSameTimePlay: boolean = false //是否和其他app同时播放
   @State isStartAutoPlay: boolean = false //启动后自动播放
   @State isShowPlayPageBack: boolean = false //是否显示播放页返回键
@@ -525,6 +539,8 @@ export struct LocalMusic {
 
     this.makeWorker()
 
+    // 初始化卡片事件监听器
+    this.initWidgetEventListener();
 
     //折叠屏的屏幕显示模式变化
     display.on('foldDisplayModeChange', (data) => {
@@ -545,21 +561,24 @@ export struct LocalMusic {
     AppStorage.setOrCreate('themeColor', themeColor);
     this.themeColor = themeColor;
     this.doChangeSetting()
-    this.windowClass.on('windowSizeChange', (size) => {
-      LogUtil.info('onecold  windowSizeChange')
-      this.doChangeBarHeight()
-      let viewWidth = px2vp(size.width);
-      let viewHeight = px2vp(size.height);
-      if(this.isPhoneLan()){
-        this.is_auto_hide_progress = false
-        setTimeout(() => {
-          this.is_auto_hide_progress = true
-        }, 6000)
-      }else{
-        this.startAutoHide()
-      }
 
-    });
+    // 安全地注册窗口大小变化监听器
+    if (this.windowClass) {
+      this.windowClass.on('windowSizeChange', (size) => {
+      LogUtil.info('onecold  windowSizeChange')
+        this.doChangeBarHeight()
+        let viewWidth = px2vp(size.width);
+        let viewHeight = px2vp(size.height);
+        if(this.isPhoneLan()){
+          this.is_auto_hide_progress = false
+          setTimeout(() => {
+            this.is_auto_hide_progress = true
+          }, 6000)
+        }else{
+          this.startAutoHide()
+        }
+      });
+    }
 
     this.eventHub.on('onStateChange', (fg: boolean) => {
       if (fg && this.curState === 'STARTED') {
@@ -871,7 +890,30 @@ export struct LocalMusic {
     }
 
     this.stopPip();
-    this.destroyPipController()
+    this.destroyPipController();
+
+    // 清理卡片事件监听器
+    if (this.widgetEventSubscriber) {
+      try {
+        commonEventManager.unsubscribe(this.widgetEventSubscriber, (err: BusinessError | undefined) => {
+          if (err) {
+            LogUtils.getInstance().error(`Failed to unsubscribe widget events: ${JSON.stringify(err)}`);
+          } else {
+            LogUtils.getInstance().LOGI('Widget event listener unsubscribed successfully');
+          }
+        });
+      } catch (error) {
+        LogUtils.getInstance().error(`Error during widget event cleanup: ${error}`);
+      }
+    }
+
+    // 清理AvSession卡片监听器
+    try {
+      this.avSessionWidgetListener.destroy();
+      LogUtils.getInstance().LOGI('AvSession widget listener destroyed successfully');
+    } catch (error) {
+      LogUtils.getInstance().error(`Error during AvSession widget listener cleanup: ${error}`);
+    }
   }
 
   async getSortedFiles(curPath: string, isWorkerPost?: boolean, destPath?: string, isOpen?: boolean) {
@@ -6272,6 +6314,7 @@ export struct LocalMusic {
   eventHub = getContext().eventHub;
   //投播组件
   private avSessionController: AvSessionController = AvSessionController.getInstance(false);
+  private avSessionWidgetListener: AvSessionWidgetListener = AvSessionWidgetListener.getInstance();
   private castController: avSession.AVCastController | undefined = undefined;
   @State isCastPlaying: boolean = false;
   @State currentTime2: number = 0;
@@ -8923,11 +8966,13 @@ export struct LocalMusic {
    *
    */
   setOrientation(orientation: number) {
-    this.windowClass.setPreferredOrientation(orientation).then(() => {
-      Logger.info('setWindowOrientation: ' + orientation + ' Succeeded.');
-    }).catch((err: BusinessError) => {
-      Logger.info('setWindowOrientation: ' + orientation + ' Failed. Cause: ' + JSON.stringify(err));
-    });
+    if (this.windowClass) {
+      this.windowClass.setPreferredOrientation(orientation).then(() => {
+        Logger.info('setWindowOrientation: ' + orientation + ' Succeeded.');
+      }).catch((err: BusinessError) => {
+        Logger.info('setWindowOrientation: ' + orientation + ' Failed. Cause: ' + JSON.stringify(err));
+      });
+    }
   }
 
   /**
@@ -9380,6 +9425,8 @@ export struct LocalMusic {
     this.updateSessionPlayState(true)
     this.watchStatus();
     this.updateLastPlayTimeStr(this.videoUrl)
+    // 广播状态变化到卡片
+    this.broadcastPlayerState();
   }
 
   updateLastPlayTimeStr(filePath: string) {
@@ -9473,6 +9520,9 @@ export struct LocalMusic {
 
     }
 
+    // 节流广播进度更新到卡片
+    this.broadcastProgressIfNeeded();
+
 
   }
 
@@ -10329,6 +10379,8 @@ export struct LocalMusic {
         this.pipController.updatePiPControlStatus(PiPWindow.PiPControlType.VIDEO_PLAY_PAUSE,
           PiPWindow.PiPControlStatus.PAUSE);
       }
+      // 广播状态变化到卡片
+      this.broadcastPlayerState();
     }
   }
 
@@ -10341,6 +10393,8 @@ export struct LocalMusic {
     this.updateSessionPlayState(false)
     this.playChange()
     this.watchStatus();
+    // 广播状态变化到卡片
+    this.broadcastPlayerState();
 
   }
 
@@ -10468,6 +10522,11 @@ export struct LocalMusic {
     }else{
       this.cover = this.songList[this.curIndex].pixelMapPath
       this.startPlayOrResumePlay()
+      // 广播歌曲变化到卡片
+      setTimeout(() => {
+        this.broadcastPlayerState();
+      }, 200);
+
     }
 
   }
@@ -10565,6 +10624,11 @@ export struct LocalMusic {
     this.videoUrl = this.songList[this.curIndex].filePath;
     this.name = this.songList[this.curIndex].name
     this.artist = this.songList[this.curIndex].artist
+    this.startPlayOrResumePlay()
+    // 广播歌曲变化到卡片
+    setTimeout(() => {
+      this.broadcastPlayerState();
+    }, 200);
     this.changeImageAnimation()
   }
 
@@ -10614,10 +10678,12 @@ export struct LocalMusic {
    */
   watchStatus() {
     // let windowClass = GlobalContext.getContext().getObject('windowClass') as window.Window;
-    if (this.CONTROL_PlayStatus === PlayStatus.PLAY) {
-      this.windowClass.setWindowKeepScreenOn(true);
-    } else {
-      this.windowClass.setWindowKeepScreenOn(false);
+    if (this.windowClass) {
+      if (this.CONTROL_PlayStatus === PlayStatus.PLAY) {
+        this.windowClass.setWindowKeepScreenOn(true);
+      } else {
+        this.windowClass.setWindowKeepScreenOn(false);
+      }
     }
   }
 
@@ -11001,6 +11067,311 @@ export struct LocalMusic {
    * 穿山甲广告代码结束
    */
 
+  /**
+   * 初始化卡片事件监听器
+   */
+  private async initWidgetEventListener(): Promise<void> {
+    try {
+      // 创建订阅信息
+      const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
+        events: [
+          WIDGET_CONTROL_EVENT,
+          WIDGET_REQUEST_STATE_EVENT
+        ]
+      };
+
+      // 创建订阅者
+      this.widgetEventSubscriber = await commonEventManager.createSubscriber(subscribeInfo);
+
+      // 订阅事件
+      await commonEventManager.subscribe(this.widgetEventSubscriber, (err, data: commonEventManager.CommonEventData) => {
+        if (!err) {
+          this.handleWidgetEvent(data);
+        } else {
+          LogUtils.getInstance().error(`Widget event subscription error: ${JSON.stringify(err)}`);
+        }
+      });
+
+      // 初始化AvSession卡片监听器
+      this.initAvSessionWidgetListener();
+
+      LogUtils.getInstance().LOGI('Widget event listener initialized successfully');
+    } catch (error) {
+      LogUtils.getInstance().error(`Failed to initialize widget event listener: ${error}`);
+    }
+  }
+
+  /**
+   * 初始化AvSession卡片监听器
+   */
+  private initAvSessionWidgetListener(): void {
+    try {
+      // 注册AvSession状态变化监听器,用于同步卡片状态
+      this.avSessionWidgetListener.addStateListener((widgetData) => {
+        // AvSession状态变化时,自动广播到卡片
+        this.broadcastAvSessionStateToWidget(widgetData);
+      });
+
+      LogUtils.getInstance().LOGI('AvSession widget listener initialized successfully');
+    } catch (error) {
+      LogUtils.getInstance().error(`Failed to initialize AvSession widget listener: ${error}`);
+    }
+  }
+
+  /**
+   * 广播AvSession状态到卡片
+   */
+  private broadcastAvSessionStateToWidget(widgetData: WidgetData): void {
+    try {
+      // 广播播放状态变化
+      const statePublishInfo: commonEventManager.CommonEventPublishData = {
+        data: JSON.stringify(widgetData)
+      };
+
+      commonEventManager.publish(PLAYER_STATE_CHANGED_EVENT, statePublishInfo, (err) => {
+        if (err) {
+          LogUtils.getInstance().error(`Failed to broadcast AvSession state: ${JSON.stringify(err)}`);
+        } else {
+          LogUtils.getInstance().LOGI('AvSession state broadcasted to widget successfully');
+        }
+      });
+
+      // 如果有歌曲信息变化,也广播歌曲变化事件
+      if (widgetData.currentSong) {
+        const songPublishInfo: commonEventManager.CommonEventPublishData = {
+          data: JSON.stringify({ currentSong: widgetData.currentSong })
+        };
+
+        commonEventManager.publish(PLAYER_SONG_CHANGED_EVENT, songPublishInfo, (err) => {
+          if (err) {
+            LogUtils.getInstance().error(`Failed to broadcast song change: ${JSON.stringify(err)}`);
+          }
+        });
+      }
+
+      // 如果有进度信息变化,也广播进度变化事件
+      if (widgetData.progress) {
+        const progressPublishInfo: commonEventManager.CommonEventPublishData = {
+          data: JSON.stringify(widgetData.progress)
+        };
+
+        commonEventManager.publish(PLAYER_PROGRESS_CHANGED_EVENT, progressPublishInfo, (err) => {
+          if (err) {
+            LogUtils.getInstance().error(`Failed to broadcast progress change: ${JSON.stringify(err)}`);
+          }
+        });
+      }
+    } catch (error) {
+      LogUtils.getInstance().error(`Failed to broadcast AvSession state to widget: ${error}`);
+    }
+  }
+
+  /**
+   * 处理卡片事件
+   */
+  private handleWidgetEvent(eventData: commonEventManager.CommonEventData): void {
+    try {
+      LogUtils.getInstance().LOGI(`Received widget event: ${eventData.event}, data: ${eventData.data}`);
+
+      if (eventData.event === WIDGET_CONTROL_EVENT) {
+        // 处理控制命令
+        this.handleWidgetControlCommand(eventData.data || '{}');
+      } else if (eventData.event === WIDGET_REQUEST_STATE_EVENT) {
+        // 处理状态请求
+        this.handleWidgetStateRequest();
+      }
+    } catch (error) {
+      LogUtils.getInstance().error(`Failed to handle widget event: ${error}`);
+    }
+  }
+
+  /**
+   * 处理卡片控制命令
+   */
+  private handleWidgetControlCommand(dataStr: string): void {
+    try {
+      const eventData = JSON.parse(dataStr) as EventData;
+      LogUtils.getInstance().LOGI(`Processing widget command: ${eventData.command}`);
+
+      switch (eventData.command) {
+        case WidgetCommand.PLAY_PAUSE:
+          this.playOrPause();
+          break;
+        case WidgetCommand.NEXT_SONG:
+          this.playNext();
+          break;
+        case WidgetCommand.PREV_SONG:
+          this.playPrevious();
+          break;
+        case WidgetCommand.SEEK_TO:
+          if (eventData.params && eventData.params.percentage !== undefined) {
+            this.handleWidgetSeekTo(eventData.params.percentage);
+          }
+          break;
+        case WidgetCommand.OPEN_APP:
+          // 应用已经在运行,不需要额外操作
+          LogUtils.getInstance().LOGI('Widget requested to open app - already running');
+          break;
+        case WidgetCommand.OPEN_PLAYER:
+          // 应用已经在运行,可以切换到播放页面
+          this.handleWidgetOpenPlayer();
+          break;
+        default:
+          LogUtils.getInstance().warn(`Unknown widget command: ${eventData.command}`);
+          break;
+      }
+
+      // 命令处理完成后,广播状态更新
+      setTimeout(() => {
+        this.broadcastPlayerState();
+      }, 100);
+    } catch (error) {
+      LogUtils.getInstance().error(`Failed to handle widget control command: ${error}`);
+    }
+  }
+
+  /**
+   * 处理卡片状态请求
+   */
+  private handleWidgetStateRequest(): void {
+    try {
+      LogUtils.getInstance().LOGI('Processing widget state request');
+      this.broadcastPlayerState();
+    } catch (error) {
+      LogUtils.getInstance().error(`Failed to handle widget state request: ${error}`);
+    }
+  }
+
+  /**
+   * 处理卡片进度跳转
+   */
+  private handleWidgetSeekTo(percentage: number): void {
+    try {
+      if (this.mIjkMediaPlayer && this.duration > 0) {
+        const targetPosition = (percentage / 100) * this.duration;
+        this.seekTo(targetPosition.toString());
+        LogUtils.getInstance().LOGI(`Widget seek to: ${percentage}% (${targetPosition}ms)`);
+      }
+    } catch (error) {
+      LogUtils.getInstance().error(`Failed to handle widget seek: ${error}`);
+    }
+  }
+
+  /**
+   * 处理卡片打开播放器请求
+   */
+  private handleWidgetOpenPlayer(): void {
+    try {
+      // 如果当前不在播放页面,切换到播放页面
+      if (!this.isShowPlay) {
+        this.isShowPlay = true;
+        LogUtils.getInstance().LOGI('Widget opened player page');
+      }
+    } catch (error) {
+      LogUtils.getInstance().error(`Failed to handle widget open player: ${error}`);
+    }
+  }
+
+  /**
+   * 节流广播进度更新
+   */
+  private broadcastProgressIfNeeded(): void {
+    const now = Date.now();
+    if (now - this.lastProgressBroadcastTime >= this.PROGRESS_BROADCAST_INTERVAL) {
+      this.lastProgressBroadcastTime = now;
+      this.broadcastPlayerProgress();
+    }
+  }
+
+  /**
+   * 广播播放进度到卡片
+   */
+  private broadcastPlayerProgress(): void {
+    try {
+      const progressData: PlayProgress = {
+        currentPosition: this.mIjkMediaPlayer?.getCurrentPosition() || 0,
+        duration: this.duration || 0,
+        percentage: this.duration > 0 ? ((this.mIjkMediaPlayer?.getCurrentPosition() || 0) / this.duration) * 100 : 0,
+        currentTimeText: this.currentTime || '00:00',
+        totalTimeText: this.totalTime || '00:00'
+      };
+
+      // 广播播放进度变化
+      const publishInfo: commonEventManager.CommonEventPublishData = {
+        data: JSON.stringify(progressData)
+      };
+
+      commonEventManager.publish(PLAYER_PROGRESS_CHANGED_EVENT, publishInfo, (err) => {
+        if (err) {
+          LogUtils.getInstance().error(`Failed to broadcast player progress: ${JSON.stringify(err)}`);
+        }
+      });
+    } catch (error) {
+      LogUtils.getInstance().error(`Failed to broadcast player progress: ${error}`);
+    }
+  }
+
+  /**
+   * 广播播放器状态到卡片
+   */
+  private broadcastPlayerState(): void {
+    try {
+      const playState: PlayState = {
+        isPlaying: this.CONTROL_PlayStatus === PlayStatus.PLAY,
+        isPaused: this.CONTROL_PlayStatus === PlayStatus.PAUSE,
+        isLoading: this.CONTROL_PlayStatus === PlayStatus.LOADING
+      };
+
+      const currentSong: SongInfo = {
+        id: this.currentSong?.id || '',
+        title: this.name || '暂无播放',
+        artist: this.artist || '未知艺术家',
+        album: this.currentSong?.album || '未知专辑',
+        coverImagePath: this.cover || '',
+        duration: this.duration || 0
+      };
+
+      const progress: PlayProgress = {
+        currentPosition: this.mIjkMediaPlayer?.getCurrentPosition() || 0,
+        duration: this.duration || 0,
+        percentage: this.duration > 0 ? ((this.mIjkMediaPlayer?.getCurrentPosition() || 0) / this.duration) * 100 : 0,
+        currentTimeText: this.stringForTime(this.mIjkMediaPlayer?.getCurrentPosition() || 0),
+        totalTimeText: this.stringForTime(this.duration || 0)
+      };
+
+      const playlist: PlaylistState = {
+        hasNext: this.curIndex < this.songList.length - 1,
+        hasPrevious: this.curIndex > 0,
+        currentIndex: this.curIndex,
+        totalCount: this.songList.length
+      };
+
+      const stateData: PlayerStateBroadcastData = {
+        playState,
+        currentSong,
+        progress,
+        playlist
+      };
+
+      // 广播播放状态变化
+      const publishInfo: commonEventManager.CommonEventPublishData = {
+        data: JSON.stringify(stateData)
+      };
+
+      commonEventManager.publish(PLAYER_STATE_CHANGED_EVENT, publishInfo, (err) => {
+        if (err) {
+          LogUtils.getInstance().error(`Failed to broadcast player state: ${JSON.stringify(err)}`);
+        } else {
+          LogUtils.getInstance().LOGI('Player state broadcasted successfully');
+        }
+      });
+    } catch (error) {
+      LogUtils.getInstance().error(`Failed to broadcast player state: ${error}`);
+    }
+  }
+
+
+
 
 }
 

+ 184 - 22
entry/src/main/ets/widget/pages/PlayerWidgetLarge.ets

@@ -1,8 +1,7 @@
-import { SimpleWidgetController } from '../../common/widget/SimpleWidgetController';
-
 /**
  * 大尺寸播放器卡片 (4x3)
  * 显示专辑封面、完整信息和扩展控制
+ * 需求: 5.3, 2.3, 2.4
  */
 @Entry
 @Component
@@ -19,6 +18,55 @@ struct PlayerWidgetLarge {
   @LocalStorageProp('hasPrevious') hasPrevious: boolean = false;
   @LocalStorageProp('showProgress') showProgress: boolean = true;
   @LocalStorageProp('showCover') showCover: boolean = true;
+  @LocalStorageProp('isLoading') isLoading: boolean = false;
+
+  /**
+   * 获取按钮透明度
+   */
+  private getButtonOpacity(enabled: boolean): number {
+    return enabled ? 1.0 : 0.4;
+  }
+
+  /**
+   * 获取按钮颜色
+   */
+  private getButtonColor(enabled: boolean): string {
+    return enabled ? '#FF007DFF' : '#66000000';
+  }
+
+  /**
+   * 获取播放按钮图标
+   */
+  private getPlayButtonIcon(): Resource {
+    if (this.isLoading) {
+      return $r('app.media.icon_load');
+    }
+    return this.isPlaying ? $r('app.media.hm_pause') : $r('app.media.hm_play');
+  }
+
+  /**
+   * 格式化显示文本
+   */
+  private getDisplayText(text: string, defaultText: string): string {
+    return (!text || text.trim() === '') ? defaultText : text;
+  }
+
+  /**
+   * 获取文本颜色
+   */
+  private getTextColor(text: string, defaultText: string, normalColor: string): string {
+    return text === defaultText ? '#99000000' : normalColor;
+  }
+
+  /**
+   * 获取专辑封面图片
+   */
+  private getCoverImage(): Resource | string {
+    if (this.coverImage && this.coverImage.trim() !== '') {
+      return this.coverImage;
+    }
+    return $r('app.media.bg_music');
+  }
 
   build() {
     Column() {
@@ -26,29 +74,45 @@ struct PlayerWidgetLarge {
       Row() {
         // 专辑封面
         if (this.showCover) {
-          Image(this.coverImage || $r('app.media.bg_music'))
+          Image(this.getCoverImage())
             .width(60)
             .height(60)
             .borderRadius(8)
             .objectFit(ImageFit.Cover)
             .margin({ right: 12 })
+            .stateStyles({
+              pressed: {
+                .opacity(0.8)
+                .scale({ x: 0.98, y: 0.98 })
+              },
+              normal: {
+                .opacity(1.0)
+                .scale({ x: 1.0, y: 1.0 })
+              }
+            })
             .onClick(() => {
-              SimpleWidgetController.handleOpenPlayer(this);
+              console.info('Heanup PlayerWidgetLarge: Cover image clicked');
+              postCardAction(this, {
+                'action': 'router',
+                'abilityName': 'EntryAbility'
+              });
             })
         }
 
         // 歌曲信息
         Column() {
-          Text(this.songTitle)
+          // 歌曲标题
+          Text(this.getDisplayText(this.songTitle, '暂无播放'))
             .fontSize(16)
-            .fontColor('#E6000000')
-            .fontWeight(FontWeight.Medium)
+            .fontColor(this.getTextColor(this.songTitle, '暂无播放', '#E6000000'))
+            .fontWeight(this.songTitle === '暂无播放' ? FontWeight.Normal : FontWeight.Medium)
             .maxLines(1)
             .textOverflow({ overflow: TextOverflow.Ellipsis })
             .width('100%')
             .textAlign(TextAlign.Start)
 
-          Text(this.songArtist)
+          // 艺术家信息
+          Text(this.getDisplayText(this.songArtist, '未知艺术家'))
             .fontSize(13)
             .fontColor('#99000000')
             .fontWeight(FontWeight.Normal)
@@ -58,7 +122,8 @@ struct PlayerWidgetLarge {
             .textAlign(TextAlign.Start)
             .margin({ top: 4 })
 
-          Text(this.songAlbum)
+          // 专辑信息
+          Text(this.getDisplayText(this.songAlbum, '未知专辑'))
             .fontSize(11)
             .fontColor('#66000000')
             .fontWeight(FontWeight.Normal)
@@ -71,8 +136,21 @@ struct PlayerWidgetLarge {
         .layoutWeight(1)
         .alignItems(HorizontalAlign.Start)
         .justifyContent(FlexAlign.Start)
+        .stateStyles({
+          pressed: {
+            .opacity(0.8)
+          },
+          normal: {
+            .opacity(1.0)
+          }
+        })
         .onClick(() => {
-          SimpleWidgetController.handleOpenPlayer(this);
+          console.info('Heanup PlayerWidgetLarge: Song info area clicked');
+          postCardAction(this, {
+            'action': 'router',
+            'abilityName': 'EntryAbility'
+
+          });
         })
       }
       .width('100%')
@@ -93,8 +171,28 @@ struct PlayerWidgetLarge {
               .height(4)
               .color('#FF007DFF')
               .backgroundColor('#1A007DFF')
+              .borderRadius(2)
+              .stateStyles({
+                pressed: {
+                  .opacity(0.8)
+                },
+                normal: {
+                  .opacity(1.0)
+                }
+              })
               .onClick((event) => {
-                SimpleWidgetController.handleProgressClick(this, event);
+                if (!this.isLoading) {
+                  console.info('Heanup PlayerWidgetLarge: Progress bar clicked');
+                  const width = Number(event.target.area.width);
+                  const percentage = width > 0 ? (event.x / width * 100) : 0;
+                  postCardAction(this, {
+                    'action': 'message',
+                    'params': {
+                      "func":"seek_to",
+                      'percentage': percentage
+                    }
+                  });
+                }
               })
           }
           .width('100%')
@@ -105,12 +203,14 @@ struct PlayerWidgetLarge {
             Text(this.currentTime)
               .fontSize(11)
               .fontColor('#99000000')
+              .fontWeight(FontWeight.Normal)
 
             Blank()
 
             Text(this.totalTime)
               .fontSize(11)
               .fontColor('#99000000')
+              .fontWeight(FontWeight.Normal)
           }
           .width('100%')
         }
@@ -125,31 +225,69 @@ struct PlayerWidgetLarge {
           Image($r('app.media.ic_previous2'))
             .width(20)
             .height(20)
-            .fillColor(this.hasPrevious ? '#FF007DFF' : '#66000000')
+            .fillColor(this.getButtonColor(this.hasPrevious))
         }
         .width(40)
         .height(40)
         .backgroundColor(Color.Transparent)
-        .enabled(this.hasPrevious)
+        .enabled(this.hasPrevious && !this.isLoading)
+        .opacity(this.getButtonOpacity(this.hasPrevious))
+        .stateStyles({
+          pressed: {
+            .scale({ x: 0.95, y: 0.95 })
+            .opacity(0.8)
+          },
+          normal: {
+            .scale({ x: 1.0, y: 1.0 })
+            .opacity(this.getButtonOpacity(this.hasPrevious))
+          }
+        })
         .onClick(() => {
-          SimpleWidgetController.handlePrevSong(this);
+          if (this.hasPrevious && !this.isLoading) {
+            console.info('Heanup PlayerWidgetLarge: Previous button clicked');
+            postCardAction(this, {
+              'action': 'message',
+              'params': {
+                "func":"prev_song"
+              }
+            });
+          }
         })
 
         Blank()
 
         // 播放/暂停按钮
         Button() {
-          Image(this.isPlaying ? $r('app.media.hm_pause') : $r('app.media.hm_play'))
+          Image(this.getPlayButtonIcon())
             .width(28)
             .height(28)
             .fillColor('#FFFFFF')
         }
         .width(52)
         .height(52)
-        .backgroundColor('#FF007DFF')
+        .backgroundColor(this.isLoading ? '#99007DFF' : '#FF007DFF')
         .borderRadius(26)
+        .enabled(!this.isLoading)
+        .stateStyles({
+          pressed: {
+            .scale({ x: 0.95, y: 0.95 })
+            .backgroundColor('#CC007DFF')
+          },
+          normal: {
+            .scale({ x: 1.0, y: 1.0 })
+            .backgroundColor(this.isLoading ? '#99007DFF' : '#FF007DFF')
+          }
+        })
         .onClick(() => {
-          SimpleWidgetController.handlePlayPause(this);
+          if (!this.isLoading) {
+            console.info('Heanup PlayerWidgetLarge: Play/Pause button clicked');
+            postCardAction(this, {
+              'action': 'message',
+              'params': {
+                "func":"play_pause"
+              }
+            });
+          }
         })
 
         Blank()
@@ -159,14 +297,33 @@ struct PlayerWidgetLarge {
           Image($r('app.media.ic_next2'))
             .width(20)
             .height(20)
-            .fillColor(this.hasNext ? '#FF007DFF' : '#66000000')
+            .fillColor(this.getButtonColor(this.hasNext))
         }
         .width(40)
         .height(40)
         .backgroundColor(Color.Transparent)
-        .enabled(this.hasNext)
+        .enabled(this.hasNext && !this.isLoading)
+        .opacity(this.getButtonOpacity(this.hasNext))
+        .stateStyles({
+          pressed: {
+            .scale({ x: 0.95, y: 0.95 })
+            .opacity(0.8)
+          },
+          normal: {
+            .scale({ x: 1.0, y: 1.0 })
+            .opacity(this.getButtonOpacity(this.hasNext))
+          }
+        })
         .onClick(() => {
-          SimpleWidgetController.handleNextSong(this);
+          if (this.hasNext && !this.isLoading) {
+            console.info('Heanup PlayerWidgetLarge: Next button clicked');
+            postCardAction(this, {
+              'action': 'message',
+              'params': {
+                "func":"next_song"
+              }
+            });
+          }
         })
       }
       .width('100%')
@@ -180,7 +337,12 @@ struct PlayerWidgetLarge {
     .borderRadius(12)
     .alignItems(HorizontalAlign.Start)
     .justifyContent(FlexAlign.SpaceBetween)
+    .shadow({
+      radius: 8,
+      color: '#1A000000',
+      offsetX: 0,
+      offsetY: 2
+    })
+    // 移除手势支持,form应用不支持复杂交互
   }
-
-
 }

+ 162 - 105
entry/src/main/ets/widget/pages/PlayerWidgetMedium.ets

@@ -1,153 +1,210 @@
 import { WidgetController } from '../../common/widget/WidgetController';
-
 /**
- * 中等尺寸播放器卡片 (4x2)
- * 显示完整信息、播放控制和进度条
+ * 小尺寸播放器卡片 (2x1)
+ * 显示基本播放控制和歌曲名称
+ * 需求: 5.1, 1.4, 1.5
  */
 @Entry
 @Component
-struct PlayerWidgetMedium {
+struct PlayerWidgetSmall {
+  // 卡片数据属性 - 使用LocalStorageProp与FormExtensionAbility通信
   @LocalStorageProp('isPlaying') isPlaying: boolean = false;
   @LocalStorageProp('songTitle') songTitle: string = '暂无播放';
-  @LocalStorageProp('songArtist') songArtist: string = '未知艺术家';
-  @LocalStorageProp('currentTime') currentTime: string = '00:00';
-  @LocalStorageProp('totalTime') totalTime: string = '00:00';
-  @LocalStorageProp('progressPercentage') progressPercentage: number = 0;
   @LocalStorageProp('hasNext') hasNext: boolean = false;
   @LocalStorageProp('hasPrevious') hasPrevious: boolean = false;
-  @LocalStorageProp('showProgress') showProgress: boolean = true;
+  @LocalStorageProp('isLoading') isLoading: boolean = false;
 
-  build() {
-    Column() {
-      // 歌曲信息区域
-      Row() {
-        Column() {
-          Text(this.songTitle)
-            .fontSize(16)
-            .fontColor('#E6000000')
-            .fontWeight(FontWeight.Medium)
-            .maxLines(1)
-            .textOverflow({ overflow: TextOverflow.Ellipsis })
-            .width('100%')
-            .textAlign(TextAlign.Start)
+  /**
+   * 获取按钮透明度
+   */
+  private getButtonOpacity(enabled: boolean): number {
+    return enabled ? 1.0 : 0.4;
+  }
 
-          Text(this.songArtist)
-            .fontSize(12)
-            .fontColor('#99000000')
-            .fontWeight(FontWeight.Normal)
-            .maxLines(1)
-            .textOverflow({ overflow: TextOverflow.Ellipsis })
-            .width('100%')
-            .textAlign(TextAlign.Start)
-            .margin({ top: 2 })
-        }
-        .layoutWeight(1)
-        .alignItems(HorizontalAlign.Start)
-        .onClick(() => {
-          WidgetController.handleOpenPlayer(this);
-        })
-      }
-      .width('100%')
-      .margin({ bottom: 12 })
+  /**
+   * 获取按钮颜色
+   */
+  private getButtonColor(enabled: boolean): string {
+    return enabled ? '#FF007DFF' : '#66000000';
+  }
+
+  /**
+   * 获取播放按钮图标
+   */
+  private getPlayButtonIcon(): Resource {
+    if (this.isLoading) {
+      return $r('app.media.hm_play'); // 加载状态图标
+    }
+    return this.isPlaying ? $r('app.media.hm_pause') : $r('app.media.hm_play');
+  }
+
+  /**
+   * 格式化歌曲标题显示
+   */
+  private getDisplayTitle(): string {
+    if (!this.songTitle || this.songTitle.trim() === '') {
+      return '暂无播放';
+    }
+    return this.songTitle;
+  }
 
-      // 播放控制区域
+  build() {
+    Row() {
+      // 播放控制按钮区域
       Row() {
         // 上一首按钮
         Button() {
           Image($r('app.media.ic_previous2'))
-            .width(18)
-            .height(18)
-            .fillColor(this.hasPrevious ? '#FF007DFF' : '#66000000')
+            .width(16)
+            .height(16)
+            .fillColor(this.getButtonColor(this.hasPrevious))
         }
-        .width(36)
-        .height(36)
+        .width(32)
+        .height(32)
         .backgroundColor(Color.Transparent)
-        .enabled(this.hasPrevious)
+        .enabled(this.hasPrevious && !this.isLoading)
+        .opacity(this.getButtonOpacity(this.hasPrevious))
+        .stateStyles({
+          pressed: {
+            .scale({ x: 0.95, y: 0.95 })
+            .opacity(0.8)
+          },
+          normal: {
+            .scale({ x: 1.0, y: 1.0 })
+            .opacity(this.getButtonOpacity(this.hasPrevious))
+          }
+        })
         .onClick(() => {
-          WidgetController.handlePrevSong(this);
+          if (this.hasPrevious && !this.isLoading) {
+            console.info('Heanup PlayerWidgetSmall: Previous button clicked');
+            postCardAction(this, {
+              'action': 'message',
+              'params': {
+                "func":"prev_song"
+              }
+            });
+          }
         })
 
         // 播放/暂停按钮
         Button() {
-          Image(this.isPlaying ? $r('app.media.hm_pause') : $r('app.media.hm_play'))
-            .width(24)
-            .height(24)
+          Image(this.getPlayButtonIcon())
+            .width(20)
+            .height(20)
             .fillColor('#FFFFFF')
         }
-        .width(44)
-        .height(44)
-        .backgroundColor('#FF007DFF')
-        .borderRadius(22)
-        .margin({ left: 12, right: 12 })
+        .width(36)
+        .height(36)
+        .backgroundColor(this.isLoading ? '#99007DFF' : '#FF007DFF')
+        .borderRadius(18)
+        .margin({ left: 8, right: 8 })
+        .enabled(!this.isLoading)
+        .stateStyles({
+          pressed: {
+            .scale({ x: 0.95, y: 0.95 })
+            .backgroundColor('#CC007DFF')
+          },
+          normal: {
+            .scale({ x: 1.0, y: 1.0 })
+            .backgroundColor(this.isLoading ? '#99007DFF' : '#FF007DFF')
+          }
+        })
         .onClick(() => {
-          WidgetController.handlePlayPause(this);
+          if (!this.isLoading) {
+            console.info('Heanup PlayerWidgetSmall: Play/Pause button clicked');
+            postCardAction(this, {
+              'action': 'message',
+              'params': {
+                "func":"play_pause"
+              }
+            });
+          }
         })
 
         // 下一首按钮
         Button() {
           Image($r('app.media.ic_next2'))
-            .width(18)
-            .height(18)
-            .fillColor(this.hasNext ? '#FF007DFF' : '#66000000')
+            .width(16)
+            .height(16)
+            .fillColor(this.getButtonColor(this.hasNext))
         }
-        .width(36)
-        .height(36)
+        .width(32)
+        .height(32)
         .backgroundColor(Color.Transparent)
-        .enabled(this.hasNext)
+        .enabled(this.hasNext && !this.isLoading)
+        .opacity(this.getButtonOpacity(this.hasNext))
+        .stateStyles({
+          pressed: {
+            .scale({ x: 0.95, y: 0.95 })
+            .opacity(0.8)
+          },
+          normal: {
+            .scale({ x: 1.0, y: 1.0 })
+            .opacity(this.getButtonOpacity(this.hasNext))
+          }
+        })
         .onClick(() => {
-          WidgetController.handleNextSong(this);
+          if (this.hasNext && !this.isLoading) {
+            console.info('Heanup PlayerWidgetSmall: Next button clicked');
+            postCardAction(this, {
+              'action': 'message',
+              'params': {
+                "func":"next_song"
+              }
+            });
+          }
         })
       }
       .justifyContent(FlexAlign.Center)
-      .width('100%')
-      .margin({ bottom: this.showProgress ? 8 : 0 })
+      .alignItems(VerticalAlign.Center)
 
-      // 播放进度区域
-      if (this.showProgress) {
-        Column() {
-          // 进度条
-          Row() {
-            Progress({
-              value: this.progressPercentage,
-              total: 100,
-              type: ProgressType.Linear
-            })
-              .width('100%')
-              .height(4)
-              .color('#FF007DFF')
-              .backgroundColor('#1A007DFF')
-              .onClick((event) => {
-                SimpleWidgetController.handleProgressClick(this, event);
-              })
-          }
+      // 歌曲信息区域
+      Column() {
+        Text(this.getDisplayTitle())
+          .fontSize(14)
+          .fontColor(this.songTitle === '暂无播放' ? '#99000000' : '#E6000000')
+          .fontWeight(this.songTitle === '暂无播放' ? FontWeight.Normal : FontWeight.Medium)
+          .maxLines(1)
+          .textOverflow({ overflow: TextOverflow.Ellipsis })
           .width('100%')
-          .margin({ bottom: 4 })
-
-          // 时间显示
-          Row() {
-            Text(this.currentTime)
-              .fontSize(10)
-              .fontColor('#99000000')
-
-            Blank()
+          .textAlign(TextAlign.Start)
+      }
+      .layoutWeight(1)
+      .margin({ left: 12 })
+      .alignItems(HorizontalAlign.Start)
+      .justifyContent(FlexAlign.Center)
+      .stateStyles({
+        pressed: {
+          .opacity(0.8)
+        },
+        normal: {
+          .opacity(1.0)
+        }
+      })
+      .onClick(() => {
+        console.info('Heanup PlayerWidgetSmall: Song info area clicked');
+        postCardAction(this, {
 
-            Text(this.totalTime)
-              .fontSize(10)
-              .fontColor('#99000000')
+          'action': 'message',
+          'params': {
+            "func":"open_player"
           }
-          .width('100%')
-        }
-        .width('100%')
-      }
+        });
+      })
     }
     .width('100%')
     .height('100%')
-    .padding(16)
+    .padding({ left: 16, right: 16, top: 8, bottom: 8 })
     .backgroundColor('#FFFFFF')
     .borderRadius(12)
-    .alignItems(HorizontalAlign.Start)
-    .justifyContent(FlexAlign.SpaceBetween)
+    .justifyContent(FlexAlign.Start)
+    .alignItems(VerticalAlign.Center)
+    .shadow({
+      radius: 8,
+      color: '#1A000000',
+      offsetX: 0,
+      offsetY: 2
+    })
+    // 移除手势支持,form应用不支持复杂交互
   }
-
-
 }

+ 130 - 21
entry/src/main/ets/widget/pages/PlayerWidgetSmall.ets

@@ -1,16 +1,52 @@
 import { WidgetController } from '../../common/widget/WidgetController';
-
 /**
  * 小尺寸播放器卡片 (2x1)
  * 显示基本播放控制和歌曲名称
+ * 需求: 5.1, 1.4, 1.5
  */
 @Entry
 @Component
 struct PlayerWidgetSmall {
+  // 卡片数据属性 - 使用LocalStorageProp与FormExtensionAbility通信
   @LocalStorageProp('isPlaying') isPlaying: boolean = false;
   @LocalStorageProp('songTitle') songTitle: string = '暂无播放';
   @LocalStorageProp('hasNext') hasNext: boolean = false;
   @LocalStorageProp('hasPrevious') hasPrevious: boolean = false;
+  @LocalStorageProp('isLoading') isLoading: boolean = false;
+
+  /**
+   * 获取按钮透明度
+   */
+  private getButtonOpacity(enabled: boolean): number {
+    return enabled ? 1.0 : 0.4;
+  }
+
+  /**
+   * 获取按钮颜色
+   */
+  private getButtonColor(enabled: boolean): string {
+    return enabled ? '#FF007DFF' : '#66000000';
+  }
+
+  /**
+   * 获取播放按钮图标
+   */
+  private getPlayButtonIcon(): Resource {
+    if (this.isLoading) {
+      return $r('app.media.hm_play'); // 加载状态图标
+    }
+    return this.isPlaying ? $r('app.media.hm_pause') : $r('app.media.hm_play');
+  }
+
+  /**
+   * 格式化歌曲标题显示
+   */
+  private getDisplayTitle(): string {
+    if (!this.songTitle || this.songTitle.trim() === '') {
+      return '暂无播放';
+    }
+    return this.songTitle;
+  }
 
   build() {
     Row() {
@@ -21,33 +57,68 @@ struct PlayerWidgetSmall {
           Image($r('app.media.ic_previous2'))
             .width(16)
             .height(16)
-            .fillColor(this.hasPrevious ? '#FF007DFF' : '#66000000')
+            .fillColor(this.getButtonColor(this.hasPrevious))
         }
         .width(32)
         .height(32)
         .backgroundColor(Color.Transparent)
-        .enabled(this.hasPrevious)
+        .enabled(this.hasPrevious && !this.isLoading)
+        .opacity(this.getButtonOpacity(this.hasPrevious))
+        .stateStyles({
+          pressed: {
+            .scale({ x: 0.95, y: 0.95 })
+            .opacity(0.8)
+          },
+          normal: {
+            .scale({ x: 1.0, y: 1.0 })
+            .opacity(this.getButtonOpacity(this.hasPrevious))
+          }
+        })
         .onClick(() => {
-          console.info('PlayerWidgetSmall: Previous button clicked');
-          WidgetController.handlePrevSong(this);
+          if (this.hasPrevious && !this.isLoading) {
+            console.info('Heanup PlayerWidgetSmall: Previous button clicked');
+            postCardAction(this, {
+              'action': 'message',
+              'params': {
+                "func":"prev_song"
+              }
+            });
+          }
         })
 
         // 播放/暂停按钮
         Button() {
-          Image(this.isPlaying ? $r('app.media.hm_pause') : $r('app.media.hm_play'))
+          Image(this.getPlayButtonIcon())
             .width(20)
             .height(20)
-            .fillColor('#FF007DFF')
+            .fillColor('#FFFFFF')
         }
         .width(36)
         .height(36)
-        .backgroundColor('#1A007DFF')
+        .backgroundColor(this.isLoading ? '#99007DFF' : '#FF007DFF')
         .borderRadius(18)
         .margin({ left: 8, right: 8 })
+        .enabled(!this.isLoading)
+        .stateStyles({
+          pressed: {
+            .scale({ x: 0.95, y: 0.95 })
+            .backgroundColor('#CC007DFF')
+          },
+          normal: {
+            .scale({ x: 1.0, y: 1.0 })
+            .backgroundColor(this.isLoading ? '#99007DFF' : '#FF007DFF')
+          }
+        })
         .onClick(() => {
-          // 添加调试日志
-          console.info('PlayerWidgetSmall: Play/Pause button clicked');
-          WidgetController.handlePlayPause(this);
+          if (!this.isLoading) {
+            console.info('Heanup PlayerWidgetSmall: Play/Pause button clicked');
+            postCardAction(this, {
+              'action': 'message',
+              'params': {
+                "func":"play_pause"
+              }
+            });
+          }
         })
 
         // 下一首按钮
@@ -55,15 +126,33 @@ struct PlayerWidgetSmall {
           Image($r('app.media.ic_next2'))
             .width(16)
             .height(16)
-            .fillColor(this.hasNext ? '#FF007DFF' : '#66000000')
+            .fillColor(this.getButtonColor(this.hasNext))
         }
         .width(32)
         .height(32)
         .backgroundColor(Color.Transparent)
-        .enabled(this.hasNext)
+        .enabled(this.hasNext && !this.isLoading)
+        .opacity(this.getButtonOpacity(this.hasNext))
+        .stateStyles({
+          pressed: {
+            .scale({ x: 0.95, y: 0.95 })
+            .opacity(0.8)
+          },
+          normal: {
+            .scale({ x: 1.0, y: 1.0 })
+            .opacity(this.getButtonOpacity(this.hasNext))
+          }
+        })
         .onClick(() => {
-          console.info('PlayerWidgetSmall: Next button clicked');
-          WidgetController.handleNextSong(this);
+          if (this.hasNext && !this.isLoading) {
+            console.info('Heanup PlayerWidgetSmall: Next button clicked');
+            postCardAction(this, {
+              'action': 'message',
+              'params': {
+                "func":"next_song"
+              }
+            });
+          }
         })
       }
       .justifyContent(FlexAlign.Center)
@@ -71,10 +160,10 @@ struct PlayerWidgetSmall {
 
       // 歌曲信息区域
       Column() {
-        Text(this.songTitle)
+        Text(this.getDisplayTitle())
           .fontSize(14)
-          .fontColor('#E6000000')
-          .fontWeight(FontWeight.Medium)
+          .fontColor(this.songTitle === '暂无播放' ? '#99000000' : '#E6000000')
+          .fontWeight(this.songTitle === '暂无播放' ? FontWeight.Normal : FontWeight.Medium)
           .maxLines(1)
           .textOverflow({ overflow: TextOverflow.Ellipsis })
           .width('100%')
@@ -84,8 +173,23 @@ struct PlayerWidgetSmall {
       .margin({ left: 12 })
       .alignItems(HorizontalAlign.Start)
       .justifyContent(FlexAlign.Center)
+      .stateStyles({
+        pressed: {
+          .opacity(0.8)
+        },
+        normal: {
+          .opacity(1.0)
+        }
+      })
       .onClick(() => {
-        WidgetController.handleOpenPlayer(this);
+        console.info('Heanup PlayerWidgetSmall: Song info area clicked');
+        postCardAction(this, {
+
+          'action': 'message',
+          'params': {
+            "func":"open_player"
+          }
+        });
       })
     }
     .width('100%')
@@ -95,7 +199,12 @@ struct PlayerWidgetSmall {
     .borderRadius(12)
     .justifyContent(FlexAlign.Start)
     .alignItems(VerticalAlign.Center)
+    .shadow({
+      radius: 8,
+      color: '#1A000000',
+      offsetX: 0,
+      offsetY: 2
+    })
+    // 移除手势支持,form应用不支持复杂交互
   }
-
-
 }

+ 2 - 2
entry/src/main/module.json5

@@ -93,8 +93,8 @@
 
     "extensionAbilities": [
       {
-        "name": "PlayerWidgetFormExtensionAbility",
-        "srcEntry": "./ets/entryformability/PlayerWidgetFormExtensionAbility.ets",
+        "name": "EntryFormAbility",
+        "srcEntry": "./ets/entryformability/EntryFormAbility.ets",
         "label": "$string:form_PlayerWidgetFormExtensionAbility_label",
         "description": "$string:form_PlayerWidgetFormExtensionAbility_desc",
         "type": "form",

+ 0 - 38
entry/src/main/resources/base/profile/form_config.json

@@ -56,44 +56,6 @@
       "supportDimensions": [
         "4*4"
       ]
-    },
-    {
-      "name": "PlayerWidgetSmallTest",
-      "description": "测试版小尺寸播放器卡片",
-      "src": "./ets/widget/pages/PlayerWidgetSmallTest.ets",
-      "uiSyntax": "arkts",
-      "window": {
-        "designWidth": 720,
-        "autoDesignWidth": true
-      },
-      "colorMode": "auto",
-      "isDefault": false,
-      "updateEnabled": true,
-      "scheduledUpdateTime": "10:30",
-      "updateDuration": 1,
-      "defaultDimension": "1*2",
-      "supportDimensions": [
-        "1*2"
-      ]
-    },
-    {
-      "name": "PlayerWidgetSimpleTest",
-      "description": "简化测试版播放器卡片",
-      "src": "./ets/widget/pages/PlayerWidgetSimpleTest.ets",
-      "uiSyntax": "arkts",
-      "window": {
-        "designWidth": 720,
-        "autoDesignWidth": true
-      },
-      "colorMode": "auto",
-      "isDefault": false,
-      "updateEnabled": true,
-      "scheduledUpdateTime": "10:30",
-      "updateDuration": 1,
-      "defaultDimension": "1*2",
-      "supportDimensions": [
-        "1*2"
-      ]
     }
   ]
 }