|
|
@@ -14,9 +14,12 @@ import { AbilityConstant, Want } from '@kit.AbilityKit';
|
|
|
import { BusinessError, emitter } from '@kit.BasicServicesKit';
|
|
|
import { AppUtil, ArrayUtil, GlobalContext, LogUtil, StrUtil } from '@pura/harmony-utils';
|
|
|
import { DemoConstants } from './DemoConstants';
|
|
|
+import { rpc } from '@kit.IPCKit';
|
|
|
|
|
|
import { Utility } from '../common/util/Utility';
|
|
|
import { WXApi, WXEventHandler } from '../common/util/WXApiWrap';
|
|
|
+import { WidgetPlayerControlService } from '../common/service/WidgetPlayerControlService';
|
|
|
+import { IndependentPlayerService } from '../common/service/IndependentPlayerService';
|
|
|
|
|
|
import { systemShare } from '@kit.ShareKit';
|
|
|
import Logger from '../common/util/Logger';
|
|
|
@@ -25,6 +28,10 @@ import StatusBarViewExtensionAbility from '@hms.pcService.StatusBarViewExtension
|
|
|
import { SpiderMan } from '@simplepeng/spider-man';
|
|
|
import { smartMobilityCommon } from '@kit.CarKit';
|
|
|
import { url } from '@kit.ArkTS';
|
|
|
+import commonEventManager from '@ohos.commonEventManager';
|
|
|
+import { WIDGET_CONTROL_EVENT } from '../common/widget/WidgetEventConstants';
|
|
|
+import { EventData, WidgetControlParams } from '../common/widget/WidgetTypes';
|
|
|
+import { VideoItem } from '../viewmodel/VideoItem';
|
|
|
|
|
|
/**
|
|
|
* 卡片配置页面参数接口
|
|
|
@@ -44,6 +51,28 @@ interface WidgetDeleteParams extends Record<string, Object> {
|
|
|
action: string;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * RPC通信返回类型的实现,用于RPC通信数据序列化和反序列化
|
|
|
+ */
|
|
|
+class MyParcelable implements rpc.Parcelable {
|
|
|
+ num: number;
|
|
|
+ str: string;
|
|
|
+ constructor(num: number, str: string) {
|
|
|
+ this.num = num;
|
|
|
+ this.str = str;
|
|
|
+ }
|
|
|
+ marshalling(messageSequence: rpc.MessageSequence): boolean {
|
|
|
+ messageSequence.writeInt(this.num);
|
|
|
+ messageSequence.writeString(this.str);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ unmarshalling(messageSequence: rpc.MessageSequence): boolean {
|
|
|
+ this.num = messageSequence.readInt();
|
|
|
+ this.str = messageSequence.readString();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* 主Ability类,继承自UIAbility
|
|
|
* 负责:
|
|
|
@@ -103,6 +132,24 @@ export default class EntryAbility extends UIAbility {
|
|
|
async onCreate(want:Want, launchParam:AbilityConstant.LaunchParam) {
|
|
|
AppStorage.setOrCreate('context', this.context);
|
|
|
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
|
|
|
+
|
|
|
+ // 注册卡片call事件监听器
|
|
|
+ this.registerWidgetCallListeners();
|
|
|
+
|
|
|
+ // 初始化独立播放器服务(优先级最高)
|
|
|
+ try {
|
|
|
+ await IndependentPlayerService.getInstance().initialize(this.context);
|
|
|
+ hilog.info(0x0000, 'testTag', '✅ IndependentPlayerService initialized successfully');
|
|
|
+ } catch (error) {
|
|
|
+ hilog.error(0x0000, 'testTag', `❌ Failed to initialize IndependentPlayerService: ${error}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化卡片播放器控制服务(作为备用)
|
|
|
+ WidgetPlayerControlService.getInstance().initialize();
|
|
|
+
|
|
|
+ // 设置备用的CommonEvent监听器
|
|
|
+ this.setupBackupCommonEventListener();
|
|
|
+
|
|
|
setTimeout(async ()=>{
|
|
|
this.loadDoWant(want)
|
|
|
await this.handleParam(want)
|
|
|
@@ -181,6 +228,13 @@ export default class EntryAbility extends UIAbility {
|
|
|
|
|
|
onDestroy() {
|
|
|
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
|
|
|
+
|
|
|
+ // 注销卡片call事件监听器
|
|
|
+ this.unregisterWidgetCallListeners();
|
|
|
+
|
|
|
+ // 销毁卡片播放器控制服务
|
|
|
+ WidgetPlayerControlService.getInstance().destroy();
|
|
|
+
|
|
|
let types: smartMobilityCommon.SmartMobilityType[] = [smartMobilityCommon.SmartMobilityType.CAR_HOP];
|
|
|
// 出行连接状态回调函数
|
|
|
const callBack = (info: smartMobilityCommon.SmartMobilityInfo) => {
|
|
|
@@ -418,4 +472,339 @@ export default class EntryAbility extends UIAbility {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 注册卡片call事件监听器
|
|
|
+ */
|
|
|
+ private registerWidgetCallListeners(): void {
|
|
|
+ try {
|
|
|
+ // 监听播放/暂停事件
|
|
|
+ this.callee.on('playPause', (data: rpc.MessageSequence) => {
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget call: playPause received`);
|
|
|
+ const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget playPause params: ${JSON.stringify(params)}`);
|
|
|
+
|
|
|
+ // 发送播放/暂停事件到主应用
|
|
|
+ this.sendWidgetControlEvent('PLAY_PAUSE', params);
|
|
|
+
|
|
|
+ return new MyParcelable(1, 'playPause_success');
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听下一首事件
|
|
|
+ this.callee.on('nextSong', (data: rpc.MessageSequence) => {
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget call: nextSong received`);
|
|
|
+ const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget nextSong params: ${JSON.stringify(params)}`);
|
|
|
+
|
|
|
+ // 发送下一首事件到主应用
|
|
|
+ this.sendWidgetControlEvent('NEXT_SONG', params);
|
|
|
+
|
|
|
+ return new MyParcelable(2, 'nextSong_success');
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听上一首事件
|
|
|
+ this.callee.on('prevSong', (data: rpc.MessageSequence) => {
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget call: prevSong received`);
|
|
|
+ const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget prevSong params: ${JSON.stringify(params)}`);
|
|
|
+
|
|
|
+ // 发送上一首事件到主应用
|
|
|
+ this.sendWidgetControlEvent('PREV_SONG', params);
|
|
|
+
|
|
|
+ return new MyParcelable(3, 'prevSong_success');
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听打开应用事件
|
|
|
+ this.callee.on('openApp', (data: rpc.MessageSequence) => {
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget call: openApp received`);
|
|
|
+ const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget openApp params: ${JSON.stringify(params)}`);
|
|
|
+
|
|
|
+ // 发送打开应用事件到主应用
|
|
|
+ this.sendWidgetControlEvent('OPEN_APP', params);
|
|
|
+
|
|
|
+ return new MyParcelable(4, 'openApp_success');
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听播放控制事件(新增,符合文档标准)
|
|
|
+ this.callee.on('playByAction', (data: rpc.MessageSequence) => {
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget call: playByAction received`);
|
|
|
+ const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
|
|
|
+ const playActionType = params['playActionType'] as string;
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget playByAction type: ${playActionType}`);
|
|
|
+
|
|
|
+ // 发送播放控制事件
|
|
|
+ this.sendWidgetControlEvent(playActionType, params);
|
|
|
+
|
|
|
+ return new MyParcelable(5, 'playByAction_success');
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听收藏事件(新增,符合文档标准)
|
|
|
+ this.callee.on('collectAction', (data: rpc.MessageSequence) => {
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget call: collectAction received`);
|
|
|
+ const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
|
|
|
+ const collectActionType = params['collectActionType'] as string;
|
|
|
+ const songId = params['songId'] as string;
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget collectAction type: ${collectActionType}, songId: ${songId}`);
|
|
|
+
|
|
|
+ return new MyParcelable(6, 'collectAction_success');
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监听卡片更新请求事件(新增,符合文档标准)
|
|
|
+ this.callee.on('requestUpdatePlayCard', (data: rpc.MessageSequence) => {
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget call: requestUpdatePlayCard received`);
|
|
|
+ const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
|
|
|
+ const formId = params['formId'] as string;
|
|
|
+ hilog.info(0x0000, 'testTag', `Widget requestUpdate formId: ${formId}`);
|
|
|
+
|
|
|
+ // 更新卡片数据
|
|
|
+ this.updatePlayCard(formId);
|
|
|
+
|
|
|
+ return new MyParcelable(7, 'requestUpdatePlayCard_success');
|
|
|
+ });
|
|
|
+
|
|
|
+ hilog.info(0x0000, 'testTag', 'Widget call listeners registered successfully');
|
|
|
+ } catch (err) {
|
|
|
+ hilog.error(0x0000, 'testTag', `Failed to register widget call listeners: ${JSON.stringify(err as BusinessError)}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 注销卡片call事件监听器
|
|
|
+ */
|
|
|
+ private unregisterWidgetCallListeners(): void {
|
|
|
+ try {
|
|
|
+ this.callee.off('playPause');
|
|
|
+ this.callee.off('nextSong');
|
|
|
+ this.callee.off('prevSong');
|
|
|
+ this.callee.off('openApp');
|
|
|
+ this.callee.off('playByAction');
|
|
|
+ this.callee.off('collectAction');
|
|
|
+ this.callee.off('requestUpdatePlayCard');
|
|
|
+ hilog.info(0x0000, 'testTag', 'Widget call listeners unregistered successfully');
|
|
|
+ } catch (err) {
|
|
|
+ hilog.error(0x0000, 'testTag', `Failed to unregister widget call listeners: ${JSON.stringify(err as BusinessError)}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新播放卡片
|
|
|
+ */
|
|
|
+ private async updatePlayCard(formId: string): Promise<void> {
|
|
|
+ try {
|
|
|
+ hilog.info(0x0000, 'testTag', `Updating play card: ${formId}`);
|
|
|
+
|
|
|
+ // 获取当前播放状态
|
|
|
+ const isPlay = AppStorage.get<boolean>('isPlay') || false;
|
|
|
+ const currentSong = AppStorage.get<VideoItem>('currentSong');
|
|
|
+
|
|
|
+ if (currentSong) {
|
|
|
+ // 使用FormUtils更新卡片
|
|
|
+ import('../common/widget/FormUtils').then((module) => {
|
|
|
+ const formUtils = module.FormUtils.getInstance();
|
|
|
+ formUtils.updateMusicControlCards(this.context, currentSong, isPlay);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ hilog.info(0x0000, 'testTag', `Play card update completed: ${formId}`);
|
|
|
+ } catch (error) {
|
|
|
+ hilog.error(0x0000, 'testTag', `Failed to update play card: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送卡片控制事件到主应用
|
|
|
+ */
|
|
|
+ private async sendWidgetControlEvent(command: string, params: Record<string, Object>): Promise<void> {
|
|
|
+ try {
|
|
|
+ hilog.info(0x0000, 'testTag', `🎵 Processing widget control command: ${command}`);
|
|
|
+
|
|
|
+ // 确保IndependentPlayerService已初始化
|
|
|
+ const independentService = IndependentPlayerService.getInstance();
|
|
|
+ if (!independentService.getCurrentState().currentSong && !independentService.getCurrentState().playlist.length) {
|
|
|
+ hilog.info(0x0000, 'testTag', '🔄 IndependentPlayerService not ready, reinitializing...');
|
|
|
+ try {
|
|
|
+ await independentService.initialize(this.context);
|
|
|
+ } catch (error) {
|
|
|
+ hilog.error(0x0000, 'testTag', `❌ Failed to reinitialize IndependentPlayerService: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 优先通过IndependentPlayerService直接处理
|
|
|
+ const eventData: emitter.EventData = {
|
|
|
+ data: {
|
|
|
+ command: command,
|
|
|
+ params: params,
|
|
|
+ source: 'widget_call',
|
|
|
+ timestamp: Date.now()
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 发送到IndependentPlayerService(事件ID: 9001)
|
|
|
+ emitter.emit({ eventId: 9001 }, eventData);
|
|
|
+ hilog.info(0x0000, 'testTag', `✅ Widget control event sent to IndependentPlayerService: ${command}`);
|
|
|
+
|
|
|
+ // 同时发送CommonEvent作为备用,确保LocalMusic也能接收到(如果已经初始化)
|
|
|
+ // this.sendCommonEventToLocalMusic(command, params);
|
|
|
+ } catch (error) {
|
|
|
+ hilog.error(0x0000, 'testTag', `❌ Failed to send widget control event: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送CommonEvent到LocalMusic
|
|
|
+ */
|
|
|
+ private async sendCommonEventToLocalMusic(command: string, params: Record<string, Object>): Promise<void> {
|
|
|
+ try {
|
|
|
+ // 构造事件数据
|
|
|
+ interface EventData {
|
|
|
+ command: string;
|
|
|
+ params: Record<string, Object>;
|
|
|
+ timestamp: number;
|
|
|
+ source: string;
|
|
|
+ }
|
|
|
+
|
|
|
+ const eventData: EventData = {
|
|
|
+ command: this.mapCommandToWidgetCommand(command),
|
|
|
+ params: params,
|
|
|
+ timestamp: Date.now(),
|
|
|
+ source: 'widget_call'
|
|
|
+ };
|
|
|
+
|
|
|
+ // 发送CommonEvent
|
|
|
+ const publishInfo: commonEventManager.CommonEventPublishData = {
|
|
|
+ data: JSON.stringify(eventData)
|
|
|
+ };
|
|
|
+
|
|
|
+ hilog.info(0x0000, 'testTag', `📤 Publishing CommonEvent: ${WIDGET_CONTROL_EVENT}`);
|
|
|
+
|
|
|
+ commonEventManager.publish(WIDGET_CONTROL_EVENT, publishInfo, (err: Error) => {
|
|
|
+ if (err) {
|
|
|
+ hilog.error(0x0000, 'testTag', `❌ Failed to publish CommonEvent: ${err}`);
|
|
|
+ } else {
|
|
|
+ hilog.info(0x0000, 'testTag', `✅ CommonEvent published successfully: ${command}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 延迟重试机制,确保LocalMusic有时间初始化
|
|
|
+ // const retryDelays = [2000, 5000, 8000]; // 2秒、5秒、8秒后重试
|
|
|
+ // retryDelays.forEach((delay, index) => {
|
|
|
+ // setTimeout(() => {
|
|
|
+ // hilog.info(0x0000, 'testTag', `🔄 Retry ${index + 1}: Publishing CommonEvent after ${delay}ms`);
|
|
|
+ // commonEventManager.publish(WIDGET_CONTROL_EVENT, publishInfo, (err: Error) => {
|
|
|
+ // if (err) {
|
|
|
+ // hilog.error(0x0000, 'testTag', `❌ Retry ${index + 1} failed: ${err}`);
|
|
|
+ // } else {
|
|
|
+ // hilog.info(0x0000, 'testTag', `✅ Retry ${index + 1} succeeded: ${command}`);
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+ // }, delay);
|
|
|
+ // });
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ hilog.error(0x0000, 'testTag', `❌ Failed to send CommonEvent: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 映射命令字符串到WidgetCommand
|
|
|
+ */
|
|
|
+ private mapCommandToWidgetCommand(command: string): string {
|
|
|
+ switch (command) {
|
|
|
+ case 'PLAY_PAUSE':
|
|
|
+ return 'PLAY_PAUSE';
|
|
|
+ case 'NEXT_SONG':
|
|
|
+ return 'NEXT_SONG';
|
|
|
+ case 'PREV_SONG':
|
|
|
+ return 'PREV_SONG';
|
|
|
+ default:
|
|
|
+ return command;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置备用的CommonEvent监听器
|
|
|
+ * 当主应用页面还没加载时,EntryAbility作为备用接收器
|
|
|
+ */
|
|
|
+ private async setupBackupCommonEventListener(): Promise<void> {
|
|
|
+ try {
|
|
|
+ hilog.info(0x0000, 'testTag', '🔧 Setting up backup CommonEvent listener...');
|
|
|
+
|
|
|
+ // 订阅WIDGET_CONTROL_EVENT,作为LocalMusic的备用处理器
|
|
|
+ const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
|
|
|
+ events: [WIDGET_CONTROL_EVENT]
|
|
|
+ };
|
|
|
+
|
|
|
+ const subscriber = await commonEventManager.createSubscriber(subscribeInfo);
|
|
|
+
|
|
|
+ await commonEventManager.subscribe(subscriber, (err, data: commonEventManager.CommonEventData) => {
|
|
|
+ if (!err && data.event === WIDGET_CONTROL_EVENT) {
|
|
|
+ hilog.info(0x0000, 'testTag', `🎵 EntryAbility received widget control event: ${data.data}`);
|
|
|
+ this.handleBackupWidgetControl(data.data || '{}');
|
|
|
+ } else if (err) {
|
|
|
+ hilog.error(0x0000, 'testTag', `❌ Backup CommonEvent error: ${JSON.stringify(err)}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ hilog.info(0x0000, 'testTag', '✅ Backup CommonEvent listener setup completed');
|
|
|
+ } catch (error) {
|
|
|
+ hilog.error(0x0000, 'testTag', `❌ Failed to setup backup CommonEvent listener: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 备用的卡片控制处理器
|
|
|
+ * 当LocalMusic还没有准备好时,EntryAbility临时处理卡片事件
|
|
|
+ */
|
|
|
+ private handleBackupWidgetControl(dataStr: string): void {
|
|
|
+ try {
|
|
|
+ const eventData = JSON.parse(dataStr) as EventData;
|
|
|
+ hilog.info(0x0000, 'testTag', `🎵 EntryAbility processing backup widget command: ${eventData.command}`);
|
|
|
+
|
|
|
+ // 通过emitter发送事件到主应用,确保LocalMusic能接收到
|
|
|
+ const emitterEventData: emitter.EventData = {
|
|
|
+ data: {
|
|
|
+ command: eventData.command,
|
|
|
+ params: eventData.params || {},
|
|
|
+ source: 'widget_backup_handler',
|
|
|
+ timestamp: Date.now()
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 使用特定的事件ID发送卡片控制事件
|
|
|
+ emitter.emit({ eventId: 9001 }, emitterEventData);
|
|
|
+ hilog.info(0x0000, 'testTag', `🎵 EntryAbility forwarded widget command via emitter: ${eventData.command}`);
|
|
|
+
|
|
|
+ // 同时延迟重发CommonEvent,给LocalMusic更多时间初始化
|
|
|
+ setTimeout(() => {
|
|
|
+ this.retryCommonEventForLocalMusic(eventData);
|
|
|
+ }, 3000);
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ hilog.error(0x0000, 'testTag', `❌ Failed to handle backup widget control: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 延迟重发CommonEvent给LocalMusic
|
|
|
+ */
|
|
|
+ private async retryCommonEventForLocalMusic(eventData: EventData): Promise<void> {
|
|
|
+ try {
|
|
|
+ const publishInfo: commonEventManager.CommonEventPublishData = {
|
|
|
+ data: JSON.stringify(eventData)
|
|
|
+ };
|
|
|
+
|
|
|
+ hilog.info(0x0000, 'testTag', `🔄 EntryAbility retrying CommonEvent for LocalMusic: ${eventData.command}`);
|
|
|
+
|
|
|
+ commonEventManager.publish(WIDGET_CONTROL_EVENT, publishInfo, (err: Error) => {
|
|
|
+ if (err) {
|
|
|
+ hilog.error(0x0000, 'testTag', `❌ Retry CommonEvent failed: ${err}`);
|
|
|
+ } else {
|
|
|
+ hilog.info(0x0000, 'testTag', `✅ Retry CommonEvent succeeded: ${eventData.command}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ hilog.error(0x0000, 'testTag', `❌ Failed to retry CommonEvent: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|