import { formBindingData, FormExtensionAbility, formProvider } from '@kit.FormKit'; import { Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { fileIo } from '@kit.CoreFileKit'; import { http } from '@kit.NetworkKit'; import { WidgetDataManager } from '../common/widget/WidgetDataManager'; import { PlayerControlService } from '../common/widget/PlayerControlService'; import { AvSessionWidgetListener } from '../common/widget/AvSessionWidgetListener'; import { WidgetData, WidgetSize, FormattedWidgetData } from '../common/widget/WidgetTypes'; import { FormLayoutManager } from '../common/widget/FormLayoutManager'; import { GlobalWidgetManager } from '../common/widget/GlobalWidgetManager'; import { WidgetSizeAdapter, SizeChangeListener } from '../common/widget/WidgetSizeAdapter'; import { PreferencesUtil } from '../common/utils/PreferencesUtil'; const TAG = 'Heanup EntryFormAbility'; /** * 扩展的卡片数据接口,支持图片传递 */ interface ExtendedWidgetData extends FormattedWidgetData { formImages?: Record; } /** * 自定义对象复制函数,替代Object.assign */ function copyWidgetData(target: ExtendedWidgetData, overrides: Partial): ExtendedWidgetData { return { isPlaying: overrides.isPlaying !== undefined ? overrides.isPlaying : target.isPlaying, isPaused: overrides.isPaused !== undefined ? overrides.isPaused : target.isPaused, isLoading: overrides.isLoading !== undefined ? overrides.isLoading : target.isLoading, songTitle: overrides.songTitle !== undefined ? overrides.songTitle : target.songTitle, songArtist: overrides.songArtist !== undefined ? overrides.songArtist : target.songArtist, songAlbum: overrides.songAlbum !== undefined ? overrides.songAlbum : target.songAlbum, coverImage: overrides.coverImage !== undefined ? overrides.coverImage : target.coverImage, currentTime: overrides.currentTime !== undefined ? overrides.currentTime : target.currentTime, totalTime: overrides.totalTime !== undefined ? overrides.totalTime : target.totalTime, progressPercentage: overrides.progressPercentage !== undefined ? overrides.progressPercentage : target.progressPercentage, hasNext: overrides.hasNext !== undefined ? overrides.hasNext : target.hasNext, hasPrevious: overrides.hasPrevious !== undefined ? overrides.hasPrevious : target.hasPrevious, showProgress: overrides.showProgress !== undefined ? overrides.showProgress : target.showProgress, showCover: overrides.showCover !== undefined ? overrides.showCover : target.showCover, widgetSize: overrides.widgetSize !== undefined ? overrides.widgetSize : target.widgetSize, timestamp: overrides.timestamp !== undefined ? overrides.timestamp : target.timestamp, imgName: overrides.imgName !== undefined ? overrides.imgName : target.imgName, formImages: overrides.formImages !== undefined ? overrides.formImages : target.formImages }; } /** * 桌面播放器卡片扩展能力 * 负责处理卡片的生命周期管理和用户交互事件 * 支持多尺寸适配和UI重构 */ export default class EntryFormAbility extends FormExtensionAbility implements SizeChangeListener { private widgetDataManager: WidgetDataManager | null = null; private playerControlService: PlayerControlService = new PlayerControlService(); private layoutManager: FormLayoutManager = FormLayoutManager.getInstance(); private sizeAdapter: WidgetSizeAdapter = WidgetSizeAdapter.getInstance(); private globalWidgetManager: GlobalWidgetManager = GlobalWidgetManager.getInstance(); // 使用实例变量而不是静态变量,确保每个进程都能正确设置监听器 private globalListenerSetup: boolean = false; // 防抖机制:避免短时间内重复更新 private lastUpdateTime: number = 0; private updateDebounceDelay: number = 100; // 100ms防抖延迟 // 进程启动时间,用于检测进程重启 private processStartTime: number = Date.now(); // 图片下载缓存 private downloadingImages: Map> = new Map(); private imageCache: Map = new Map(); // url -> fileName映射 /** * 卡片创建时调用 */ onAddForm(want: Want): formBindingData.FormBindingData { // 检查参数有效性 if (!want || !want.parameters) { hilog.error(0x0000, TAG, 'FormAbility onAddForm want or want.parameters is undefined'); return formBindingData.createFormBindingData(''); } // 初始化服务 try { this.initializeServices(); } catch (error) { hilog.error(0x0000, TAG, `Heanup EntryFormAbility failed to initialize services: ${error}`); } 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 formDimension = want.parameters?.['ohos.extra.param.key.form_dimension'] as string; const tempFlag = want.parameters?.['ohos.extra.param.key.form_temporary'] as boolean; // 持久化保存 Form ID(异步执行,不阻塞返回) this.saveFormIdToPersistence(formId).then(() => { }) // 检测卡片尺寸并注册到全局管理器 const widgetSize = this.sizeAdapter.detectSizeFromWant(want); try { this.globalWidgetManager.registerWidget(formId, widgetSize); } catch (error) { hilog.error(0x0000, TAG, `Failed to register widget: ${error}`); } // 注册各种监听器 this.sizeAdapter.registerSizeChangeListener(formId, this); // 初始化卡片数据 this.initializeWidget(formId, widgetSize); // 验证监听器设置状态 setTimeout(() => { const avSessionListener = AvSessionWidgetListener.getInstance(); const listenerCount = avSessionListener.getListenerCount(); if (listenerCount === 0) { hilog.warn(0x0000, TAG, `⚠️ No listeners found! Form process may not receive data updates!`); } else { } }, 2000); // 获取当前播放状态而不是初始数据 this.playerControlService.getCurrentPlayState().then((currentState: WidgetData) => { const adaptedData = this.layoutManager.adaptDataForSize(currentState, widgetSize); const formData = formBindingData.createFormBindingData(adaptedData); // 立即更新卡片以显示当前状态 formProvider.updateForm(formId, formData); }); // 返回初始数据作为临时显示 const initialData = this.widgetDataManager?.getInitialWidgetData() || this.getDefaultWidgetData(); const adaptedData = this.layoutManager.adaptDataForSize(initialData, widgetSize); return formBindingData.createFormBindingData(adaptedData); } /** * 卡片更新时调用 */ onUpdateForm(formId: string): void { // 确保服务已初始化 if (!this.globalListenerSetup) { this.initializeServices(); } // 确保widget已注册到GlobalWidgetManager if (!this.globalWidgetManager.hasWidget(formId)) { this.globalWidgetManager.registerWidget(formId, 'medium' as WidgetSize); } // 只更新指定的卡片,避免重复更新 this.updateWidgetData(formId); } /** * 卡片删除时调用 */ onRemoveForm(formId: string): void { // 从持久化存储中移除 Form ID(异步执行) this.removeFormIdFromPersistence(formId).then(() => { }); // 注销各种监听器 this.sizeAdapter.unregisterSizeChangeListener(formId); // 从全局管理器中注销卡片 this.globalWidgetManager.unregisterWidget(formId); // 清理卡片相关数据和配置 this.widgetDataManager?.removeWidgetData(formId); } /** * 卡片可见性变化时调用 */ onVisibilityChange(newStatus: Record): void { const formIds = Object.keys(newStatus); for (let i = 0; i < formIds.length; i++) { const formId = formIds[i]; const isVisible = newStatus[formId] === 1; if (isVisible) { // 卡片变为可见时,更新数据 this.updateWidgetData(formId); } } } /** * 卡片配置更新时调用 */ onConfigurationUpdate(newConfig: Object): void { // 更新所有卡片以适应新配置 this.widgetDataManager?.updateAllWidgets(); } /** * 实现SizeChangeListener接口 * 处理卡片尺寸变化事件 */ onSizeChanged(formId: string, oldSize: WidgetSize, newSize: WidgetSize): void { // 更新全局管理器中的尺寸记录 this.globalWidgetManager.updateWidgetSize(formId, newSize); // 立即更新卡片数据以适应新尺寸 this.updateWidgetData(formId); } /** * 处理卡片尺寸变化(系统调用) * @param newStatus 新的尺寸状态 */ onAcquireFormState(want: Want): number { const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string; if (formId) { // 检测新的尺寸 const newSize = this.sizeAdapter.detectSizeFromWant(want); const oldSize = this.globalWidgetManager.getWidgetSize(formId); if (oldSize && this.sizeAdapter.validateSizeChange(oldSize, newSize)) { // 处理尺寸变化 this.handleFormSizeChange(formId, oldSize, newSize); } } // 返回卡片状态 - 使用数字常量代替枚举 return 1; // READY状态 } /** * 初始化服务 */ private initializeServices(): void { if (!this.widgetDataManager) { this.widgetDataManager = new WidgetDataManager(); } // 设置全局状态监听器(每个进程实例设置一次) if (!this.globalListenerSetup) { this.setupGlobalStateListener(); this.globalListenerSetup = true; } } /** * 设置全局状态监听器 */ private setupGlobalStateListener(): void { try { const avSessionListener = AvSessionWidgetListener.getInstance(); avSessionListener.addStateListener((data: WidgetData) => { this.updateAllWidgetsWithData(data); }); } catch (error) { hilog.error(0x0000, TAG, `Heanup EntryFormAbility [Process:${this.processStartTime}] failed to setup global listener: ${error}`); } } /** * 使用指定数据更新所有卡片 */ private updateAllWidgetsWithData(data: WidgetData): void { // 防抖机制:避免短时间内重复更新 const now = Date.now(); if (now - this.lastUpdateTime < this.updateDebounceDelay) { return; } this.lastUpdateTime = now; const activeWidgets = this.globalWidgetManager.getActiveWidgets(); if (activeWidgets.size === 0) { hilog.warn(0x0000, TAG, 'Heanup EntryFormAbility no widgets to update in this process, skipping'); return; } activeWidgets.forEach((size: WidgetSize, formId: string) => { this.updateSingleWidget(formId, size, data); }); } /** * 更新单个卡片,带重试机制和网络图片支持 */ private updateSingleWidget(formId: string, size: WidgetSize, data: WidgetData, retryCount: number = 0): void { try { // 根据卡片尺寸适配数据 const formattedData = this.layoutManager.adaptDataForSize(data, size); // 转换为ExtendedWidgetData const adaptedData: ExtendedWidgetData = { isPlaying: formattedData.isPlaying, isPaused: formattedData.isPaused, isLoading: formattedData.isLoading, songTitle: formattedData.songTitle, songArtist: formattedData.songArtist, songAlbum: formattedData.songAlbum, coverImage: formattedData.coverImage, currentTime: formattedData.currentTime, totalTime: formattedData.totalTime, progressPercentage: formattedData.progressPercentage, hasNext: formattedData.hasNext, hasPrevious: formattedData.hasPrevious, showProgress: formattedData.showProgress, showCover: formattedData.showCover, widgetSize: formattedData.widgetSize, timestamp: formattedData.timestamp, imgName: formattedData.imgName, formImages: formattedData.formImages }; // 使用统一的图片处理方法 this.updateWidgetWithImage(formId, adaptedData, retryCount); } catch (error) { hilog.error(0x0000, TAG, `Heanup widget ${formId} process error: ${error}`); // 如果是处理错误,也尝试重试 if (retryCount < 2) { setTimeout(() => { this.updateSingleWidget(formId, size, data, retryCount + 1); }, 1000 * (retryCount + 1)); } } } /** * 直接更新卡片(无网络图片) */ private updateWidgetDirectly(formId: string, adaptedData: ExtendedWidgetData, retryCount: number = 0): void { const formData = formBindingData.createFormBindingData(adaptedData); formProvider.updateForm(formId, formData).then(() => { }).catch(() => { hilog.error(0x0000, TAG, `Heanup widget ${formId} update failed, retry count: ${retryCount}`); // 重试机制:最多重试2次 if (retryCount < 2) { setTimeout(() => { this.updateWidgetDirectly(formId, adaptedData, retryCount + 1); }, 1000 * (retryCount + 1)); // 递增延迟 } }); } /** * 统一处理图片更新 */ private async updateWidgetWithImage(formId: string, adaptedData: ExtendedWidgetData, retryCount: number = 0): Promise { try { if (!adaptedData.coverImage || adaptedData.coverImage.trim() === '') { // 没有封面图片,直接更新 this.updateWidgetDirectly(formId, adaptedData, retryCount); return; } if (this.isNetworkUrl(adaptedData.coverImage)) { // 处理网络图片 await this.updateWidgetWithNetworkImage(formId, adaptedData, retryCount); } else if (this.isLocalFileUri(adaptedData.coverImage)) { // 处理本地文件URI await this.updateWidgetWithLocalImage(formId, adaptedData, retryCount); } else { // 其他情况,可能是相对路径或其他格式,直接使用 this.updateWidgetDirectly(formId, adaptedData, retryCount); } } catch (error) { hilog.error(0x0000, TAG, `Heanup widget ${formId} image update error: ${error}`); // 失败时使用默认数据 const dataWithoutImage: ExtendedWidgetData = copyWidgetData(adaptedData, { coverImage: '' }); this.updateWidgetDirectly(formId, dataWithoutImage, retryCount); } } /** * 处理本地文件URI图片 */ private async updateWidgetWithLocalImage(formId: string, adaptedData: ExtendedWidgetData, retryCount: number = 0): Promise { try { const localFileUri: string = adaptedData.coverImage; // 先用无图片的数据快速更新一次,确保界面响应性 const dataWithoutImage: ExtendedWidgetData = copyWidgetData(adaptedData, { coverImage: '', imgName: '', formImages: undefined }); this.updateWidgetDirectly(formId, dataWithoutImage, 0); // 处理本地图片文件 const fileName = await this.processLocalImageFile(localFileUri); if (fileName) { try { // 按照官方文档要求,准备 formImages 和文件描述符 const imageMap: Record = {}; const fileDescriptor = await this.getImageFileDescriptor(fileName); imageMap[fileName] = fileDescriptor; // 按照官方文档要求,imgName 必须与 formImages 中的 key 相同 const dataWithImage: ExtendedWidgetData = copyWidgetData(adaptedData, { coverImage: '', // 清空原路径 imgName: fileName, // 设置图片名称用于 memory:// 协议 formImages: imageMap // 必填字段,不可缺省 }); const formDataWithImage = formBindingData.createFormBindingData(dataWithImage); await formProvider.updateForm(formId, formDataWithImage); } catch (fdError) { hilog.error(0x0000, TAG, `Heanup widget ${formId} failed to get file descriptor: ${fdError}`); // 文件描述符获取失败,使用默认图片 this.updateWidgetDirectly(formId, dataWithoutImage, 0); } } else { hilog.warn(0x0000, TAG, `Heanup widget ${formId} failed to process local image, using default`); // 图片处理失败,使用默认图片 this.updateWidgetDirectly(formId, dataWithoutImage, 0); } } catch (error) { hilog.error(0x0000, TAG, `Heanup widget ${formId} local image update error: ${error}`); // 失败后回退到无图片模式 const dataWithoutImage: ExtendedWidgetData = copyWidgetData(adaptedData, { coverImage: '', imgName: '', formImages: undefined }); this.updateWidgetDirectly(formId, dataWithoutImage, retryCount); } } /** * 使用网络图片更新卡片 */ private async updateWidgetWithNetworkImage(formId: string, adaptedData: ExtendedWidgetData, retryCount: number = 0): Promise { try { const imageUrl: string = adaptedData.coverImage; // 先用无图片的数据快速更新一次 const dataWithoutImage: ExtendedWidgetData = copyWidgetData(adaptedData, { coverImage: '' }); this.updateWidgetDirectly(formId, dataWithoutImage, 0); // 下载图片 const fileName = await this.downloadNetworkImage(imageUrl); if (fileName) { // 准备包含图片的数据 const imageMap: Record = {}; imageMap[fileName] = await this.getImageFileDescriptor(fileName); const dataWithImage: ExtendedWidgetData = copyWidgetData(adaptedData, { coverImage: '', // 清空URL imgName: fileName, // 设置图片名称用于memory://协议 formImages: imageMap }); const formDataWithImage = formBindingData.createFormBindingData(dataWithImage); await formProvider.updateForm(formId, formDataWithImage); } else { hilog.warn(0x0000, TAG, `Heanup widget ${formId} failed to download image, using default`); // 图片下载失败,使用默认图片 this.updateWidgetDirectly(formId, dataWithoutImage, 0); } } catch (error) { hilog.error(0x0000, TAG, `Heanup widget ${formId} network image update error: ${error}`); // 失败后回退到无图片模式 const dataWithoutImage: ExtendedWidgetData = copyWidgetData(adaptedData, { coverImage: '' }); this.updateWidgetDirectly(formId, dataWithoutImage, retryCount); } } /** * 检查是否为网络URL */ private isNetworkUrl(url: string): boolean { return url.startsWith('http://') || url.startsWith('https://'); } /** * 检查是否为本地文件URI */ private isLocalFileUri(url: string): boolean { return url.startsWith('file://'); } /** * 处理本地图片文件,将其复制到卡片可访问的临时目录 */ private async processLocalImageFile(fileUri: string): Promise { try { // 检查缓存 if (this.imageCache.has(fileUri)) { const fileName = this.imageCache.get(fileUri)!; // 验证缓存文件是否仍然存在 - 使用 FormExtensionAbility 的 tempDir const formTempDir = this.context.getApplicationContext().tempDir; const tempFilePath = `${formTempDir}/${fileName}`; if (fileIo.accessSync(tempFilePath)) { return fileName; } else { // 缓存文件已不存在,清除缓存记录 this.imageCache.delete(fileUri); hilog.warn(0x0000, TAG, `Cached file no longer exists, will reprocess: ${fileName}`); } } // 检查是否正在处理 if (this.downloadingImages.has(fileUri)) { const existingPromise = this.downloadingImages.get(fileUri); if (existingPromise) { return await existingPromise; } } // 开始处理本地文件 const processPromise = this.performLocalImageCopy(fileUri); this.downloadingImages.set(fileUri, processPromise); const fileName = await processPromise; // 清理处理状态 this.downloadingImages.delete(fileUri); if (fileName) { this.imageCache.set(fileUri, fileName); } return fileName; } catch (error) { hilog.error(0x0000, TAG, `Failed to process local image ${fileUri}: ${error}`); this.downloadingImages.delete(fileUri); return null; } } /** * 执行本地图片文件复制 */ private async performLocalImageCopy(fileUri: string): Promise { try { // 转换 file:// URI 为实际文件路径 const realPath = fileUri.replace('file://', ''); // 检查源文件是否存在 if (!fileIo.accessSync(realPath)) { hilog.error(0x0000, TAG, `Source image file does not exist: ${realPath}`); return null; } // 生成目标文件名(包含时间戳和随机数,确保每次都不同) const fileExtension = this.getFileExtension(realPath) || 'jpg'; const fileName = `widget_local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}.${fileExtension}`; // 使用 FormExtensionAbility 的 tempDir(官方文档要求) const formTempDir = this.context.getApplicationContext().tempDir; const tempFilePath = `${formTempDir}/${fileName}`; // 复制文件到临时目录 fileIo.copyFileSync(realPath, tempFilePath); return fileName; } catch (error) { hilog.error(0x0000, TAG, `Failed to copy local image: ${error}`); return null; } } /** * 获取文件扩展名 */ private getFileExtension(filePath: string): string | null { const lastDotIndex = filePath.lastIndexOf('.'); if (lastDotIndex === -1 || lastDotIndex === filePath.length - 1) { return null; } return filePath.substring(lastDotIndex + 1).toLowerCase(); } /** * 下载网络图片 */ private async downloadNetworkImage(imageUrl: string): Promise { try { // 检查缓存 if (this.imageCache.has(imageUrl)) { const fileName = this.imageCache.get(imageUrl)!; return fileName; } // 检查是否正在下载 if (this.downloadingImages.has(imageUrl)) { const existingPromise = this.downloadingImages.get(imageUrl); if (existingPromise) { return await existingPromise; } } // 开始下载 const downloadPromise = this.performImageDownload(imageUrl); this.downloadingImages.set(imageUrl, downloadPromise); const fileName = await downloadPromise; // 清理下载状态 this.downloadingImages.delete(imageUrl); if (fileName) { this.imageCache.set(imageUrl, fileName); } return fileName; } catch (error) { hilog.error(0x0000, TAG, `Failed to download image ${imageUrl}: ${error}`); this.downloadingImages.delete(imageUrl); return null; } } /** * 执行图片下载 */ private async performImageDownload(imageUrl: string): Promise { try { // 创建HTTP请求 const httpRequest = http.createHttp(); // 设置超时时间为3秒,适合卡片的快速响应需求 const response = await httpRequest.request(imageUrl, { method: http.RequestMethod.GET, expectDataType: http.HttpDataType.ARRAY_BUFFER, connectTimeout: 3000, readTimeout: 3000 }); if (response.responseCode === http.ResponseCode.OK && response.result) { // 生成文件名(确保每次都不同,符合官方文档要求) const fileName = `widget_cover_${Date.now()}_${Math.random().toString(36).substr(2, 9)}.jpg`; // 使用 FormExtensionAbility 的 tempDir(官方文档要求) const formTempDir = this.context.getApplicationContext().tempDir; const filePath = `${formTempDir}/${fileName}`; // 保存文件 const file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); await fileIo.write(file.fd, response.result as ArrayBuffer); fileIo.closeSync(file); httpRequest.destroy(); return fileName; } else { hilog.error(0x0000, TAG, `HTTP request failed: ${response.responseCode}`); httpRequest.destroy(); return null; } } catch (error) { hilog.error(0x0000, TAG, `Download error: ${error}`); return null; } } /** * 获取图片文件描述符 */ private async getImageFileDescriptor(fileName: string): Promise { try { // 使用 FormExtensionAbility 的 tempDir(官方文档要求) const formTempDir = this.context.getApplicationContext().tempDir; const filePath = `${formTempDir}/${fileName}`; const file = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY); // 注意:文件描述符会被系统自动管理,不需要手动关闭 // 系统会在卡片更新完成后自动关闭文件描述符 return file.fd; } catch (error) { hilog.error(0x0000, TAG, `Failed to get file descriptor for ${fileName}: ${error}`); throw new Error(`Failed to get file descriptor for ${fileName}: ${error}`); } } /** * 获取默认卡片数据 */ 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' as WidgetSize, theme: 'auto' as string, showProgress: true, showCover: true } }; } /** * 初始化卡片 */ private async initializeWidget(formId: string, widgetSize: WidgetSize): Promise { try { // 获取当前播放状态 const currentState = await this.playerControlService.getCurrentPlayState(); // 适配数据到指定尺寸 const adaptedData = this.layoutManager.adaptDataForSize(currentState, widgetSize); await this.widgetDataManager?.saveWidgetData(formId, adaptedData); // 立即更新一次卡片数据 await this.updateWidgetData(formId); } catch (error) { hilog.error(0x0000, TAG, `Failed to initialize widget ${formId}: ${error}`); } } /** * 更新卡片数据(简化版本) */ private async updateWidgetData(formId: string): Promise { try { const currentState = await this.playerControlService.getCurrentPlayState(); // 获取卡片当前尺寸 const currentSize = this.globalWidgetManager.getWidgetSize(formId) || WidgetSize.MEDIUM; // 适配数据到当前尺寸并直接更新 const adaptedData = this.layoutManager.adaptDataForSize(currentState, currentSize); const formData = formBindingData.createFormBindingData(adaptedData); await formProvider.updateForm(formId, formData); } catch (error) { hilog.error(0x0000, TAG, `Failed to update widget ${formId}: ${error}`); } } /** * 处理表单尺寸变化 */ private async handleFormSizeChange(formId: string, oldSize: WidgetSize, newSize: WidgetSize): Promise { try { // 获取当前播放状态 const currentState = await this.playerControlService.getCurrentPlayState(); // 使用适配器处理尺寸变化 const adaptedData = this.sizeAdapter.handleSizeChange(formId, oldSize, newSize, currentState); // 更新卡片数据 await this.widgetDataManager?.updateWidget(formId, adaptedData); } catch (error) { hilog.error(0x0000, TAG, `Failed to handle form size change: ${error}`); } } /** * 保存 Form ID 到持久化存储 */ private async saveFormIdToPersistence(formId: string): Promise { try { const preferencesUtil = PreferencesUtil.getInstance(); const prefs = await preferencesUtil.getPreferences(this.context); await preferencesUtil.addFormId(prefs, formId); } catch (error) { hilog.error(0x0000, TAG, `❌ Failed to save Form ID: ${error}`); } } /** * 从持久化存储中移除 Form ID */ private async removeFormIdFromPersistence(formId: string): Promise { try { const preferencesUtil = PreferencesUtil.getInstance(); const prefs = await preferencesUtil.getPreferences(this.context); await preferencesUtil.removeFormId(prefs, formId); } catch (error) { hilog.error(0x0000, TAG, `❌ Failed to remove Form ID: ${error}`); } } }