|
@@ -38,10 +38,13 @@ 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 { AvSessionWidgetListener } from '../common/widget/AvSessionWidgetListener';
|
|
|
-import {
|
|
|
|
|
- PlayProgress} from '../common/widget/WidgetTypes';
|
|
|
|
|
|
|
+import { WidgetData, PlayProgress, PlayState, SongInfo, PlaylistState, PlayerStateBroadcastData } from '../common/widget/WidgetTypes';
|
|
|
import { PlayerStateListener, PlayerState, PlayerError } from '../common/service/PlayerStateModel';
|
|
import { PlayerStateListener, PlayerState, PlayerError } from '../common/service/PlayerStateModel';
|
|
|
import {
|
|
import {
|
|
|
|
|
+ WIDGET_CONTROL_EVENT,
|
|
|
|
|
+ WIDGET_REQUEST_STATE_EVENT,
|
|
|
|
|
+ PLAYER_STATE_CHANGED_EVENT,
|
|
|
|
|
+ PLAYER_SONG_CHANGED_EVENT,
|
|
|
PLAYER_PROGRESS_CHANGED_EVENT
|
|
PLAYER_PROGRESS_CHANGED_EVENT
|
|
|
} from '../common/widget/WidgetEventConstants';
|
|
} from '../common/widget/WidgetEventConstants';
|
|
|
import {
|
|
import {
|
|
@@ -191,7 +194,7 @@ export struct LocalMusic {
|
|
|
@StorageProp('windowWidth') windowWidth: number = 0;
|
|
@StorageProp('windowWidth') windowWidth: number = 0;
|
|
|
@StorageProp('windowHeight') windowHeight: number = 0;
|
|
@StorageProp('windowHeight') windowHeight: number = 0;
|
|
|
@StorageProp('isHiCarStatus') isHiCarStatus: boolean = false;
|
|
@StorageProp('isHiCarStatus') isHiCarStatus: boolean = false;
|
|
|
- private UNKNOWN:string = '';
|
|
|
|
|
|
|
+ private UNKONWN:string = '';
|
|
|
private listArea: Area = {
|
|
private listArea: Area = {
|
|
|
width: 0,
|
|
width: 0,
|
|
|
height: 0,
|
|
height: 0,
|
|
@@ -500,6 +503,8 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
// 更新播放状态相关的UI
|
|
// 更新播放状态相关的UI
|
|
|
this.localMusic.setIsPlaying(state.isPlaying);
|
|
this.localMusic.setIsPlaying(state.isPlaying);
|
|
|
|
|
+ this.localMusic.updateSessionPlayState(state.isPlaying);
|
|
|
|
|
+
|
|
|
// 如果状态发生变化,触发UI更新
|
|
// 如果状态发生变化,触发UI更新
|
|
|
if (previousStatus !== this.localMusic.CONTROL_PlayStatus) {
|
|
if (previousStatus !== this.localMusic.CONTROL_PlayStatus) {
|
|
|
this.localMusic.playChange();
|
|
this.localMusic.playChange();
|
|
@@ -649,7 +654,7 @@ export struct LocalMusic {
|
|
|
PreferencesUtil.putSync('isFirstTiped',false)
|
|
PreferencesUtil.putSync('isFirstTiped',false)
|
|
|
}
|
|
}
|
|
|
this.initSetting()
|
|
this.initSetting()
|
|
|
- this.UNKNOWN = Utility.resourceToString(this.context, $r('app.string.unknown'));
|
|
|
|
|
|
|
+ this.UNKONWN = Utility.resourceToString(this.context, $r('app.string.unknown'));
|
|
|
this.topRectHeight = px2vp(AppUtil.getStatusBarHeight());
|
|
this.topRectHeight = px2vp(AppUtil.getStatusBarHeight());
|
|
|
Utility.getAppName(getContext(this)).then((appName: string) => {
|
|
Utility.getAppName(getContext(this)).then((appName: string) => {
|
|
|
this.appName = appName
|
|
this.appName = appName
|
|
@@ -3119,7 +3124,7 @@ export struct LocalMusic {
|
|
|
.padding({ top: 8 })
|
|
.padding({ top: 8 })
|
|
|
.maxLines(1)
|
|
.maxLines(1)
|
|
|
.visibility(StrUtil.isEmpty(this.videoLocalList[0].genre)||
|
|
.visibility(StrUtil.isEmpty(this.videoLocalList[0].genre)||
|
|
|
- this.videoLocalList[0].genre.includes(this.UNKNOWN)
|
|
|
|
|
|
|
+ this.videoLocalList[0].genre.includes(this.UNKONWN)
|
|
|
?Visibility.None:Visibility.Visible)
|
|
?Visibility.None:Visibility.Visible)
|
|
|
.fontWeight(FontWeight.Bold)
|
|
.fontWeight(FontWeight.Bold)
|
|
|
.fontColor($r('app.color.text_color'))
|
|
.fontColor($r('app.color.text_color'))
|
|
@@ -3131,7 +3136,7 @@ export struct LocalMusic {
|
|
|
.padding({ top: 8 })
|
|
.padding({ top: 8 })
|
|
|
.maxLines(1)
|
|
.maxLines(1)
|
|
|
.visibility(StrUtil.isEmpty(this.videoLocalList[0].year)||
|
|
.visibility(StrUtil.isEmpty(this.videoLocalList[0].year)||
|
|
|
- this.videoLocalList[0].year.includes(this.UNKNOWN)
|
|
|
|
|
|
|
+ this.videoLocalList[0].year.includes(this.UNKONWN)
|
|
|
?Visibility.None:Visibility.Visible)
|
|
?Visibility.None:Visibility.Visible)
|
|
|
.fontWeight(FontWeight.Bold)
|
|
.fontWeight(FontWeight.Bold)
|
|
|
.fontColor($r('app.color.text_color'))
|
|
.fontColor($r('app.color.text_color'))
|
|
@@ -5813,6 +5818,7 @@ export struct LocalMusic {
|
|
|
//退出APP
|
|
//退出APP
|
|
|
const mContext = getContext(this) as common.UIAbilityContext
|
|
const mContext = getContext(this) as common.UIAbilityContext
|
|
|
mContext.terminateSelf();
|
|
mContext.terminateSelf();
|
|
|
|
|
+ AvSessionController.getInstance(true).unregisterSessionListener()
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -9697,7 +9703,8 @@ export struct LocalMusic {
|
|
|
// 更新播放状态
|
|
// 更新播放状态
|
|
|
this.CONTROL_PlayStatus = PlayStatus.PLAY;
|
|
this.CONTROL_PlayStatus = PlayStatus.PLAY;
|
|
|
this.setIsPlaying(true);
|
|
this.setIsPlaying(true);
|
|
|
-
|
|
|
|
|
|
|
+ this.updateSessionPlayState(true);
|
|
|
|
|
+
|
|
|
LogUtils.getInstance().LOGI("LocalMusic: startPlayOrResumePlay completed via UnifiedPlayerService");
|
|
LogUtils.getInstance().LOGI("LocalMusic: startPlayOrResumePlay completed via UnifiedPlayerService");
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
LogUtils.getInstance().LOGI(`LocalMusic startPlayOrResumePlay error: ${error}`);
|
|
LogUtils.getInstance().LOGI(`LocalMusic startPlayOrResumePlay error: ${error}`);
|
|
@@ -9712,6 +9719,7 @@ export struct LocalMusic {
|
|
|
private handlePlaybackError() {
|
|
private handlePlaybackError() {
|
|
|
this.CONTROL_PlayStatus = PlayStatus.INIT;
|
|
this.CONTROL_PlayStatus = PlayStatus.INIT;
|
|
|
this.setIsPlaying(false);
|
|
this.setIsPlaying(false);
|
|
|
|
|
+ this.updateSessionPlayState(false);
|
|
|
this.mDestroyPage = true;
|
|
this.mDestroyPage = true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -10030,6 +10038,16 @@ export struct LocalMusic {
|
|
|
this.replayVisible = Visibility.None;
|
|
this.replayVisible = Visibility.None;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private hideLoadIng() {
|
|
|
|
|
+ this.loadingVisible = Visibility.None;
|
|
|
|
|
+ this.replayVisible = Visibility.None;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private showRePlay() {
|
|
|
|
|
+ this.loadingVisible = Visibility.None;
|
|
|
|
|
+ this.replayVisible = Visibility.Visible;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 原来的 play 方法已移除,播放逻辑现在由 UnifiedPlayerService 统一处理
|
|
// 原来的 play 方法已移除,播放逻辑现在由 UnifiedPlayerService 统一处理
|
|
|
// private async play(url: string) { ... } 已移除
|
|
// private async play(url: string) { ... } 已移除
|
|
|
|
|
|
|
@@ -10038,6 +10056,12 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
// 所有的播放器配置和监听器设置已移至 UnifiedPlayerService
|
|
// 所有的播放器配置和监听器设置已移至 UnifiedPlayerService
|
|
|
|
|
|
|
|
|
|
+ //保存最后播放的那首歌和已经对应的播放列表
|
|
|
|
|
+ saveLastPlayList() {
|
|
|
|
|
+ PreferencesUtil.putSync('LastMusicInfo', this.currentSong)
|
|
|
|
|
+ PreferencesUtil.putSync('LastMusicList', this.songList)
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// 清空播放记录
|
|
// 清空播放记录
|
|
|
clearVideoHistory() {
|
|
clearVideoHistory() {
|
|
@@ -10056,6 +10080,24 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public async setAvSessionListener() {
|
|
|
|
|
+ if (!this.avSessionController) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('play', this.sessionPlayCallback);
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('pause', this.sessionPauseCallback);
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('stop', this.sessionStopCallback);
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('playNext', this.sessionPlayNextCallback);
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('playPrevious', this.sessionPlayPreviousCallback);
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('fastForward', this.sessionFastForwardCallback);
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('rewind', this.sessionRewindCallback);
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('seek', this.sessionSeekCallback);
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('setLoopMode', this.sessionSetLoopModeCallback);
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('toggleFavorite', this.sessionToggleFavoriteCallback);
|
|
|
|
|
+
|
|
|
|
|
+ this.avSessionController.getAvSession()?.on('outputDeviceChange', this.sessionOutputDeviceChange)
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
setLoopMode() {
|
|
setLoopMode() {
|
|
|
let itype = this.playType + 1
|
|
let itype = this.playType + 1
|
|
@@ -10141,6 +10183,54 @@ export struct LocalMusic {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private sessionToggleFavoriteCallback = (assetId: string) => {
|
|
|
|
|
+ if (this.currentSong) {
|
|
|
|
|
+ console.info(`on toggleFavorite `);
|
|
|
|
|
+ // 应用收到收藏命令,进行收藏处理。
|
|
|
|
|
+
|
|
|
|
|
+ this.isFac = Utility.getIsFav(this.favList, this.currentSong)
|
|
|
|
|
+ this.doFav(this.currentSong)
|
|
|
|
|
+ // 应用内完成或者取消收藏,把新的收藏状态设置给AVSession。
|
|
|
|
|
+ let playbackState: avSession.AVPlaybackState = {
|
|
|
|
|
+ isFavorite: !this.isFac,
|
|
|
|
|
+ };
|
|
|
|
|
+ this.avSessionController.getAvSession()?.setAVPlaybackState(playbackState).then(() => {
|
|
|
|
|
+ console.info(`SetAVPlaybackState successfully`);
|
|
|
|
|
+ }).catch((err: BusinessError) => {
|
|
|
|
|
+ console.info(`SetAVPlaybackState BusinessError: code: ${err.code}, message: ${err.message}`);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ private sessionOutputDeviceChange = async (connectState: avSession.ConnectionState,
|
|
|
|
|
+ device: avSession.OutputDeviceInfo) => {
|
|
|
|
|
+ let currentDevice: avSession.DeviceInfo = device?.devices?.[0];
|
|
|
|
|
+ if (currentDevice.castCategory === avSession.AVCastCategory.CATEGORY_REMOTE &&
|
|
|
|
|
+ connectState === avSession.ConnectionState.STATE_CONNECTED) {
|
|
|
|
|
+
|
|
|
|
|
+ await this.startCasting();
|
|
|
|
|
+
|
|
|
|
|
+ } else if ((currentDevice.castCategory === avSession.AVCastCategory.CATEGORY_REMOTE &&
|
|
|
|
|
+ connectState === avSession.ConnectionState.STATE_DISCONNECTED) ||
|
|
|
|
|
+ (currentDevice.castCategory === avSession.AVCastCategory.CATEGORY_LOCAL &&
|
|
|
|
|
+ connectState === avSession.ConnectionState.STATE_CONNECTED)) {
|
|
|
|
|
+ if (device) {
|
|
|
|
|
+ this.endCasting();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ private async startCasting() {
|
|
|
|
|
+ this.castController = await this.avSessionController.getAvSession()?.getAVCastController();
|
|
|
|
|
+ this.setPlaybackStateChangeListener();
|
|
|
|
|
+ this.pause();
|
|
|
|
|
+ this.initQueueItem();
|
|
|
|
|
+ this.prepare();
|
|
|
|
|
+ let refToIsCasting: AbstractProperty<boolean> | undefined = AppStorage.ref('isCasting');
|
|
|
|
|
+ refToIsCasting?.set(true);
|
|
|
|
|
+ this.castSeek = true;
|
|
|
|
|
+ this.isCastPlaying = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
async changeCasting() {
|
|
async changeCasting() {
|
|
|
this.castController = await this.avSessionController.getAvSession()?.getAVCastController();
|
|
this.castController = await this.avSessionController.getAvSession()?.getAVCastController();
|
|
|
this.initQueueItem();
|
|
this.initQueueItem();
|
|
@@ -10179,6 +10269,37 @@ export struct LocalMusic {
|
|
|
this.castController?.on('playPrevious', this.playPreviousCallback);
|
|
this.castController?.on('playPrevious', this.playPreviousCallback);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private updateSessionPlayState(isPlay: boolean): void {
|
|
|
|
|
+ // if(!this.isBgPlayOpen)
|
|
|
|
|
+ // return
|
|
|
|
|
+ Logger.info(TAG, `updateIsPlay isPlay: ${isPlay}`);
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 通过UnifiedPlayerService获取当前位置
|
|
|
|
|
+ const currentState = this.unifiedPlayerService.getCurrentState();
|
|
|
|
|
+ this.setAVSessionPlayState({
|
|
|
|
|
+ state: isPlay ? avSession.PlaybackState.PLAYBACK_STATE_PLAY : avSession.PlaybackState.PLAYBACK_STATE_PAUSE,
|
|
|
|
|
+ position: {
|
|
|
|
|
+ elapsedTime: currentState.currentPosition,
|
|
|
|
|
+ updateTime: new Date().getTime()
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ LogUtils.getInstance().LOGI(`updateSessionPlayState error: ${error}`);
|
|
|
|
|
+ // 如果获取失败,使用默认值
|
|
|
|
|
+ this.setAVSessionPlayState({
|
|
|
|
|
+ state: isPlay ? avSession.PlaybackState.PLAYBACK_STATE_PLAY : avSession.PlaybackState.PLAYBACK_STATE_PAUSE,
|
|
|
|
|
+ position: {
|
|
|
|
|
+ elapsedTime: 0,
|
|
|
|
|
+ updateTime: new Date().getTime()
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private setAVSessionPlayState(playbackState: avSession.AVPlaybackState): void {
|
|
|
|
|
+ this.avSessionController.setAvSessionPlayState(playbackState)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private positionChange(position: number) {
|
|
private positionChange(position: number) {
|
|
|
this.currentTime2 = position / 1000;
|
|
this.currentTime2 = position / 1000;
|
|
|
this.currentStringTime = secondToTime(Math.floor(position / 1000));
|
|
this.currentStringTime = secondToTime(Math.floor(position / 1000));
|
|
@@ -10431,6 +10552,7 @@ export struct LocalMusic {
|
|
|
this.mDestroyPage = true;
|
|
this.mDestroyPage = true;
|
|
|
this.CONTROL_PlayStatus = PlayStatus.PAUSE;
|
|
this.CONTROL_PlayStatus = PlayStatus.PAUSE;
|
|
|
this.setIsPlaying(false);
|
|
this.setIsPlaying(false);
|
|
|
|
|
+ this.updateSessionPlayState(false);
|
|
|
this.playChange()
|
|
this.playChange()
|
|
|
|
|
|
|
|
if (this.pipController) {
|
|
if (this.pipController) {
|
|
@@ -10454,6 +10576,7 @@ export struct LocalMusic {
|
|
|
this.stopProgressTask();
|
|
this.stopProgressTask();
|
|
|
this.CONTROL_PlayStatus = PlayStatus.INIT;
|
|
this.CONTROL_PlayStatus = PlayStatus.INIT;
|
|
|
this.setIsPlaying(false);
|
|
this.setIsPlaying(false);
|
|
|
|
|
+ this.updateSessionPlayState(false)
|
|
|
this.playChange()
|
|
this.playChange()
|
|
|
this.watchStatus();
|
|
this.watchStatus();
|
|
|
|
|
|
|
@@ -11249,6 +11372,82 @@ export struct LocalMusic {
|
|
|
// this.status = "广告加载中..."
|
|
// this.status = "广告加载中..."
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 展示Banner广告
|
|
|
|
|
+ */
|
|
|
|
|
+ // showBannerAd() {
|
|
|
|
|
+ // if (!this.bannerAd) {
|
|
|
|
|
+ // return
|
|
|
|
|
+ // }
|
|
|
|
|
+ // this.isShowAd = true
|
|
|
|
|
+ // this.status = "已展示"
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 穿山甲广告代码结束
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 初始化AvSession卡片监听器
|
|
|
|
|
+ */
|
|
|
|
|
+ private initAvSessionWidgetListener(): void {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 不需要在这里注册监听器,因为我们直接通过updateWidgetData更新数据
|
|
|
|
|
+ // 避免循环调用和重复广播
|
|
|
|
|
+ LogUtils.getInstance().LOGI('AvSession widget listener initialized successfully (no circular listener)');
|
|
|
|
|
+ } 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}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 节流广播进度更新
|
|
* 节流广播进度更新
|
|
|
*/
|
|
*/
|