王士厅

音频悬浮窗

... ... @@ -31,6 +31,14 @@ export enum EmitterEventId {
// App进入后台
APP_ENTER_BACKGROUD = 101,
// 更换音频名称
AUDIO_CHANGE_TITLe = 10,
// 更换音频状态
AUDIO_CHANGE_STATUS = 11,
// 获取音频悬浮窗焦点状态
AUDIO_WINDOW_TYPE = 12,
}
... ...
... ... @@ -23,7 +23,7 @@ export class EmitterUtils {
* @param eventId 事件id
* @param str 字符串数据
*/
static sendEvent(eventId: number, str?: string) {
static sendEvent(eventId: number, str?: string | number) {
let event: emitter.InnerEvent = {
eventId: eventId,
priority: emitter.EventPriority.LOW
... ...
... ... @@ -142,6 +142,7 @@ export struct MorningEveningPaperComponent {
if (this.compListItem.operDataList && this.compListItem.operDataList.length > 0) {
this.getAllContentInteractData(this.compListItem.operDataList)
}
Logger.debug('compInfoBean?.compList[0].audioDataList', JSON.stringify(compInfoBean?.compList[0].audioDataList))
if (compInfoBean?.compList[0].audioDataList) {
this.audioPlayUrl = compInfoBean?.compList[0].audioDataList[0].audioUrl
this.audioTitle = compInfoBean?.compList[0].audioDataList[0].title
... ... @@ -301,8 +302,9 @@ export struct MorningEveningPaperComponent {
.onClick(() => {
Logger.info("TAG", "cj compInfoBean onClick1 = " + this.isAudioPlaying)
// dialog.open()
this.AudioSuspension.showWindow()
// this.playerController.firstPlay(this.audioPlayUrl)
// this.playerController.firstPlay(this.audioPlayUrl, this.audioTitle)
this.AudioSuspension.setPlayerUrl(this.audioPlayUrl, this.audioTitle)
Logger.info(TAG, "this.audioPlayUrl = " + this.audioPlayUrl)
Logger.info("TAG", "cj compInfoBean onClick2 = " + this.isAudioPlaying)
})
}
... ...
... ... @@ -23,6 +23,9 @@ import { WDRouterPage, WDRouterRule } from 'wdRouter/Index';
import { PageRepository } from '../../repository/PageRepository';
import { SpConstants } from 'wdConstant/Index';
import { WDShare } from 'wdShare/Index';
import { AudioSuspensionModel } from '../../viewmodel/AudioSuspensionModel'
import { EmitterEventId, EmitterUtils } from 'wdKit/Index'
import { PlayerConstants } from 'wdPlayer'
const TAG = 'OperRowListView';
... ... @@ -55,15 +58,23 @@ export struct OperRowListView {
@State newsStatusOfUser: batchLikeAndCollectResult | undefined = undefined // 点赞、收藏状态
@State likeBean: Record<string, string> = {}
@State audioUrl: string = ''
@State audioTitle: string = ''
@State bgColor: ResourceColor = Color.White
@State showCommentIcon: boolean = true
@State bottomSafeHeight: number = AppStorage.get<number>('bottomSafeHeight') || 0
needLike: boolean = true
private AudioSuspension = new AudioSuspensionModel()
@State currentStatus: number | string | undefined = 0;
async aboutToAppear() {
console.info(TAG, '22222----', this.styleType)
console.info(TAG, '3333----', this.needLike)
this.handleStyle()
EmitterUtils.receiveEvent(EmitterEventId.AUDIO_CHANGE_STATUS, (val: number | string | undefined) => {
console.log(TAG,'this.currentStatus', val)
this.currentStatus = val
})
}
async onDetailUpdated() {
... ... @@ -97,7 +108,9 @@ export struct OperRowListView {
// 音频需要数据
if (this.operationButtonList?.includes('listen')) {
this.audioUrl = this.contentDetailData.audioList[0]?.audioUrl || ''
this.audioTitle = this.contentDetailData.newsTitle || ''
console.log(TAG, 'this.audioUrl+++', this.audioUrl)
console.log(TAG, 'this.audioTitle+++', this.audioTitle)
}
}
... ... @@ -231,13 +244,14 @@ export struct OperRowListView {
@Builder
builderListen() {
Column() {
Image($r('app.media.icon_listen'))
Image(this.currentStatus === PlayerConstants.STATUS_START ? $r("app.media.icon_audio_pause") : $r("app.media.icon_listen"))
.width(24)
.height(24)
.aspectRatio(1)
.interpolation(ImageInterpolation.High)
.onClick((event: ClickEvent) => {
ToastUtils.showToast('音频为公共方法,待开发', 1000);
this.AudioSuspension.setPlayerUrl(this.audioUrl, this.audioTitle)
// ToastUtils.showToast('音频为公共方法,待开发', 1000);
})
}
.width(42)
... ...
... ... @@ -2,6 +2,7 @@ import window from '@ohos.window';
import { Logger } from 'wdKit';
import { WDPlayerController } from 'wdPlayer';
import { BusinessError } from '@ohos.base';
import { EmitterEventId, EmitterUtils } from 'wdKit/Index'
const TAG = 'AudioSuspensionModel'
... ... @@ -11,6 +12,10 @@ const TAG = 'AudioSuspensionModel'
export class AudioSuspensionModel {
public playerController: SubscribedAbstractProperty<WDPlayerController> = AppStorage.link<WDPlayerController>('playerController')
public floatWindowClass: SubscribedAbstractProperty<window.Window> = AppStorage.link<window.Window>('floatWindowClass')
public srcTitle: string = ''
private url: string = ''
private expandWidth: number = 800
private expandHeight: number = 200
constructor() {
this.initPlayerController()
}
... ... @@ -23,6 +28,10 @@ export class AudioSuspensionModel {
AppStorage.setOrCreate('playerController', new WDPlayerController());
this.playerController = AppStorage.link<WDPlayerController>('playerController')
Logger.info(TAG, 'playerController create success')
this.playerController.get().onStatusChange = (status: number) => {
console.info(TAG, 'this.currentStatus', status)
EmitterUtils.sendEvent(EmitterEventId.AUDIO_CHANGE_STATUS, status)
}
} else {
Logger.info(TAG, 'playerController already exit')
}
... ... @@ -30,32 +39,70 @@ export class AudioSuspensionModel {
/**
* 配置音频地址
*/
public setPlayerUrl() {
// this.playerController.switchPlayOrPause()
Logger.info(TAG, 'handlePlayer')
public setPlayerUrl(url: string, srcTitle: string) {
// console.log(TAG,'this.url', this.url)
// console.log(TAG,'url', url)
if (this.url === url) {
this.playerController.get().switchPlayOrPause()
} else {
this.url = url
this.playerController.get().firstPlay(url)
this.playerController.get().onCanplay = () => {
this.playerController.get().play()
}
this.srcTitle = srcTitle
EmitterUtils.sendEvent(EmitterEventId.AUDIO_CHANGE_TITLe, this.srcTitle)
console.log(TAG, 'handlePlayer')
this.resizeWindow(this.expandWidth, this.expandHeight)
}
this.showWindow()
}
// 显示悬浮窗。
public showWindow() {
this.floatWindowClass.get().showWindow((err: BusinessError) => {
// 判断当前窗口是否已显示,使用callback异步回调。
this.floatWindowClass.get().isShowing((err: BusinessError, data) => {
const errCode: number = err.code;
if (errCode) {
console.error(TAG, 'Failed window is showing Cause:' + JSON.stringify(err));
return;
}
console.info(TAG, 'window is showing: ' + JSON.stringify(data));
if(data === false) {
// 显示当前窗口,使用callback异步回调。
this.floatWindowClass.get().showWindow((err: BusinessError) => {
let errCode: number = err.code;
if (errCode) {
console.error(TAG, 'floatWindowClass Failed to show the window. Cause: ' + JSON.stringify(err));
return;
}
console.info(TAG, 'floatWindowClass Succeeded in showing the window.');
});
}
});
}
// 设置悬浮窗尺寸
public resizeWindow(width: number, height: number) {
this.floatWindowClass.get().resize(width, height, (err: BusinessError) => {
let errCode: number = err.code;
if (errCode) {
console.error('floatWindowClass Failed to show the window. Cause: ' + JSON.stringify(err));
console.error(TAG, 'floatWindowClass Failed to change the window size. Cause:' + JSON.stringify(err));
return;
}
console.info('floatWindowClass Succeeded in showing the window.');
console.info(TAG, 'floatWindowClass Succeeded in changing the window size.');
});
}
// 销毁悬浮窗 使用destroy对其进行销毁。
public destroyWindow() {
this.floatWindowClass.get().destroyWindow((err: BusinessError) => {
/*this.floatWindowClass.get().destroyWindow((err: BusinessError) => {
let errCode: number = err.code;
if (errCode) {
console.error('floatWindowClass Failed to destroy the window. Cause: ' + JSON.stringify(err));
return;
}
console.info('floatWindowClass Succeeded in destroying the window.');
});
});*/
}
... ...
... ... @@ -17,6 +17,22 @@ export class DateFormatUtil {
return `${'00'}${':'}${DateFormatUtil.padding(second.toString())}`;
}
}
/**
* ms数转成00:00格式
* @param milliseconds
* @returns
*/
static convertMillisecondsToMinutesSeconds(milliseconds: number): string {
const totalSeconds = Math.floor(milliseconds / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
// 添加前导零,确保格式为两位数
const formattedMinutes = String(minutes).padStart(2, '0');
const formattedSeconds = String(seconds).padStart(2, '0');
return `${formattedMinutes}:${formattedSeconds}`;
}
static padding(num: string) {
let length = 2;
... ...
... ... @@ -23,6 +23,8 @@ import { ConfigurationConstant } from '@kit.AbilityKit';
import { WDPushNotificationManager } from 'wdHwAbility/Index';
import { StartupManager } from '../startupmanager/StartupManager';
let floatWindowClass: window.Window | null = null;
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
StartupManager.sharedInstance().appOnCreate(want, launchParam, this.context)
... ... @@ -82,7 +84,6 @@ export default class EntryAbility extends UIAbility {
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
});
// 1.创建悬浮窗
let floatWindowClass: window.Window | null = null;
const config: window.Configuration = {
name: "floatWindow", windowType: window.WindowType.TYPE_FLOAT, ctx: this.context
};
... ... @@ -104,14 +105,6 @@ export default class EntryAbility extends UIAbility {
}
console.info('floatWindowClass Succeeded in moving the window.');
});
floatWindowClass.resize(800, 200, (err: BusinessError) => {
let errCode: number = err.code;
if (errCode) {
console.error('floatWindowClass Failed to change the window size. Cause:' + JSON.stringify(err));
return;
}
console.info('floatWindowClass Succeeded in changing the window size.');
});
// 3.为悬浮窗加载对应的目标页面。
floatWindowClass.setUIContent("pages/view/AudioComponent", (err: BusinessError) => {
let errCode: number = err.code;
... ... @@ -122,11 +115,27 @@ export default class EntryAbility extends UIAbility {
console.info('floatWindowClass Succeeded in loading the content.');
});
floatWindowClass.on('windowEvent', (data) => {
EmitterUtils.sendEvent(EmitterEventId.AUDIO_WINDOW_TYPE, data)
});
});
}
destroyFloatWindow() {
(floatWindowClass as window.Window).destroyWindow((err: BusinessError) => {
let errCode: number = err.code;
if (errCode) {
console.error('floatWindowClass Failed to destroy the window. Cause: ' + JSON.stringify(err));
return;
}
console.info('floatWindowClass Succeeded in destroying the window.');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
this.destroyFloatWindow()
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
... ...
import { AudioSuspensionModel } from 'wdComponent'
import { PlayerConstants } from 'wdPlayer'
import { PlayerConstants, DateFormatUtil } from 'wdPlayer'
import { EmitterEventId, EmitterUtils } from 'wdKit/Index'
import window from '@ohos.window';
const TAG = 'AudioSuspensionModel'
@Entry
@Component
struct Index {
private AudioSuspension = new AudioSuspensionModel()
@State audioTitle: string = '来了!新闻早班车5月9日';
@State currentTime: string = '00:03';
@State totalTime: string = '06:16';
@State progressVal: number = 20;
@State currentStatus: number = 0;
@State audioTitle: string | undefined = '';
@State currentTime: string = '00:00';
@State totalTime: string = '00:00';
@State progressVal: number = 0;
@State currentStatus: number | string |undefined = 0;
@State isExpand: boolean = true;
private expandWidth: number = 800
private expandHeight: number = 200
private foldWidth: number = 200
private foldHeight: number = 200
aboutToAppear() {
this.AudioSuspension.playerController.get().onTimeUpdate = (position, duration) => {
this.currentTime = DateFormatUtil.convertMillisecondsToMinutesSeconds(position);
this.totalTime = DateFormatUtil.convertMillisecondsToMinutesSeconds(duration);
this.progressVal = Math.floor(position * 100 / duration);
}
EmitterUtils.receiveEvent(EmitterEventId.AUDIO_CHANGE_TITLe, (val:string | undefined) => {
console.log(TAG,'this.audioTitle', val)
this.audioTitle = val
})
EmitterUtils.receiveEvent(EmitterEventId.AUDIO_CHANGE_STATUS, (val: number | string | undefined) => {
console.log(TAG,'this.currentStatus', val)
this.currentStatus = val
})
EmitterUtils.receiveEvent(EmitterEventId.AUDIO_WINDOW_TYPE, (val: number | string | undefined) => {
if (val == window.WindowEventType.WINDOW_ACTIVE) {
console.info(TAG, 'current window stage event is WINDOW_ACTIVE', val);
this.isExpand = true
this.AudioSuspension.resizeWindow(this.expandWidth, this.expandHeight)
} else if (val == window.WindowEventType.WINDOW_INACTIVE) {
console.info(TAG, 'current window stage event is WINDOW_INACTIVE', val);
this.isExpand = false
this.AudioSuspension.resizeWindow(this.foldWidth, this.foldHeight)
}
})
}
onPageHide() {
// this.status = PlayerConstants.STATUS_PAUSE;
this.AudioSuspension.playerController.get()?.pause();
}
build() {
Stack({ alignContent: Alignment.End }) {
Column() { //标题 时间 进度条
Marquee({
start: true,
step: 5,
loop: Number.POSITIVE_INFINITY,
fromStart: true,
src: this.audioTitle
})
.width("60%")
.height(20)
.fontColor('#222222')
.fontSize(14)
.margin({ top: 10, left: 10 })
.alignSelf(ItemAlign.Start)
.onStart(() => {
console.info('Marquee animation complete onStart')
if(this.isExpand) {
Column() { //标题 时间 进度条
Marquee({
start: true,
step: 5,
loop: Number.POSITIVE_INFINITY,
fromStart: true,
src: this.audioTitle
})
.onBounce(() => {
console.info('Marquee animation complete onBounce')
})
.onFinish(() => {
console.info('Marquee animation complete onFinish')
})
Row() {
Text(this.currentTime)
.fontSize(12)
.fontColor("#999999")
.height("100%")
.alignSelf(ItemAlign.Start)
Text("/" + this.totalTime)
.fontSize(12)
.fontColor("#999999")
.height("100%")
.width("60%")
.height(20)
.fontColor('#222222')
.fontSize(14)
.margin({ top: 10, left: 10 })
.alignSelf(ItemAlign.Start)
.onStart(() => {
console.info('Marquee animation complete onStart')
})
.onBounce(() => {
console.info('Marquee animation complete onBounce')
})
.onFinish(() => {
console.info('Marquee animation complete onFinish')
})
}
.width("100%")
.height(16)
.margin({ top: 4, left: 10 })
Row() {
Text(this.currentTime)
.fontSize(12)
.fontColor("#999999")
.height("100%")
.alignSelf(ItemAlign.Start)
Text("/" + this.totalTime)
.fontSize(12)
.fontColor("#999999")
.height("100%")
.alignSelf(ItemAlign.Start)
Progress({ value: this.progressVal, total: 100, type: ProgressType.Capsule })
.color("#ED2800")
.backgroundColor($r('app.color.white'))
}
.width("100%")
.height(3)
.margin({ top: 7 })
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Start)
.height(16)
.margin({ top: 4, left: 10 })
Row() {
Image(this.currentStatus != PlayerConstants.STATUS_START ? $r("app.media.icon_audio_pause") : $r("app.media.icon_audio_playing"))
.objectFit(ImageFit.Contain)
.width(24)
.height(24)
.margin({ right: 12 })
.onClick(() => {
if (this.AudioSuspension.playerController) {
this.AudioSuspension.playerController.get().switchPlayOrPause()
this.currentStatus = this.AudioSuspension.playerController.get().getStatus()
}
})
Progress({ value: this.progressVal, total: 100, type: ProgressType.Capsule })
.color("#ED2800")
.backgroundColor($r('app.color.white'))
.width("100%")
.height(3)
.margin({ top: 7 })
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Start)
Image($r("app.media.icon_audio_close"))
.objectFit(ImageFit.Contain)
.width(24)
.height(24)
.onClick(() => {
if (this.AudioSuspension.playerController) {
this.AudioSuspension.playerController.get().stop()
this.AudioSuspension.destroyWindow()
}
})
}.width(80)
.height(60)
Row() {
Image(this.currentStatus === PlayerConstants.STATUS_START ? $r("app.media.icon_audio_pause") : $r("app.media.icon_audio_playing"))
.objectFit(ImageFit.Contain)
.width(24)
.height(24)
.margin({ right: 12 })
.onClick(() => {
if (this.AudioSuspension.playerController) {
this.AudioSuspension.playerController.get().switchPlayOrPause()
}
})
Image($r("app.media.icon_audio_close"))
.objectFit(ImageFit.Contain)
.width(24)
.height(24)
.onClick(() => {
if (this.AudioSuspension.playerController) {
this.AudioSuspension.playerController.get().stop()
this.AudioSuspension.destroyWindow()
}
})
}.width(80)
.height(60)
} else {
Row() {
Image(this.currentStatus === PlayerConstants.STATUS_START ? $r("app.media.icon_audio_pause") : $r("app.media.icon_audio_playing"))
.objectFit(ImageFit.Contain)
.width(24)
.height(24)
.onClick(() => {
if (this.AudioSuspension.playerController) {
this.AudioSuspension.playerController.get().switchPlayOrPause()
}
})
}
.justifyContent(FlexAlign.Center)
.width(60)
.height(60)
}
}
.width('100%')
.height('100%')
... ...
... ... @@ -92,9 +92,16 @@
{
"name": "ohos.permission.INTERNET"
},
// {
// "name": "ohos.permission.SYSTEM_FLOAT_WINDOW",
// }
{
"name": "ohos.permission.SYSTEM_FLOAT_WINDOW",
"reason": "$string:EntryAbility_desc",
"usedScene": {
"abilities": [
"FormAbility"
],
"when": "inuse"
}
}
]
}
}
\ No newline at end of file
... ...