|
|
@@ -539,11 +539,6 @@ export struct LocalMusic {
|
|
|
// 更新最近播放时间
|
|
|
this.localMusic.updateLastPlayTimeStr(song.filePath);
|
|
|
|
|
|
- // 广播歌曲变化到卡片
|
|
|
- setTimeout(() => {
|
|
|
- this.localMusic.broadcastPlayerState();
|
|
|
- }, 100);
|
|
|
-
|
|
|
LogUtils.getInstance().LOGI(`LocalMusic: Song synchronized - ${song.name} at index ${currentIndex}`);
|
|
|
}
|
|
|
|
|
|
@@ -708,7 +703,6 @@ export struct LocalMusic {
|
|
|
timestamp: (widgetEventData['timestamp'] as number) || Date.now(),
|
|
|
source: (widgetEventData['source'] as string) || 'unknown'
|
|
|
};
|
|
|
- this.handleWidgetControlFromEmitter(typedEventData);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -738,21 +732,6 @@ export struct LocalMusic {
|
|
|
|
|
|
this.makeWorker()
|
|
|
|
|
|
- // 初始化卡片事件监听器
|
|
|
- this.initWidgetEventListener();
|
|
|
-
|
|
|
- // 延迟广播初始状态到卡片(在songList初始化之后)
|
|
|
- setTimeout(() => {
|
|
|
- this.broadcastPlayerState();
|
|
|
- LogUtils.getInstance().LOGI(`Delayed state broadcast: songList.length=${this.songList.length}, curIndex=${this.curIndex}`);
|
|
|
- }, 2000);
|
|
|
-
|
|
|
- // 移除定期广播,避免重复更新
|
|
|
- // setInterval(() => {
|
|
|
- // LogUtils.getInstance().LOGI('Periodic widget state broadcast');
|
|
|
- // this.broadcastPlayerState();
|
|
|
- // }, 10000);
|
|
|
-
|
|
|
//折叠屏的屏幕显示模式变化
|
|
|
display.on('foldDisplayModeChange', (data) => {
|
|
|
this.doChangeBarHeight()
|
|
|
@@ -1068,17 +1047,9 @@ export struct LocalMusic {
|
|
|
this.cover = this.currentSong.pixelMapPath
|
|
|
AppStorage.setOrCreate('currentSong',this.currentSong) ;
|
|
|
|
|
|
- // 延迟广播初始状态到卡片
|
|
|
- setTimeout(() => {
|
|
|
- this.broadcastPlayerState();
|
|
|
- LogUtils.getInstance().LOGI(`Initial state broadcasted: songList.length=${this.songList.length}, curIndex=${this.curIndex}`);
|
|
|
- }, 1000);
|
|
|
+
|
|
|
} else {
|
|
|
this.name = '空空如也'
|
|
|
- // 即使没有歌曲也要广播状态
|
|
|
- setTimeout(() => {
|
|
|
- this.broadcastPlayerState();
|
|
|
- }, 1000);
|
|
|
}
|
|
|
})
|
|
|
|
|
|
@@ -9724,9 +9695,6 @@ export struct LocalMusic {
|
|
|
this.setIsPlaying(true);
|
|
|
this.updateSessionPlayState(true);
|
|
|
|
|
|
- // 广播状态变化到卡片
|
|
|
- this.broadcastPlayerState();
|
|
|
-
|
|
|
LogUtils.getInstance().LOGI("LocalMusic: startPlayOrResumePlay completed via UnifiedPlayerService");
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`LocalMusic startPlayOrResumePlay error: ${error}`);
|
|
|
@@ -9810,11 +9778,7 @@ export struct LocalMusic {
|
|
|
AppStorage.setOrCreate('songList', this.songList);
|
|
|
AppStorage.setOrCreate('currIndex', this.curIndex);
|
|
|
AppStorage.setOrCreate('currentSong', currentSong);
|
|
|
-
|
|
|
- // 广播歌曲变化到卡片
|
|
|
- setTimeout(() => {
|
|
|
- this.broadcastPlayerState();
|
|
|
- }, 200);
|
|
|
+
|
|
|
}
|
|
|
|
|
|
LogUtils.getInstance().LOGI(`LocalMusic: playSongAtIndex ${index} completed via UnifiedPlayerService`);
|
|
|
@@ -10575,9 +10539,6 @@ export struct LocalMusic {
|
|
|
PiPWindow.PiPControlStatus.PAUSE);
|
|
|
}
|
|
|
|
|
|
- // 广播状态变化到卡片
|
|
|
- this.broadcastPlayerState();
|
|
|
-
|
|
|
LogUtils.getInstance().LOGI("LocalMusic: pause completed via UnifiedPlayerService");
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`LocalMusic pause error: ${error}`);
|
|
|
@@ -10598,9 +10559,6 @@ export struct LocalMusic {
|
|
|
this.playChange()
|
|
|
this.watchStatus();
|
|
|
|
|
|
- // 广播状态变化到卡片
|
|
|
- this.broadcastPlayerState();
|
|
|
-
|
|
|
LogUtils.getInstance().LOGI("LocalMusic: stop completed via UnifiedPlayerService");
|
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`LocalMusic stop error: ${error}`);
|
|
|
@@ -10755,11 +10713,7 @@ export struct LocalMusic {
|
|
|
AppStorage.setOrCreate('currentSong', currentSong);
|
|
|
|
|
|
this.changeImageAnimation();
|
|
|
-
|
|
|
- // 确保广播歌曲变化到卡片
|
|
|
- setTimeout(() => {
|
|
|
- this.broadcastPlayerState();
|
|
|
- }, 200);
|
|
|
+
|
|
|
}
|
|
|
|
|
|
LogUtils.getInstance().LOGI("LocalMusic: playNext completed via UnifiedPlayerService");
|
|
|
@@ -10792,10 +10746,6 @@ export struct LocalMusic {
|
|
|
this.artist = this.songList[this.curIndex].artist
|
|
|
this.name = this.songList[this.curIndex].name
|
|
|
this.changeImageAnimation()
|
|
|
-
|
|
|
- setTimeout(() => {
|
|
|
- this.broadcastPlayerState();
|
|
|
- }, 200);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -10828,10 +10778,6 @@ export struct LocalMusic {
|
|
|
}else{
|
|
|
this.cover = this.songList[this.curIndex].pixelMapPath
|
|
|
this.startPlayOrResumePlay()
|
|
|
- // 广播歌曲变化到卡片
|
|
|
- setTimeout(() => {
|
|
|
- this.broadcastPlayerState();
|
|
|
- }, 200);
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -10949,11 +10895,7 @@ export struct LocalMusic {
|
|
|
AppStorage.setOrCreate('songList', this.songList);
|
|
|
AppStorage.setOrCreate('currIndex', this.curIndex);
|
|
|
AppStorage.setOrCreate('currentSong', currentSong);
|
|
|
-
|
|
|
- // 广播歌曲变化到卡片
|
|
|
- setTimeout(() => {
|
|
|
- this.broadcastPlayerState();
|
|
|
- }, 200);
|
|
|
+
|
|
|
this.changeImageAnimation();
|
|
|
}
|
|
|
}
|
|
|
@@ -10986,10 +10928,6 @@ export struct LocalMusic {
|
|
|
this.name = this.songList[this.curIndex].name
|
|
|
this.artist = this.songList[this.curIndex].artist
|
|
|
this.startPlayOrResumePlay()
|
|
|
-
|
|
|
- setTimeout(() => {
|
|
|
- this.broadcastPlayerState();
|
|
|
- }, 200);
|
|
|
this.changeImageAnimation()
|
|
|
}
|
|
|
|
|
|
@@ -11428,40 +11366,6 @@ 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 - LocalMusic is ready to receive CommonEvents!');
|
|
|
- } catch (error) {
|
|
|
- LogUtils.getInstance().error(`❌ Failed to initialize widget event listener: ${error}`);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 初始化AvSession卡片监听器
|
|
|
*/
|
|
|
@@ -11523,153 +11427,6 @@ export struct LocalMusic {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 处理卡片事件
|
|
|
- */
|
|
|
- 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(`🎵 LocalMusic processing widget command: ${eventData.command}`);
|
|
|
- this.executeWidgetCommand(eventData);
|
|
|
- } catch (error) {
|
|
|
- LogUtils.getInstance().error(`Failed to handle widget control command: ${error}`);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 处理来自emitter的卡片控制命令
|
|
|
- */
|
|
|
- private handleWidgetControlFromEmitter(eventData: EventData): void {
|
|
|
- try {
|
|
|
- LogUtils.getInstance().LOGI(`🎵 LocalMusic processing widget command from emitter: ${eventData.command}`);
|
|
|
- this.executeWidgetCommand(eventData);
|
|
|
- } catch (error) {
|
|
|
- LogUtils.getInstance().error(`Failed to handle widget control command from emitter: ${error}`);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 执行卡片控制命令的通用方法
|
|
|
- */
|
|
|
- private executeWidgetCommand(eventData: EventData): void {
|
|
|
- try {
|
|
|
- // 直接执行命令,无需冲突检测
|
|
|
- this.executeWidgetCommandInternal(eventData);
|
|
|
- } catch (error) {
|
|
|
- LogUtils.getInstance().error(`Failed to execute widget command: ${error}`);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 内部执行卡片控制命令
|
|
|
- */
|
|
|
- private executeWidgetCommandInternal(eventData: EventData): void {
|
|
|
- try {
|
|
|
- LogUtils.getInstance().LOGI(`🎵 LocalMusic executing widget command: ${eventData.command} from ${eventData.source}`);
|
|
|
-
|
|
|
- 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 execute widget command internally: ${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.duration > 0) {
|
|
|
- const targetPosition = (percentage / 100) * this.duration;
|
|
|
- this.seekTo(targetPosition.toString());
|
|
|
- LogUtils.getInstance().LOGI(`Widget seek to: ${percentage}% (${targetPosition}ms)`);
|
|
|
-
|
|
|
- // 延迟广播状态更新,确保seekTo操作完成
|
|
|
- setTimeout(() => {
|
|
|
- this.broadcastPlayerState();
|
|
|
- }, 300);
|
|
|
- }
|
|
|
- } 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}`);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 节流广播进度更新
|
|
|
*/
|
|
|
@@ -11712,150 +11469,6 @@ export struct LocalMusic {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 强制更新卡片状态(用于测试和调试)
|
|
|
- */
|
|
|
- public forceUpdateWidgetState(): void {
|
|
|
- LogUtils.getInstance().LOGI('Force updating widget state...');
|
|
|
- LogUtils.getInstance().LOGI(`Current state: PlayStatus=${this.CONTROL_PlayStatus}, songTitle=${this.name}, curIndex=${this.curIndex}, songListLength=${this.songList.length}`);
|
|
|
- this.broadcastPlayerState();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 广播播放器状态到卡片(带节流)
|
|
|
- */
|
|
|
- private broadcastPlayerState(): void {
|
|
|
- // 节流:避免短时间内重复广播
|
|
|
- const now = Date.now();
|
|
|
- if (now - this.lastStateBroadcastTime < this.STATE_BROADCAST_INTERVAL) {
|
|
|
- return;
|
|
|
- }
|
|
|
- this.lastStateBroadcastTime = now;
|
|
|
-
|
|
|
- try {
|
|
|
- LogUtils.getInstance().LOGI(`Broadcasting player state: CONTROL_PlayStatus=${this.CONTROL_PlayStatus}, PlayStatus.PLAY=${PlayStatus.PLAY}, PlayStatus.PAUSE=${PlayStatus.PAUSE}`);
|
|
|
-
|
|
|
- const playState: PlayState = {
|
|
|
- isPlaying: this.CONTROL_PlayStatus === PlayStatus.PLAY,
|
|
|
- isPaused: this.CONTROL_PlayStatus === PlayStatus.PAUSE,
|
|
|
- isLoading: this.CONTROL_PlayStatus === PlayStatus.LOADING
|
|
|
- };
|
|
|
-
|
|
|
- LogUtils.getInstance().LOGI(`PlayState created: isPlaying=${playState.isPlaying}, isPaused=${playState.isPaused}, isLoading=${playState.isLoading}`);
|
|
|
-
|
|
|
- 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.unifiedPlayerService.getCurrentPosition() || 0,
|
|
|
- duration: this.duration || 0,
|
|
|
- percentage: this.duration > 0 ? ((this.unifiedPlayerService.getCurrentPosition() || 0) / this.duration) * 100 : 0,
|
|
|
- currentTimeText: this.stringForTime(this.unifiedPlayerService.getCurrentPosition() || 0),
|
|
|
- totalTimeText: this.stringForTime(this.duration || 0)
|
|
|
- };
|
|
|
-
|
|
|
- const hasNext = this.curIndex < this.songList.length - 1;
|
|
|
- const hasPrevious = this.curIndex > 0;
|
|
|
-
|
|
|
- // 添加详细的按钮状态日志
|
|
|
- LogUtils.getInstance().LOGI(`Button state calculation: curIndex=${this.curIndex}, songListLength=${this.songList.length}, hasNext=${hasNext}, hasPrevious=${hasPrevious}`);
|
|
|
-
|
|
|
- const playlist: PlaylistState = {
|
|
|
- hasNext: hasNext,
|
|
|
- hasPrevious: hasPrevious,
|
|
|
- currentIndex: this.curIndex,
|
|
|
- totalCount: this.songList.length
|
|
|
- };
|
|
|
-
|
|
|
- // 添加调试日志
|
|
|
- LogUtils.getInstance().LOGI(`Widget playlist state: curIndex=${this.curIndex}, songListLength=${this.songList.length}, hasNext=${hasNext}, hasPrevious=${hasPrevious}`);
|
|
|
-
|
|
|
- // 创建完整的WidgetData对象
|
|
|
- const widgetData: WidgetData = {
|
|
|
- playState: playState,
|
|
|
- currentSong: currentSong,
|
|
|
- progress: progress,
|
|
|
- playlist: playlist,
|
|
|
- config: {
|
|
|
- size: 'medium',
|
|
|
- theme: 'auto',
|
|
|
- showProgress: true,
|
|
|
- showCover: true
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- // 直接通过 Form ID 更新卡片数据(官方推荐方式)
|
|
|
- try {
|
|
|
- interface DirectFormUpdateServiceInstance {
|
|
|
- updateAllForms(data: WidgetData): Promise<void>;
|
|
|
- }
|
|
|
-
|
|
|
- interface DirectFormUpdateServiceClass {
|
|
|
- getInstance(): DirectFormUpdateServiceInstance;
|
|
|
- }
|
|
|
-
|
|
|
- interface DirectFormUpdateServiceModule {
|
|
|
- DirectFormUpdateService: DirectFormUpdateServiceClass;
|
|
|
- }
|
|
|
-
|
|
|
- import('../common/widget/DirectFormUpdateService').then((module: DirectFormUpdateServiceModule) => {
|
|
|
- const directFormService = module.DirectFormUpdateService.getInstance();
|
|
|
- directFormService.updateAllForms(widgetData).then(() => {
|
|
|
- LogUtils.getInstance().LOGI('Direct form update completed successfully');
|
|
|
- }).catch((error: Error) => {
|
|
|
- LogUtils.getInstance().error(`Direct form update failed: ${error.message}`);
|
|
|
- });
|
|
|
- }).catch((importError: Error) => {
|
|
|
- LogUtils.getInstance().error(`Failed to import DirectFormUpdateService: ${importError.message}`);
|
|
|
- });
|
|
|
- } catch (error) {
|
|
|
- LogUtils.getInstance().error(`Failed to update widget via DirectFormUpdateService: ${error}`);
|
|
|
- }
|
|
|
-
|
|
|
- const stateData: PlayerStateBroadcastData = {
|
|
|
- playState: playState,
|
|
|
- currentSong: currentSong,
|
|
|
- progress: progress,
|
|
|
- playlist: 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: hasNext=${hasNext}, hasPrevious=${hasPrevious}, songTitle=${currentSong.title}`);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 单独广播进度更新事件
|
|
|
- const progressPublishInfo: commonEventManager.CommonEventPublishData = {
|
|
|
- data: JSON.stringify(progress)
|
|
|
- };
|
|
|
-
|
|
|
- commonEventManager.publish(PLAYER_PROGRESS_CHANGED_EVENT, progressPublishInfo, (err) => {
|
|
|
- if (err) {
|
|
|
- LogUtils.getInstance().error(`Failed to broadcast progress: ${JSON.stringify(err)}`);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- } catch (error) {
|
|
|
- LogUtils.getInstance().error(`Failed to broadcast player state: ${error}`);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
|
|
|
}
|
|
|
|