DirectFormUpdateService.ets 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import { hilog } from '@kit.PerformanceAnalysisKit';
  2. import { formProvider, formBindingData } from '@kit.FormKit';
  3. import { preferences } from '@kit.ArkData';
  4. import { WidgetData, FormattedWidgetData, WidgetSize } from './WidgetTypes';
  5. import { FormLayoutManager } from './FormLayoutManager';
  6. import { PreferencesUtil } from '../utils/PreferencesUtil';
  7. import { Context } from '@kit.AbilityKit';
  8. // 定义支持的值类型
  9. type SupportedValueType = string | number | boolean | Uint8Array;
  10. const TAG = 'DirectFormUpdateService';
  11. /**
  12. * 表单状态数据接口
  13. */
  14. interface FormStateData extends Record<string, SupportedValueType> {
  15. size: string;
  16. lastUpdate: number;
  17. lastSongId: string;
  18. }
  19. /**
  20. * 表单验证结果接口
  21. */
  22. interface FormValidationResult {
  23. formId: string;
  24. isValid: boolean;
  25. error: Error | null;
  26. }
  27. /**
  28. * 测试数据接口
  29. */
  30. interface TestFormData {
  31. test: string;
  32. }
  33. /**
  34. * 直接卡片更新服务
  35. * 按照 HarmonyOS 官方推荐方式,通过持久化的 Form ID 直接更新卡片
  36. */
  37. export class DirectFormUpdateService {
  38. private static instance: DirectFormUpdateService | null = null;
  39. private layoutManager: FormLayoutManager = FormLayoutManager.getInstance();
  40. private preferencesUtil: PreferencesUtil = PreferencesUtil.getInstance();
  41. private appContext: Context | null = null;
  42. private constructor() {}
  43. public static getInstance(): DirectFormUpdateService {
  44. if (!DirectFormUpdateService.instance) {
  45. DirectFormUpdateService.instance = new DirectFormUpdateService();
  46. }
  47. return DirectFormUpdateService.instance;
  48. }
  49. /**
  50. * 设置应用上下文
  51. */
  52. public setAppContext(context: Context): void {
  53. this.appContext = context;
  54. hilog.info(0x0000, TAG, '🎯 App context set for DirectFormUpdateService');
  55. }
  56. /**
  57. * 更新所有卡片数据
  58. */
  59. public async updateAllForms(data: WidgetData): Promise<void> {
  60. try {
  61. if (!this.appContext) {
  62. hilog.error(0x0000, TAG, '❌ App context not set, cannot update forms');
  63. return;
  64. }
  65. hilog.info(0x0000, TAG, `🎯 Starting direct form update: isPlaying=${data.playState.isPlaying}, title=${data.currentSong.title}`);
  66. // 获取所有持久化的 Form ID
  67. const prefs = await this.preferencesUtil.getPreferences(this.appContext);
  68. const formIds = await this.preferencesUtil.getFormIds(prefs);
  69. if (formIds.length === 0) {
  70. hilog.info(0x0000, TAG, '📋 No forms found in persistence, skipping update');
  71. return;
  72. }
  73. hilog.info(0x0000, TAG, `📋 Found ${formIds.length} forms to update: [${formIds.join(', ')}]`);
  74. // 为每个卡片创建单独的更新任务,添加详细日志
  75. const updatePromises = formIds.map((formId, index) => {
  76. hilog.info(0x0000, TAG, `🚀 Creating update task ${index + 1}/${formIds.length} for form: ${formId}`);
  77. return this.updateSingleForm(formId, data, prefs);
  78. });
  79. hilog.info(0x0000, TAG, `⏳ Executing ${updatePromises.length} parallel update tasks...`);
  80. // 更新每个卡片
  81. const results = await Promise.allSettled(updatePromises);
  82. // 统计更新结果
  83. const successCount = results.filter(result => result.status === 'fulfilled').length;
  84. const failureCount = results.filter(result => result.status === 'rejected').length;
  85. hilog.info(0x0000, TAG, `🎯 Form update completed: ${successCount} succeeded, ${failureCount} failed`);
  86. // 详细记录每个结果
  87. results.forEach((result, index) => {
  88. const formId = formIds[index];
  89. if (result.status === 'fulfilled') {
  90. hilog.info(0x0000, TAG, `✅ Form ${index + 1}/${formIds.length} (${formId}) updated successfully`);
  91. } else {
  92. const error = result.reason as Error;
  93. hilog.error(0x0000, TAG, `❌ Form ${index + 1}/${formIds.length} (${formId}) failed: ${error.message}`);
  94. }
  95. });
  96. // 清理无效的 Form ID(16501001 错误表示 Form 不存在)
  97. if (failureCount > 0) {
  98. hilog.info(0x0000, TAG, `🧹 Starting cleanup of failed forms...`);
  99. await this.cleanupInvalidForms(prefs, formIds, results);
  100. // 重新获取清理后的 Form ID 列表
  101. const remainingFormIds = await this.preferencesUtil.getFormIds(prefs);
  102. hilog.info(0x0000, TAG, `📋 After cleanup: ${remainingFormIds.length} forms remaining: [${remainingFormIds.join(', ')}]`);
  103. }
  104. } catch (error) {
  105. hilog.error(0x0000, TAG, `❌ Failed to update forms: ${error}`);
  106. }
  107. }
  108. /**
  109. * 更新单个卡片
  110. */
  111. private async updateSingleForm(formId: string, data: WidgetData, prefs: preferences.Preferences): Promise<void> {
  112. try {
  113. hilog.info(0x0000, TAG, `🎯 [${formId}] Starting form update...`);
  114. // 获取卡片的当前状态(如果有保存的话)
  115. const formState = await this.preferencesUtil.getFormState(prefs, formId);
  116. const widgetSizeStr = (formState?.size as string) || 'medium'; // 默认使用 medium 尺寸
  117. hilog.info(0x0000, TAG, `🎯 [${formId}] Widget size: ${widgetSizeStr}, formState: ${JSON.stringify(formState)}`);
  118. // 转换为 WidgetSize 类型
  119. const widgetSize: WidgetSize = widgetSizeStr as WidgetSize;
  120. // 适配数据到卡片尺寸
  121. const adaptedData = this.layoutManager.adaptDataForSize(data, widgetSize);
  122. hilog.info(0x0000, TAG, `🎯 [${formId}] Data adapted for size ${widgetSize}`);
  123. // 转换为 FormattedWidgetData
  124. const formattedData: FormattedWidgetData = {
  125. isPlaying: adaptedData.isPlaying,
  126. isPaused: adaptedData.isPaused,
  127. isLoading: adaptedData.isLoading,
  128. songTitle: adaptedData.songTitle,
  129. songArtist: adaptedData.songArtist,
  130. songAlbum: adaptedData.songAlbum,
  131. coverImage: adaptedData.coverImage,
  132. currentTime: adaptedData.currentTime,
  133. totalTime: adaptedData.totalTime,
  134. progressPercentage: adaptedData.progressPercentage,
  135. hasNext: adaptedData.hasNext,
  136. hasPrevious: adaptedData.hasPrevious,
  137. showProgress: adaptedData.showProgress,
  138. showCover: adaptedData.showCover,
  139. widgetSize: adaptedData.widgetSize,
  140. timestamp: Date.now(),
  141. imgName: adaptedData.imgName || ''
  142. };
  143. hilog.info(0x0000, TAG, `🎯 [${formId}] Formatted data prepared: isPlaying=${formattedData.isPlaying}, title="${formattedData.songTitle}"`);
  144. // 创建 FormBindingData 并更新卡片
  145. const formData = formBindingData.createFormBindingData(formattedData);
  146. hilog.info(0x0000, TAG, `🎯 [${formId}] FormBindingData created, calling formProvider.updateForm...`);
  147. await formProvider.updateForm(formId, formData);
  148. hilog.info(0x0000, TAG, `✅ [${formId}] Form updated successfully: isPlaying=${formattedData.isPlaying}, title="${formattedData.songTitle}"`);
  149. // 保存当前状态
  150. const stateData: FormStateData = {
  151. size: widgetSizeStr,
  152. lastUpdate: Date.now(),
  153. lastSongId: data.currentSong.id
  154. };
  155. await this.preferencesUtil.saveFormState(prefs, formId, stateData);
  156. hilog.info(0x0000, TAG, `💾 [${formId}] Form state saved`);
  157. } catch (error) {
  158. hilog.error(0x0000, TAG, `❌ [${formId}] Failed to update form: ${JSON.stringify(error)}`);
  159. throw new Error(`Failed to update form ${formId}: ${JSON.stringify(error)}`); // 重新抛出错误,让 Promise.allSettled 捕获
  160. }
  161. }
  162. /**
  163. * 清理无效的 Form ID
  164. */
  165. private async cleanupInvalidForms(prefs: preferences.Preferences, formIds: string[], results: PromiseSettledResult<void>[]): Promise<void> {
  166. try {
  167. const invalidFormIds: string[] = [];
  168. results.forEach((result: PromiseSettledResult<void>, index: number) => {
  169. if (result.status === 'rejected') {
  170. const error = result.reason as Error;
  171. const errorStr = error.toString();
  172. const formId = formIds[index];
  173. hilog.warn(0x0000, TAG, `🔍 Analyzing error for form ${formId}: ${errorStr}`);
  174. // 检查是否是因为 Form 不存在导致的错误
  175. // 16501001 是 Form 不存在的错误代码
  176. if (errorStr.includes('form not exist') ||
  177. errorStr.includes('16501001') ||
  178. errorStr.includes('FormProvider') ||
  179. errorStr.includes('invalid form')) {
  180. hilog.warn(0x0000, TAG, `🗑️ Form ${formId} appears to be invalid (error: ${errorStr}), marking for cleanup`);
  181. invalidFormIds.push(formId);
  182. } else {
  183. hilog.error(0x0000, TAG, `⚠️ Form ${formId} failed with unknown error: ${errorStr}`);
  184. }
  185. }
  186. });
  187. if (invalidFormIds.length > 0) {
  188. hilog.info(0x0000, TAG, `🧹 Cleaning up ${invalidFormIds.length} invalid forms: [${invalidFormIds.join(', ')}]`);
  189. for (const invalidFormId of invalidFormIds) {
  190. hilog.info(0x0000, TAG, `🗑️ Removing invalid form ID: ${invalidFormId}`);
  191. await this.preferencesUtil.removeFormId(prefs, invalidFormId);
  192. }
  193. hilog.info(0x0000, TAG, `✅ Invalid forms cleaned up successfully. Remaining forms will be updated normally.`);
  194. } else {
  195. hilog.warn(0x0000, TAG, `⚠️ ${results.filter(r => r.status === 'rejected').length} forms failed but none appear to be invalid (may be temporary errors)`);
  196. }
  197. } catch (error) {
  198. hilog.error(0x0000, TAG, `❌ Failed to cleanup invalid forms: ${error}`);
  199. }
  200. }
  201. }