| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- import formProvider from '@ohos.app.form.formProvider';
- import formBindingData from '@ohos.app.form.formBindingData';
- import preferences from '@ohos.data.preferences';
- import { hilog } from '@kit.PerformanceAnalysisKit';
- import { WidgetData, WidgetSize, WidgetTheme, FormattedWidgetData, PreferencesData, CacheStats } from './WidgetTypes';
- const TAG = 'WidgetDataManager';
- const WIDGET_PREFERENCES_NAME = 'widget_data_prefs';
- const CACHE_EXPIRY_TIME = 30 * 1000; // 30秒缓存过期时间
- /**
- * 缓存项接口
- */
- interface CacheItem {
- data: WidgetData;
- timestamp: number;
- expiry: number;
- }
- /**
- * 卡片数据管理器
- * 负责卡片数据的持久化存储、缓存和更新
- */
- export class WidgetDataManager {
- private preferencesStore: preferences.Preferences | null = null;
- private dataCache: Map<string, CacheItem> = new Map();
- private lastUpdateTime: number = 0;
- private context: object | null = null;
- constructor(context?: object) {
- this.context = context || null;
- this.initPreferences();
- this.startCacheCleanup();
- }
- /**
- * 初始化数据存储
- */
- private async initPreferences(): Promise<void> {
- try {
- if (this.context) {
- const store = await preferences.getPreferences(this.context as Context, WIDGET_PREFERENCES_NAME);
- this.preferencesStore = store;
- hilog.info(0x0000, TAG, 'Preferences initialized successfully with context');
- } else {
- hilog.warn(0x0000, TAG, 'No context provided, preferences initialization skipped');
- }
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to initialize preferences: ${error}`);
- }
- }
- /**
- * 获取初始卡片数据
- */
- getInitialWidgetData(): WidgetData {
- const initialData: WidgetData = {
- 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: WidgetSize.MEDIUM,
- theme: WidgetTheme.AUTO,
- showProgress: true,
- showCover: true
- }
- };
- return initialData;
- }
- /**
- * 保存卡片数据
- */
- async saveWidgetData(formId: string, data: WidgetData | FormattedWidgetData): Promise<void> {
- try {
- if (!this.preferencesStore) {
- await this.initPreferences();
- }
-
- const dataKey = `widget_${formId}`;
- await this.preferencesStore?.put(dataKey, JSON.stringify(data));
- await this.preferencesStore?.flush();
-
- hilog.info(0x0000, TAG, `Widget data saved for form: ${formId}`);
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to save widget data: ${error}`);
- }
- }
- /**
- * 获取卡片数据
- */
- async getWidgetData(formId: string): Promise<WidgetData> {
- try {
- if (!this.preferencesStore) {
- await this.initPreferences();
- }
-
- const dataKey = `widget_${formId}`;
- const dataStr = await this.preferencesStore?.get(dataKey, '') as string;
-
- if (dataStr) {
- return JSON.parse(dataStr) as WidgetData;
- } else {
- return this.getInitialWidgetData() as WidgetData;
- }
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to get widget data: ${error}`);
- return this.getInitialWidgetData() as WidgetData;
- }
- }
- /**
- * 更新卡片显示
- */
- async updateWidget(formId: string, data: WidgetData | FormattedWidgetData): Promise<void> {
- try {
- hilog.info(0x0000, TAG, `Updating widget ${formId} with data type check`);
-
- let formattedData: FormattedWidgetData;
-
- // 检查数据类型,如果已经是格式化数据则直接使用
- if (this.isFormattedWidgetData(data)) {
- hilog.info(0x0000, TAG, `Data is already formatted for widget ${formId}`);
- formattedData = data as FormattedWidgetData;
-
- // 如果是格式化数据,需要转换回WidgetData进行存储
- const widgetData = this.convertToWidgetData(formattedData);
- await this.saveWidgetData(formId, widgetData);
- } else {
- hilog.info(0x0000, TAG, `Formatting raw data for widget ${formId}`);
- // 保存原始数据到本地存储
- await this.saveWidgetData(formId, data as WidgetData);
-
- // 格式化数据用于卡片显示
- formattedData = this.formatDataForWidget(data as WidgetData);
- }
-
- hilog.info(0x0000, TAG, `Final formatted data for ${formId}: isPlaying=${formattedData.isPlaying}, hasNext=${formattedData.hasNext}, hasPrevious=${formattedData.hasPrevious}, title=${formattedData.songTitle}, coverImage=${formattedData.coverImage ? 'present' : 'empty'}, showCover=${formattedData.showCover}`);
-
- // 创建卡片绑定数据
- const formData = formBindingData.createFormBindingData(formattedData);
-
- // 更新卡片
- await formProvider.updateForm(formId, formData);
-
- hilog.info(0x0000, TAG, `Widget updated successfully: ${formId}`);
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to update widget: ${error}`);
- }
- }
- /**
- * 批量更新所有卡片
- */
- async updateAllWidgets(data?: WidgetData): Promise<void> {
- try {
- if (!this.preferencesStore) {
- await this.initPreferences();
- }
-
- // 获取所有卡片ID
- const allKeys = await this.preferencesStore?.getAll();
- const emptyPrefs: PreferencesData = {};
- const widgetKeys = Object.keys(allKeys || emptyPrefs).filter(key => key.startsWith('widget_'));
-
- for (const key of widgetKeys) {
- const formId: string = key.replace('widget_', '');
- const widgetData: WidgetData = data || await this.getWidgetData(formId);
- await this.updateWidget(formId, widgetData);
- }
-
- hilog.info(0x0000, TAG, `Updated ${widgetKeys.length} widgets`);
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to update all widgets: ${error}`);
- }
- }
- /**
- * 删除卡片数据
- */
- async removeWidgetData(formId: string): Promise<void> {
- try {
- if (!this.preferencesStore) {
- await this.initPreferences();
- }
-
- const dataKey = `widget_${formId}`;
- await this.preferencesStore?.delete(dataKey);
- await this.preferencesStore?.flush();
-
- hilog.info(0x0000, TAG, `Widget data removed for form: ${formId}`);
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to remove widget data: ${error}`);
- }
- }
- /**
- * 格式化数据用于卡片显示
- */
- public formatDataForWidget(data: WidgetData): FormattedWidgetData {
- hilog.info(0x0000, TAG, `Formatting data for widget: hasNext=${data.playlist.hasNext}, hasPrevious=${data.playlist.hasPrevious}, currentIndex=${data.playlist.currentIndex}, totalCount=${data.playlist.totalCount}`);
-
- // 修复按钮状态:基于当前索引和总数重新计算正确的按钮状态
- let hasNext = data.playlist.hasNext;
- let hasPrevious = data.playlist.hasPrevious;
-
- hilog.info(0x0000, TAG, `Original button states: hasNext=${hasNext}, hasPrevious=${hasPrevious}, currentIndex=${data.playlist.currentIndex}, totalCount=${data.playlist.totalCount}`);
-
- // 如果有播放列表,重新计算按钮状态
- if (data.playlist.totalCount > 1) {
- const correctHasNext = data.playlist.currentIndex < data.playlist.totalCount - 1;
- const correctHasPrevious = data.playlist.currentIndex > 0;
-
- // 如果计算出的状态与当前状态不一致,进行修复
- if (hasNext !== correctHasNext || hasPrevious !== correctHasPrevious) {
- hasNext = correctHasNext;
- hasPrevious = correctHasPrevious;
- hilog.info(0x0000, TAG, `Fixed button states: hasNext=${hasNext}, hasPrevious=${hasPrevious}, currentIndex=${data.playlist.currentIndex}, totalCount=${data.playlist.totalCount}`);
- } else {
- hilog.info(0x0000, TAG, `Button states are correct: hasNext=${hasNext}, hasPrevious=${hasPrevious}`);
- }
- } else if (data.playlist.totalCount <= 1) {
- // 如果只有一首歌或没有歌,按钮都应该禁用
- hasNext = false;
- hasPrevious = false;
- hilog.info(0x0000, TAG, `Single or no song, buttons disabled: totalCount=${data.playlist.totalCount}`);
- }
-
- const formattedData: FormattedWidgetData = {
- // 播放状态
- isPlaying: data.playState.isPlaying,
- isPaused: data.playState.isPaused,
- isLoading: data.playState.isLoading,
-
- // 歌曲信息
- songTitle: this.truncateText(data.currentSong.title, 20),
- songArtist: this.truncateText(data.currentSong.artist, 15),
- songAlbum: this.truncateText(data.currentSong.album, 15),
- coverImage: data.currentSong.coverImagePath && data.currentSong.coverImagePath.trim() !== '' ? data.currentSong.coverImagePath : '',
-
- // 播放进度
- currentTime: data.progress.currentTimeText,
- totalTime: data.progress.totalTimeText,
- progressPercentage: data.progress.percentage,
-
- // 控制按钮状态(使用修复后的值)
- hasNext: hasNext,
- hasPrevious: hasPrevious,
-
- // 卡片配置
- showProgress: data.config.showProgress,
- showCover: data.config.showCover,
- widgetSize: data.config.size as string,
-
- // 时间戳用于强制更新
- timestamp: Date.now()
- };
-
- hilog.info(0x0000, TAG, `Formatted widget data: isPlaying=${formattedData.isPlaying}, hasNext=${formattedData.hasNext}, hasPrevious=${formattedData.hasPrevious}, title=${formattedData.songTitle}`);
- return formattedData;
- }
- /**
- * 截断文本
- */
- private truncateText(text: string, maxLength: number): string {
- if (text.length <= maxLength) {
- return text;
- }
- return text.substring(0, maxLength - 1) + '…';
- }
- /**
- * 启动缓存清理定时器
- */
- private startCacheCleanup(): void {
- setInterval(() => {
- this.cleanExpiredCache();
- }, 60 * 1000); // 每分钟清理一次过期缓存
- }
- /**
- * 清理过期缓存
- */
- private cleanExpiredCache(): void {
- const now = Date.now();
- const expiredKeys: string[] = [];
-
- this.dataCache.forEach((item: CacheItem, key: string) => {
- if (now > item.expiry) {
- expiredKeys.push(key);
- }
- });
-
- expiredKeys.forEach((key: string) => {
- this.dataCache.delete(key);
- });
-
- if (expiredKeys.length > 0) {
- hilog.info(0x0000, TAG, `Cleaned ${expiredKeys.length} expired cache items`);
- }
- }
- /**
- * 从缓存获取数据
- */
- private getCachedData(formId: string): WidgetData | null {
- const cacheKey = `cache_${formId}`;
- const cacheItem = this.dataCache.get(cacheKey);
-
- if (cacheItem && Date.now() < cacheItem.expiry) {
- hilog.info(0x0000, TAG, `Cache hit for form: ${formId}`);
- return cacheItem.data;
- }
-
- if (cacheItem) {
- // 缓存已过期,删除
- this.dataCache.delete(cacheKey);
- hilog.info(0x0000, TAG, `Cache expired for form: ${formId}`);
- }
-
- return null;
- }
- /**
- * 设置缓存数据
- */
- private setCachedData(formId: string, data: WidgetData): void {
- const cacheKey = `cache_${formId}`;
- const now = Date.now();
-
- const cacheItem: CacheItem = {
- data: data,
- timestamp: now,
- expiry: now + CACHE_EXPIRY_TIME
- };
-
- this.dataCache.set(cacheKey, cacheItem);
- hilog.info(0x0000, TAG, `Data cached for form: ${formId}`);
- }
- /**
- * 获取卡片数据(带缓存)
- */
- async getWidgetDataWithCache(formId: string): Promise<WidgetData> {
- // 先尝试从缓存获取
- const cachedData = this.getCachedData(formId);
- if (cachedData) {
- return cachedData;
- }
-
- // 缓存未命中,从持久化存储获取
- const data = await this.getWidgetData(formId);
-
- // 设置缓存
- this.setCachedData(formId, data);
-
- return data;
- }
- /**
- * 更新卡片数据(带缓存)
- */
- async updateWidgetWithCache(formId: string, data: WidgetData): Promise<void> {
- // 更新缓存
- this.setCachedData(formId, data);
-
- // 更新卡片显示
- await this.updateWidget(formId, data);
- }
- /**
- * 清除指定卡片的缓存
- */
- clearWidgetCache(formId: string): void {
- const cacheKey = `cache_${formId}`;
- if (this.dataCache.has(cacheKey)) {
- this.dataCache.delete(cacheKey);
- hilog.info(0x0000, TAG, `Cache cleared for form: ${formId}`);
- }
- }
- /**
- * 清除所有缓存
- */
- clearAllCache(): void {
- const cacheSize = this.dataCache.size;
- this.dataCache.clear();
- hilog.info(0x0000, TAG, `All cache cleared, ${cacheSize} items removed`);
- }
- /**
- * 获取缓存统计信息
- */
- getCacheStats(): CacheStats {
- const stats: CacheStats = {
- size: this.dataCache.size,
- hitRate: 0, // 可以在实际使用中统计命中率
- lastUpdate: this.lastUpdateTime
- };
- return stats;
- }
- /**
- * 预热缓存
- */
- async preloadCache(): Promise<void> {
- try {
- if (!this.preferencesStore) {
- await this.initPreferences();
- }
-
- const allKeys = await this.preferencesStore?.getAll();
- const emptyPrefs: PreferencesData = {};
- const widgetKeys = Object.keys(allKeys || emptyPrefs).filter(key => key.startsWith('widget_'));
-
- for (const key of widgetKeys) {
- const formId: string = key.replace('widget_', '');
- const data: WidgetData = await this.getWidgetData(formId);
- this.setCachedData(formId, data);
- }
-
- hilog.info(0x0000, TAG, `Cache preloaded for ${widgetKeys.length} widgets`);
- } catch (error) {
- 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;
- }
- }
- }
|