|
@@ -23,6 +23,7 @@ import Logger from '../common/util/Logger';
|
|
|
import { photoAccessHelper } from '@kit.MediaLibraryKit';
|
|
import { photoAccessHelper } from '@kit.MediaLibraryKit';
|
|
|
import { Utility } from '../common/util/Utility';
|
|
import { Utility } from '../common/util/Utility';
|
|
|
import { BusinessError, emitter } from '@kit.BasicServicesKit';
|
|
import { BusinessError, emitter } from '@kit.BasicServicesKit';
|
|
|
|
|
+import commonEventManager from '@ohos.commonEventManager';
|
|
|
import { BubbleBean } from '../viewmodel/BubbleBean';
|
|
import { BubbleBean } from '../viewmodel/BubbleBean';
|
|
|
import { PopupPosition, XPopup } from '@chinalike/popup';
|
|
import { PopupPosition, XPopup } from '@chinalike/popup';
|
|
|
import { common, ConfigurationConstant } from '@kit.AbilityKit';
|
|
import { common, ConfigurationConstant } from '@kit.AbilityKit';
|
|
@@ -36,6 +37,15 @@ import { effectKit } from '@kit.ArkGraphics2D';
|
|
|
import { ColorConversion } from '../common/util/ColorConversion';
|
|
import { ColorConversion } from '../common/util/ColorConversion';
|
|
|
import { LyricController, LyricParser, LyricView2 } from '@seagazer/cclyric';
|
|
import { LyricController, LyricParser, LyricView2 } from '@seagazer/cclyric';
|
|
|
import { AvSessionController } from '../controller/AvSessionController';
|
|
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 {
|
|
import {
|
|
|
// DeviceChangeReason,
|
|
// DeviceChangeReason,
|
|
|
IjkMediaPlayer,
|
|
IjkMediaPlayer,
|
|
@@ -77,6 +87,7 @@ import { TextNodeController } from './PipLyricTextBuilder';
|
|
|
import { IndexerView } from './IndexerView';
|
|
import { IndexerView } from './IndexerView';
|
|
|
import { KeyCode } from '@kit.InputKit';
|
|
import { KeyCode } from '@kit.InputKit';
|
|
|
import { KnockController } from '../controller/KnockController';
|
|
import { KnockController } from '../controller/KnockController';
|
|
|
|
|
+import { WidgetCommand, EventData, WidgetControlParams } from '../common/widget/WidgetTypes';
|
|
|
// import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
|
|
// import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
|
|
|
const TAG = 'LocalMusic';
|
|
const TAG = 'LocalMusic';
|
|
|
|
|
|
|
@@ -186,6 +197,9 @@ export struct LocalMusic {
|
|
|
@State isCircleBtn: boolean = false //是否圆形播放按钮
|
|
@State isCircleBtn: boolean = false //是否圆形播放按钮
|
|
|
@State twoFingerType: number = 3 //双支放大缩小的类型
|
|
@State twoFingerType: number = 3 //双支放大缩小的类型
|
|
|
@State isScrollHide: boolean = false //是否滚动隐藏
|
|
@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 isSameTimePlay: boolean = false //是否和其他app同时播放
|
|
|
@State isStartAutoPlay: boolean = false //启动后自动播放
|
|
@State isStartAutoPlay: boolean = false //启动后自动播放
|
|
|
@State isShowPlayPageBack: boolean = false //是否显示播放页返回键
|
|
@State isShowPlayPageBack: boolean = false //是否显示播放页返回键
|
|
@@ -525,6 +539,8 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
this.makeWorker()
|
|
this.makeWorker()
|
|
|
|
|
|
|
|
|
|
+ // 初始化卡片事件监听器
|
|
|
|
|
+ this.initWidgetEventListener();
|
|
|
|
|
|
|
|
//折叠屏的屏幕显示模式变化
|
|
//折叠屏的屏幕显示模式变化
|
|
|
display.on('foldDisplayModeChange', (data) => {
|
|
display.on('foldDisplayModeChange', (data) => {
|
|
@@ -545,21 +561,24 @@ export struct LocalMusic {
|
|
|
AppStorage.setOrCreate('themeColor', themeColor);
|
|
AppStorage.setOrCreate('themeColor', themeColor);
|
|
|
this.themeColor = themeColor;
|
|
this.themeColor = themeColor;
|
|
|
this.doChangeSetting()
|
|
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) => {
|
|
this.eventHub.on('onStateChange', (fg: boolean) => {
|
|
|
if (fg && this.curState === 'STARTED') {
|
|
if (fg && this.curState === 'STARTED') {
|
|
@@ -871,7 +890,30 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
this.stopPip();
|
|
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) {
|
|
async getSortedFiles(curPath: string, isWorkerPost?: boolean, destPath?: string, isOpen?: boolean) {
|
|
@@ -6272,6 +6314,7 @@ export struct LocalMusic {
|
|
|
eventHub = getContext().eventHub;
|
|
eventHub = getContext().eventHub;
|
|
|
//投播组件
|
|
//投播组件
|
|
|
private avSessionController: AvSessionController = AvSessionController.getInstance(false);
|
|
private avSessionController: AvSessionController = AvSessionController.getInstance(false);
|
|
|
|
|
+ private avSessionWidgetListener: AvSessionWidgetListener = AvSessionWidgetListener.getInstance();
|
|
|
private castController: avSession.AVCastController | undefined = undefined;
|
|
private castController: avSession.AVCastController | undefined = undefined;
|
|
|
@State isCastPlaying: boolean = false;
|
|
@State isCastPlaying: boolean = false;
|
|
|
@State currentTime2: number = 0;
|
|
@State currentTime2: number = 0;
|
|
@@ -8923,11 +8966,13 @@ export struct LocalMusic {
|
|
|
*
|
|
*
|
|
|
*/
|
|
*/
|
|
|
setOrientation(orientation: number) {
|
|
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.updateSessionPlayState(true)
|
|
|
this.watchStatus();
|
|
this.watchStatus();
|
|
|
this.updateLastPlayTimeStr(this.videoUrl)
|
|
this.updateLastPlayTimeStr(this.videoUrl)
|
|
|
|
|
+ // 广播状态变化到卡片
|
|
|
|
|
+ this.broadcastPlayerState();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
updateLastPlayTimeStr(filePath: string) {
|
|
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,
|
|
this.pipController.updatePiPControlStatus(PiPWindow.PiPControlType.VIDEO_PLAY_PAUSE,
|
|
|
PiPWindow.PiPControlStatus.PAUSE);
|
|
PiPWindow.PiPControlStatus.PAUSE);
|
|
|
}
|
|
}
|
|
|
|
|
+ // 广播状态变化到卡片
|
|
|
|
|
+ this.broadcastPlayerState();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -10341,6 +10393,8 @@ export struct LocalMusic {
|
|
|
this.updateSessionPlayState(false)
|
|
this.updateSessionPlayState(false)
|
|
|
this.playChange()
|
|
this.playChange()
|
|
|
this.watchStatus();
|
|
this.watchStatus();
|
|
|
|
|
+ // 广播状态变化到卡片
|
|
|
|
|
+ this.broadcastPlayerState();
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -10468,6 +10522,11 @@ export struct LocalMusic {
|
|
|
}else{
|
|
}else{
|
|
|
this.cover = this.songList[this.curIndex].pixelMapPath
|
|
this.cover = this.songList[this.curIndex].pixelMapPath
|
|
|
this.startPlayOrResumePlay()
|
|
this.startPlayOrResumePlay()
|
|
|
|
|
+ // 广播歌曲变化到卡片
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.broadcastPlayerState();
|
|
|
|
|
+ }, 200);
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
@@ -10565,6 +10624,11 @@ export struct LocalMusic {
|
|
|
this.videoUrl = this.songList[this.curIndex].filePath;
|
|
this.videoUrl = this.songList[this.curIndex].filePath;
|
|
|
this.name = this.songList[this.curIndex].name
|
|
this.name = this.songList[this.curIndex].name
|
|
|
this.artist = this.songList[this.curIndex].artist
|
|
this.artist = this.songList[this.curIndex].artist
|
|
|
|
|
+ this.startPlayOrResumePlay()
|
|
|
|
|
+ // 广播歌曲变化到卡片
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.broadcastPlayerState();
|
|
|
|
|
+ }, 200);
|
|
|
this.changeImageAnimation()
|
|
this.changeImageAnimation()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -10614,10 +10678,12 @@ export struct LocalMusic {
|
|
|
*/
|
|
*/
|
|
|
watchStatus() {
|
|
watchStatus() {
|
|
|
// let windowClass = GlobalContext.getContext().getObject('windowClass') as window.Window;
|
|
// 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}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|