| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- import commonEventManager from '@ohos.commonEventManager';
- import Want from '@ohos.app.ability.Want';
- import common from '@ohos.app.ability.common';
- import { hilog } from '@kit.PerformanceAnalysisKit';
- import { WidgetData, WidgetCommand, EventData, RequestData, WidgetControlParams } from './WidgetTypes';
- import { WidgetTypeHelpers } from './WidgetTypeHelpers';
- import {
- WIDGET_CONTROL_EVENT,
- WIDGET_REQUEST_STATE_EVENT,
- PLAYER_STATE_CHANGED_EVENT,
- PLAYER_SONG_CHANGED_EVENT,
- PLAYER_PROGRESS_CHANGED_EVENT,
- APP_BUNDLE_NAME,
- APP_ABILITY_NAME
- } from './WidgetEventConstants';
- import { AvSessionWidgetListener } from './AvSessionWidgetListener';
- const TAG = 'Heanup PlayerControlService';
- /**
- * 启动参数接口
- */
- interface LaunchParameters {
- page: string;
- source: string;
- timestamp: string;
- }
- /**
- * 播放器控制服务
- * 负责与主应用的播放器进行通信和状态同步
- */
- export class PlayerControlService {
- private stateListeners: Array<(data: WidgetData) => void> = [];
- private isListenerRegistered: boolean = false;
- private avSessionListener: AvSessionWidgetListener;
- constructor() {
- this.avSessionListener = AvSessionWidgetListener.getInstance();
- this.initializeEventListener();
- this.initializeAvSessionListener();
- }
- /**
- * 初始化事件监听器
- */
- private async initializeEventListener(): Promise<void> {
- if (this.isListenerRegistered) {
- hilog.info(0x0000, TAG, 'Event listener already registered');
- return;
- }
- try {
- // 监听播放状态变化事件
- const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
- events: [
- PLAYER_STATE_CHANGED_EVENT,
- PLAYER_SONG_CHANGED_EVENT,
- PLAYER_PROGRESS_CHANGED_EVENT
- ]
- };
- hilog.info(0x0000, TAG, `Subscribing to events: ${subscribeInfo.events.join(', ')}`);
- const subscriber = await commonEventManager.createSubscriber(subscribeInfo);
-
- await commonEventManager.subscribe(subscriber, (err, data: commonEventManager.CommonEventData) => {
- if (!err) {
- hilog.info(0x0000, TAG, `📡 CommonEvent received in Form process: ${data.event}`);
- hilog.info(0x0000, TAG, `📡 Event data length: ${data.data?.length || 0} characters`);
- this.handlePlayerStateChange(data);
- } else {
- hilog.error(0x0000, TAG, `❌ CommonEvent error: ${JSON.stringify(err)}`);
- }
- });
- this.isListenerRegistered = true;
- hilog.info(0x0000, TAG, 'Event listener initialized successfully');
-
- // 立即请求当前状态,确保新进程能获取到最新数据
- setTimeout(() => {
- this.requestCurrentState();
- }, 1000);
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to initialize event listener: ${error}`);
- }
- }
- /**
- * 请求当前播放状态
- */
- private async requestCurrentState(): Promise<void> {
- try {
- const requestData: RequestData = {
- timestamp: Date.now(),
- source: 'widget_form_process_recovery'
- };
- const requestInfo: commonEventManager.CommonEventPublishData = {
- data: JSON.stringify(requestData)
- };
- await commonEventManager.publish(WIDGET_REQUEST_STATE_EVENT, requestInfo, (err) => {
- if (err) {
- hilog.error(0x0000, TAG, `Failed to request current state: ${err}`);
- } else {
- hilog.info(0x0000, TAG, 'Current state requested from main app for recovery');
- }
- });
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to request current state: ${error}`);
- }
- }
- /**
- * 强制重新连接和同步状态
- */
- async forceReconnect(): Promise<void> {
- try {
- hilog.info(0x0000, TAG, 'Force reconnecting to main app...');
-
- // 重新请求当前状态
- await this.requestCurrentState();
-
- // 等待一段时间后再次请求,确保能收到响应
- setTimeout(async () => {
- await this.requestCurrentState();
- }, 2000);
-
- hilog.info(0x0000, TAG, 'Force reconnect completed');
- } catch (error) {
- hilog.error(0x0000, TAG, `Force reconnect failed: ${error}`);
- }
- }
- /**
- * 发送控制命令到主应用
- */
- async sendControlCommand(command: WidgetCommand, params?: WidgetControlParams): Promise<boolean> {
- try {
- const defaultParams: WidgetControlParams = {};
- const eventData: EventData = {
- command: command,
- params: params || defaultParams,
- timestamp: Date.now(),
- source: 'widget'
- };
- // 发送CommonEvent到主应用
- const publishInfo: commonEventManager.CommonEventPublishData = {
- data: JSON.stringify(eventData)
- };
- await commonEventManager.publish(WIDGET_CONTROL_EVENT, publishInfo, (err) => {
- if (err) {
- hilog.error(0x0000, TAG, `Failed to publish event: ${err}`);
- }
- });
- hilog.info(0x0000, TAG, `Control command sent: ${command}`);
- return true;
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to send control command: ${error}`);
- return false;
- }
- }
- /**
- * 获取当前播放状态
- */
- async getCurrentPlayState(): Promise<WidgetData> {
- try {
- // 优先从AvSession获取当前状态
- const avSessionData = this.avSessionListener.getCurrentWidgetData();
-
- // 同时请求CommonEvent状态作为备用
- const requestData: RequestData = {
- timestamp: Date.now(),
- source: 'widget_form_process'
- };
- const requestInfo: commonEventManager.CommonEventPublishData = {
- data: JSON.stringify(requestData)
- };
- commonEventManager.publish(WIDGET_REQUEST_STATE_EVENT, requestInfo, (err) => {
- if (err) {
- hilog.error(0x0000, TAG, `Failed to publish request state event: ${err}`);
- }
- });
- return avSessionData;
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to get current play state: ${error}`);
- return this.getDefaultWidgetData();
- }
- }
- /**
- * 初始化AvSession监听器
- */
- private initializeAvSessionListener(): void {
- try {
- // 注册AvSession状态监听器,这个监听器主要用于EntryFormAbility的全局监听
- // 不要在这里注册,让EntryFormAbility直接注册到AvSessionWidgetListener
- hilog.info(0x0000, TAG, 'AvSession listener initialized successfully (no direct registration needed)');
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to initialize AvSession listener: ${error}`);
- }
- }
- /**
- * 注册状态变化监听器
- */
- registerStateListener(callback: (data: WidgetData) => void): void {
- // 检查是否已经注册过相同的监听器,避免重复注册
- if (this.stateListeners.indexOf(callback) === -1) {
- this.stateListeners.push(callback);
- hilog.info(0x0000, TAG, `State listener registered, total listeners: ${this.stateListeners.length}`);
- } else {
- hilog.warn(0x0000, TAG, 'State listener already registered, skipping');
- return;
- }
-
- // 延迟获取当前状态,给主应用时间来广播真实状态
- setTimeout(() => {
- try {
- const currentData = this.avSessionListener.getCurrentWidgetData();
- hilog.info(0x0000, TAG, `Sending current data to listener: hasNext=${currentData.playlist.hasNext}, hasPrevious=${currentData.playlist.hasPrevious}`);
- callback(currentData);
- } catch (error) {
- hilog.error(0x0000, TAG, `Error getting current AvSession data: ${error}`);
- }
- }, 1500); // 延迟1.5秒,确保主应用已经广播了状态
- }
- /**
- * 启动主应用
- */
- async launchMainApp(page?: string, params?: Record<string, Object>): Promise<boolean> {
- try {
- interface LaunchParameters {
- page: string;
- source: string;
- timestamp: string;
- }
-
- const baseParams: LaunchParameters = {
- page: page || 'main',
- source: 'widget', // 标识来源是卡片
- timestamp: Date.now().toString()
- };
- // 创建Want对象
- const want: Want = {
- bundleName: APP_BUNDLE_NAME,
- abilityName: APP_ABILITY_NAME,
- parameters: {
- page: baseParams.page,
- source: baseParams.source,
- timestamp: baseParams.timestamp
- }
- };
- // 添加额外参数
- if (params && want.parameters) {
- const paramKeys = Object.keys(params);
- for (let i = 0; i < paramKeys.length; i++) {
- const key = paramKeys[i];
- want.parameters[key] = params[key];
- }
- }
- const context = getContext() as common.UIAbilityContext;
- await context.startAbility(want);
-
- hilog.info(0x0000, TAG, `Main app launched with page: ${page}, params: ${JSON.stringify(params)}`);
- return true;
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to launch main app: ${error}`);
-
- // 如果启动失败,尝试启动到默认页面
- try {
- const fallbackWant: Want = {
- bundleName: APP_BUNDLE_NAME,
- abilityName: APP_ABILITY_NAME
- };
-
- const context = getContext() as common.UIAbilityContext;
- await context.startAbility(fallbackWant);
-
- hilog.info(0x0000, TAG, 'Main app launched with fallback method');
- return true;
- } catch (fallbackError) {
- hilog.error(0x0000, TAG, `Fallback launch also failed: ${fallbackError}`);
- return false;
- }
- }
- }
- /**
- * 处理播放器状态变化
- */
- private handlePlayerStateChange(eventData: commonEventManager.CommonEventData): void {
- try {
- hilog.info(0x0000, TAG, `📨 Form process handling CommonEvent: ${eventData.event}`);
- hilog.info(0x0000, TAG, `📨 Event data: ${eventData.data?.substring(0, 200)}...`);
-
- const data = JSON.parse(eventData.data || '{}') as Object;
- let widgetData: WidgetData;
- if (eventData.event === PLAYER_PROGRESS_CHANGED_EVENT) {
- // 处理进度更新事件
- widgetData = this.updateProgressData(data);
- } else {
- // 处理完整状态更新事件
- widgetData = this.convertToWidgetData(data);
- }
-
- hilog.info(0x0000, TAG, `📨 Form process converted data: isPlaying=${widgetData.playState.isPlaying}, title=${widgetData.currentSong.title}`);
-
- // 只更新AvSession监听器的数据,避免重复通知
- // AvSession监听器会自动通知所有注册的监听器
- this.avSessionListener.updateWidgetData(widgetData);
-
- hilog.info(0x0000, TAG, `📨 Form process: ${eventData.event} handled, data updated in AvSession`);
- } catch (error) {
- hilog.error(0x0000, TAG, `❌ Form process failed to handle player state change: ${error}`);
- }
- }
- /**
- * 更新进度数据
- */
- private updateProgressData(progressData: Object): WidgetData {
- try {
- const data: Record<string, Object> = progressData as Record<string, Object>;
- // 获取当前缓存的数据,而不是默认数据
- const currentData = this.avSessionListener.getCurrentWidgetData();
-
- // 只更新进度相关数据,保持其他状态不变
- currentData.progress = {
- currentPosition: (data['currentPosition'] as number) || 0,
- duration: (data['duration'] as number) || 0,
- percentage: (data['percentage'] as number) || 0,
- currentTimeText: (data['currentTimeText'] as string) || '00:00',
- totalTimeText: (data['totalTimeText'] as string) || '00:00'
- };
-
- // 更新缓存
- this.avSessionListener.updateWidgetData(currentData);
-
- hilog.info(0x0000, TAG, `Progress updated: ${currentData.progress.percentage.toFixed(1)}%, ${currentData.progress.currentTimeText}/${currentData.progress.totalTimeText}`);
-
- return currentData;
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to update progress data: ${error}`);
- return this.getDefaultWidgetData();
- }
- }
- /**
- * 转换播放器数据为卡片数据格式
- */
- private convertToWidgetData(playerData: Object): WidgetData {
- try {
- const data: Record<string, Object> = playerData as Record<string, Object>;
-
- // 正确解析PlayerStateBroadcastData结构
- const playState = (data['playState'] as Record<string, Object>) || {};
- const currentSong = (data['currentSong'] as Record<string, Object>) || {};
- const progress = (data['progress'] as Record<string, Object>) || {};
- const playlist = (data['playlist'] as Record<string, Object>) || {};
-
- // 检查是否接收到不完整的数据
- const isPlaylistDataIncomplete = playlist['hasNext'] === undefined ||
- playlist['hasPrevious'] === undefined ||
- playlist['currentIndex'] === undefined ||
- playlist['totalCount'] === undefined;
-
- if (isPlaylistDataIncomplete) {
- hilog.warn(0x0000, TAG, `Received incomplete playlist data, using cached data`);
- // 如果接收到不完整的数据,返回当前缓存的数据
- const cachedData = this.avSessionListener.getCurrentWidgetData();
-
- // 只更新非playlist的数据,保持playlist数据不变
- const updatedData: WidgetData = {
- playState: {
- isPlaying: (playState['isPlaying'] as boolean) !== undefined ? (playState['isPlaying'] as boolean) : cachedData.playState.isPlaying,
- isPaused: (playState['isPaused'] as boolean) !== undefined ? (playState['isPaused'] as boolean) : cachedData.playState.isPaused,
- isLoading: (playState['isLoading'] as boolean) !== undefined ? (playState['isLoading'] as boolean) : cachedData.playState.isLoading
- },
- currentSong: {
- id: (currentSong['id'] as string) || cachedData.currentSong.id,
- title: (currentSong['title'] as string) || cachedData.currentSong.title,
- artist: (currentSong['artist'] as string) || cachedData.currentSong.artist,
- album: (currentSong['album'] as string) || cachedData.currentSong.album,
- coverImagePath: (currentSong['coverImagePath'] as string) || cachedData.currentSong.coverImagePath,
- duration: (currentSong['duration'] as number) || cachedData.currentSong.duration
- },
- progress: {
- currentPosition: (progress['currentPosition'] as number) !== undefined ? (progress['currentPosition'] as number) : cachedData.progress.currentPosition,
- duration: (progress['duration'] as number) !== undefined ? (progress['duration'] as number) : cachedData.progress.duration,
- percentage: (progress['percentage'] as number) !== undefined ? (progress['percentage'] as number) : cachedData.progress.percentage,
- currentTimeText: (progress['currentTimeText'] as string) || cachedData.progress.currentTimeText,
- totalTimeText: (progress['totalTimeText'] as string) || cachedData.progress.totalTimeText
- },
- playlist: cachedData.playlist, // 保持缓存的playlist数据
- config: cachedData.config
- };
-
- hilog.info(0x0000, TAG, `Using cached playlist data: hasNext=${updatedData.playlist.hasNext}, hasPrevious=${updatedData.playlist.hasPrevious}, currentIndex=${updatedData.playlist.currentIndex}, totalCount=${updatedData.playlist.totalCount}`);
- return updatedData;
- }
-
- const widgetData: WidgetData = {
- playState: {
- isPlaying: (playState['isPlaying'] as boolean) || false,
- isPaused: (playState['isPaused'] as boolean) || true,
- isLoading: (playState['isLoading'] as boolean) || false
- },
- currentSong: {
- id: (currentSong['id'] as string) || '',
- title: (currentSong['title'] as string) || '暂无播放',
- artist: (currentSong['artist'] as string) || '未知艺术家',
- album: (currentSong['album'] as string) || '未知专辑',
- coverImagePath: (currentSong['coverImagePath'] as string) || '',
- duration: (currentSong['duration'] as number) || 0
- },
- progress: {
- currentPosition: (progress['currentPosition'] as number) || 0,
- duration: (progress['duration'] as number) || 0,
- percentage: (progress['percentage'] as number) || 0,
- currentTimeText: (progress['currentTimeText'] as string) || '00:00',
- totalTimeText: (progress['totalTimeText'] as string) || '00:00'
- },
- playlist: {
- hasNext: (playlist['hasNext'] as boolean) !== undefined ? (playlist['hasNext'] as boolean) : false,
- hasPrevious: (playlist['hasPrevious'] as boolean) !== undefined ? (playlist['hasPrevious'] as boolean) : false,
- currentIndex: (playlist['currentIndex'] as number) !== undefined ? (playlist['currentIndex'] as number) : 0,
- totalCount: (playlist['totalCount'] as number) !== undefined ? (playlist['totalCount'] as number) : 0
- },
- config: {
- size: 'medium',
- theme: 'auto',
- showProgress: true,
- showCover: true
- }
- };
-
- // 添加详细的按钮状态调试日志
- hilog.info(0x0000, TAG, `Raw playlist data: hasNext=${playlist['hasNext']}, hasPrevious=${playlist['hasPrevious']}, currentIndex=${playlist['currentIndex']}, totalCount=${playlist['totalCount']}`);
- hilog.info(0x0000, TAG, `Converted widget data: isPlaying=${widgetData.playState.isPlaying}, hasNext=${widgetData.playlist.hasNext}, hasPrevious=${widgetData.playlist.hasPrevious}, currentIndex=${widgetData.playlist.currentIndex}, totalCount=${widgetData.playlist.totalCount}, title=${widgetData.currentSong.title}`);
-
- return widgetData;
- } catch (error) {
- hilog.error(0x0000, TAG, `Failed to convert player data: ${error}`);
- return this.getDefaultWidgetData();
- }
- }
- /**
- * 获取默认卡片数据
- */
- private getDefaultWidgetData(): WidgetData {
- return WidgetTypeHelpers.createDefaultWidgetData();
- }
- /**
- * 计算播放进度百分比
- */
- private calculatePercentage(current: number, total: number): number {
- if (total <= 0) return 0;
- return Math.min(100, Math.max(0, (current / total) * 100));
- }
- /**
- * 格式化时间显示
- */
- private formatTime(seconds: number): string {
- const mins = Math.floor(seconds / 60);
- const secs = Math.floor(seconds % 60);
- return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
- }
- }
|